From b00923dc2b48ce214b8023e988096e57b6a994f3 Mon Sep 17 00:00:00 2001 From: dsutanto Date: Wed, 19 Aug 2026 11:14:40 +0700 Subject: [PATCH] feat: implement K{coop}-L{floor} standard, smart discovery, multi-coop runner, and move test runs to test_run_folder --- README.md | 4 + RUN.md | 51 ++--- configs/K1-L1.yaml | 186 ++++++++++++++++++ configs/K1-L2.yaml | 186 ++++++++++++++++++ configs/K2-L1.yaml | 186 ++++++++++++++++++ configs/K2-L2.yaml | 186 ++++++++++++++++++ configs/K3-L1.yaml | 186 ++++++++++++++++++ configs/K3-L2.yaml | 186 ++++++++++++++++++ configs/K4-L1.yaml | 186 ++++++++++++++++++ configs/K4-L2.yaml | 186 ++++++++++++++++++ configs/K5-L1.yaml | 186 ++++++++++++++++++ configs/K5-L2.yaml | 186 ++++++++++++++++++ run_all_coops.sh | 56 ++++++ src/chicken_counter/batch_discovery.py | 48 ++++- test_run.ds => test_run_folder/test_run.ds | 0 test_run.sh => test_run_folder/test_run.sh | 15 +- .../test_run_hybrid.sh | 19 +- .../test_run_mortality.sh | 0 .../test_run_tensor_batch.sh | 19 +- .../test_run_video_2026-05-23.sh | 14 +- .../test_run_video_2026-05-24.sh | 14 +- .../test_run_video_2026-06-06.sh | 14 +- .../test_run_video_2026-06-16.sh | 14 +- .../test_run_video_2026-06-18.sh | 14 +- 24 files changed, 2068 insertions(+), 74 deletions(-) create mode 100644 configs/K1-L1.yaml create mode 100644 configs/K1-L2.yaml create mode 100644 configs/K2-L1.yaml create mode 100644 configs/K2-L2.yaml create mode 100644 configs/K3-L1.yaml create mode 100644 configs/K3-L2.yaml create mode 100644 configs/K4-L1.yaml create mode 100644 configs/K4-L2.yaml create mode 100644 configs/K5-L1.yaml create mode 100644 configs/K5-L2.yaml create mode 100755 run_all_coops.sh rename test_run.ds => test_run_folder/test_run.ds (100%) rename test_run.sh => test_run_folder/test_run.sh (75%) rename test_run_hybrid.sh => test_run_folder/test_run_hybrid.sh (66%) rename test_run_mortality.sh => test_run_folder/test_run_mortality.sh (100%) rename test_run_tensor_batch.sh => test_run_folder/test_run_tensor_batch.sh (65%) rename test_run_video_2026-05-23.sh => test_run_folder/test_run_video_2026-05-23.sh (74%) rename test_run_video_2026-05-24.sh => test_run_folder/test_run_video_2026-05-24.sh (72%) rename test_run_video_2026-06-06.sh => test_run_folder/test_run_video_2026-06-06.sh (75%) rename test_run_video_2026-06-16.sh => test_run_folder/test_run_video_2026-06-16.sh (72%) rename test_run_video_2026-06-18.sh => test_run_folder/test_run_video_2026-06-18.sh (72%) diff --git a/README.md b/README.md index 002b6e0..e9a8a3b 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ visual. ```text configs/ + K1-L1.yaml .. K5-L2.yaml ← Standard floor configurations (K1-L1 to K5-L2) cameras/example_camera.yaml cycle7_batch.yaml cycle7_batch_optimized.yaml @@ -49,6 +50,9 @@ src/chicken_counter/ dashboard.py export_engine.py export_excel_report.py +run_all_coops.sh ← Master multi-coop batch runner +start_dashboard.sh ← Live dashboard launcher +test_run_folder/ ← Archive of test run scripts and logs ``` ## Install diff --git a/RUN.md b/RUN.md index 19f02d2..76af176 100644 --- a/RUN.md +++ b/RUN.md @@ -34,6 +34,7 @@ venv/bin/pip install -r requirements.txt ```text chicken-counting-sukawarna-det/ ├── configs/ +│ ├── K1-L1.yaml .. K5-L2.yaml ← Standard floor configurations (K1-L1 to K5-L2) │ ├── cycle7_batch_optimized.yaml ← Main batch processing config │ ├── cycle7_batch.yaml ← Alternate batch config │ ├── mortality_config.yaml ← Mortality (carcass) detection config @@ -50,9 +51,9 @@ chicken-counting-sukawarna-det/ ├── dashboard.py ← Live API + Dashboard server ├── export_engine.py ← Manual TensorRT export utility ├── export_excel_report.py ← Excel reporting & analytics generator +├── run_all_coops.sh ← Master multi-coop batch runner (K1-L1 to K5-L2) ├── start_dashboard.sh ← Portable dashboard launcher ← USE THIS -├── test_run_mortality.sh ← Run mortality detection -├── test_run.sh ← Run batch processing all dates +├── test_run_folder/ ← Archive of test run scripts and logs └── requirements.txt ``` @@ -76,47 +77,33 @@ Put your model files in the `models/` directory. ### A — Batch Video Processing (Daily Chicken Count) -Place input videos under the `VIDEOS` folder adjacent to the project: +Place input videos under the `VIDEOS` folder adjacent to the project following the `K{coop}-L{floor}` format: ```text -../VIDEOS/cycle7/kandang-atas/ - 2026-06-18/ - kandang_1_camera_1_2026-06-18_120056.mp4 - kandang_1_camera_2_2026-06-18_120456.mp4 - ... +../VIDEOS/cycle7/ + K1-L1/ + 2026-06-18/ + K1-L1_cam1_2026-06-18_120056.mp4 + K1-L1_cam2_2026-06-18_120456.mp4 + K1-L1_cam3_2026-06-18_121012.mp4 + K1-L1_cam4_2026-06-18_121530.mp4 ``` Then run: ```bash -# Run all dates (multi-process mode) -./test_run.sh +# Run all coops (10 floors: K1-L1 to K5-L2) for a specific date +./run_all_coops.sh 2026-06-18 -# Run all dates (Tensor Batching mode) -./test_run_tensor_batch.sh - -# Run all dates (Hybrid mode: Threaded CPU + Batched GPU) -./test_run_hybrid.sh - -# Run a specific date (4 cameras in parallel) +# Run a specific floor (e.g. Kandang 1 Lantai 1) PYTHONPATH=src venv/bin/python -m chicken_counter.cli batch \ - --config configs/cycle7_batch_optimized.yaml \ + --config configs/K1-L1.yaml \ --date 2026-06-18 -# Run a specific date using Model-Level Tensor Batching -PYTHONPATH=src venv/bin/python -m chicken_counter.cli batch \ - --config configs/cycle7_batch_optimized.yaml \ - --date 2026-06-18 \ - --mode tensor_batching - -# Run a specific date using Hybrid Execution Mode -PYTHONPATH=src venv/bin/python -m chicken_counter.cli batch \ - --config configs/cycle7_batch_optimized.yaml \ - --date 2026-06-18 \ - --mode hybrid - -# Run with video output enabled -./test_run_video_2026-06-18.sh +# Run archive scripts in test_run_folder +./test_run_folder/test_run.sh +./test_run_folder/test_run_tensor_batch.sh +./test_run_folder/test_run_hybrid.sh ``` Output is saved in `../VIDEOS/cycle7/kandang-atas/2026-06-18/output/`. diff --git a/configs/K1-L1.yaml b/configs/K1-L1.yaml new file mode 100644 index 0000000..14671b0 --- /dev/null +++ b/configs/K1-L1.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K1-L1 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K1-L1 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K1-L2.yaml b/configs/K1-L2.yaml new file mode 100644 index 0000000..0bf636e --- /dev/null +++ b/configs/K1-L2.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K1-L2 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K1-L2 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K2-L1.yaml b/configs/K2-L1.yaml new file mode 100644 index 0000000..b230da8 --- /dev/null +++ b/configs/K2-L1.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K2-L1 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K2-L1 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K2-L2.yaml b/configs/K2-L2.yaml new file mode 100644 index 0000000..9050820 --- /dev/null +++ b/configs/K2-L2.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K2-L2 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K2-L2 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K3-L1.yaml b/configs/K3-L1.yaml new file mode 100644 index 0000000..1129db2 --- /dev/null +++ b/configs/K3-L1.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K3-L1 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K3-L1 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K3-L2.yaml b/configs/K3-L2.yaml new file mode 100644 index 0000000..271a4f5 --- /dev/null +++ b/configs/K3-L2.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K3-L2 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K3-L2 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K4-L1.yaml b/configs/K4-L1.yaml new file mode 100644 index 0000000..68a5b0b --- /dev/null +++ b/configs/K4-L1.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K4-L1 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K4-L1 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K4-L2.yaml b/configs/K4-L2.yaml new file mode 100644 index 0000000..d9725f7 --- /dev/null +++ b/configs/K4-L2.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K4-L2 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K4-L2 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K5-L1.yaml b/configs/K5-L1.yaml new file mode 100644 index 0000000..22a170b --- /dev/null +++ b/configs/K5-L1.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K5-L1 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K5-L1 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/configs/K5-L2.yaml b/configs/K5-L2.yaml new file mode 100644 index 0000000..5f55301 --- /dev/null +++ b/configs/K5-L2.yaml @@ -0,0 +1,186 @@ +batch: + root_dir: ../VIDEOS/cycle7/K5-L2 + camera_glob: '*cam{num}_*.mp4' + output_subdir: output + compress_max_mb: 200 + delete_intermediate: false + checkpoint_every_n_frames: 3000 + location: K5-L2 + db_path: db/chicken_counts.db + cycle_start_date: '2026-05-22' + execution_mode: parallel_processes +stages: + early_cycle: + day_min: 0 + day_max: 15 + detection: + conf: 0.1 + min_box_area_px: 100 + roi: + min_overlap_ratio: 0.1 + mid_cycle: + day_min: 16 + day_max: 999 +defaults: + detection: + model_path: models/NUC5070_chicken-detection-model-v26n-300e-best-2026-05-02-NEW.engine + classes: + - 0 + ignored_classes: + - 1 + - 2 + conf: 0.4 + iou: 0.55 + imgsz: 640 + device: '0' + min_box_area_px: 3000 + validate_while_inside: true + dedupe_radius_px: 0 + dedupe_frames: 12 + detection_zone: + enabled: true + buffer_above_px: 250 + buffer_below_px: 250 + show_in_overlay: true + tracker: + tracker_config_path: configs/trackers/botsort_chicken.yaml + persist: true + track_buffer: 90 + gate: + mode: two_line + lines_y: + - 420 + - 730 + direction: bottom_to_up + motion: + enabled: true + axis: vertical + forward_sign: 1.0 + ema_alpha: 0.2 + reverse_enter_threshold: -1.5 + reverse_exit_threshold: -0.5 + debounce_frames: 12 + min_features: 40 + max_corners: 50 + stride_frames: 3 + flow_scale: 0.5 + quality_level: 0.01 + min_distance: 8 + block_radius: 6 + overlay: + show_boxes: true + show_track_trails: false + trail_length: 20 + show_center_marker: false + show_track_ring: false + count_anchor: + - 780 + - 120 + inside_box_only: true + validated_only: true + pending_blink: true + pending_colors: + - - 255 + - 255 + - 0 + - - 0 + - 255 + - 255 + display: + show_window: false + encoder: auto + output_bitrate_kbps: 2000 + codec_preference: + - avc1 + - mp4v + - H264 + performance: + half: false + overlay_buffer_reuse: true + inference_stride: 2 + stream: + enabled: true + shm_dir: /dev/shm + interval_frames: 5 + feedback: + enabled: true + every_n_frames: 3000 + save_images: false + log_to_terminal: true + roi: + inset_left_px: 60 + inset_right_px: 60 + inset_top_px: 0 + inset_bottom_px: 0 + min_overlap_ratio: 0.3 +cameras: + CC1: + camera_num: 1 + count_anchor: + - 780 + - 120 + detection: + min_box_area_px: 2500 + conf: 0.35 + roi: + points: + - - 250 + - 330 + - - 1650 + - 330 + - - 1650 + - 720 + - - 250 + - 720 + CC2: + camera_num: 2 + count_anchor: + - 900 + - 120 + detection: + conf: 0.5 + dedupe_radius_px: 32 + dedupe_frames: 24 + roi: + points: + - - 20 + - 380 + - - 1880 + - 380 + - - 1880 + - 720 + - - 20 + - 720 + CC3: + camera_num: 3 + count_anchor: + - 900 + - 120 + detection: + dedupe_radius_px: 24 + dedupe_frames: 16 + roi: + points: + - - 20 + - 330 + - - 1880 + - 330 + - - 1880 + - 720 + - - 20 + - 720 + CC4: + camera_num: 4 + count_anchor: + - 700 + - 120 + roi: + points: + - - 50 + - 330 + - - 1450 + - 330 + - - 1450 + - 720 + - - 50 + - 720 diff --git a/run_all_coops.sh b/run_all_coops.sh new file mode 100755 index 0000000..1036a38 --- /dev/null +++ b/run_all_coops.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Master runner for 5-coop / 10-floor daily processing +set -u + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export PYTHONPATH="$SCRIPT_DIR/src" + +if [ -f "$SCRIPT_DIR/venv/bin/python" ]; then + PYTHON="$SCRIPT_DIR/venv/bin/python" +else + PYTHON="python3" +fi + +DATE="${1:-$(date +%Y-%m-%d)}" +MODE="${2:-parallel_processes}" + +# List of all configured floors +CONFIGS=( + "configs/K1-L1.yaml" + "configs/K1-L2.yaml" + "configs/K2-L1.yaml" + "configs/K2-L2.yaml" + "configs/K3-L1.yaml" + "configs/K3-L2.yaml" + "configs/K4-L1.yaml" + "configs/K4-L2.yaml" + "configs/K5-L1.yaml" + "configs/K5-L2.yaml" +) + +echo "=================================================================" +echo " Starting 5-Coop Multi-Floor Batch Processing for Date: $DATE" +echo " Execution Mode: $MODE" +echo "=================================================================" + +SUCCESS_COUNT=0 +SKIPPED_COUNT=0 + +for config in "${CONFIGS[@]}"; do + if [ -f "$SCRIPT_DIR/$config" ]; then + echo "" + echo ">>> [PROCESSING] Location Config: $config <<<" + if $PYTHON -m chicken_counter.cli batch --config "$config" --date "$DATE" --mode "$MODE"; then + SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) + else + echo "⚠️ [SKIPPED / ERROR] $config for date $DATE (video not found or error occurred)." + SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) + fi + fi +done + +echo "" +echo "=================================================================" +echo " Multi-Floor Batch Summary: $SUCCESS_COUNT completed, $SKIPPED_COUNT skipped." +echo " Database: db/chicken_counts.db" +echo "=================================================================" diff --git a/src/chicken_counter/batch_discovery.py b/src/chicken_counter/batch_discovery.py index e010a06..595d2d1 100644 --- a/src/chicken_counter/batch_discovery.py +++ b/src/chicken_counter/batch_discovery.py @@ -21,17 +21,59 @@ def discover_camera_videos(day_dir: Path, settings: BatchSettings) -> CameraDisc result = CameraDiscoveryResult() total_configured = len(settings.cameras) + # Exclude output videos and non-video files + def _is_valid_source_video(path: Path) -> bool: + name = path.name.lower() + if name.endswith(("_vis.mp4", "_compressed.mp4", "_overlay.mp4", "_preview.mp4")): + return False + if "output" in path.parts: + return False + return path.suffix.lower() in (".mp4", ".avi", ".mkv", ".mov") + for camera_id, preset in sorted( settings.cameras.items(), key=lambda item: item[1].camera_num, ): - pattern = settings.batch.camera_glob.format(num=preset.camera_num) - matches = sorted(day_dir.glob(pattern)) + num = preset.camera_num + primary_pattern = settings.batch.camera_glob.format(num=num) + matches = [p for p in sorted(day_dir.glob(primary_pattern)) if _is_valid_source_video(p)] + + # Fallback candidate patterns if primary pattern doesn't match + if not matches: + fallback_patterns = [ + f"*cam{num}_*.mp4", + f"*cam_{num}_*.mp4", + f"*camera_{num}_*.mp4", + f"*camera{num}_*.mp4", + f"*_c{num}_*.mp4", + f"*cam{num}.mp4", + f"*camera{num}.mp4", + f"*camera_{num}.mp4", + ] + for fb_pat in fallback_patterns: + matches = [p for p in sorted(day_dir.glob(fb_pat)) if _is_valid_source_video(p)] + if matches: + break + + # Case-insensitive fallback search + if not matches: + for f in sorted(day_dir.iterdir()): + if f.is_file() and _is_valid_source_video(f): + fname = f.name.lower() + if ( + f"cam{num}" in fname + or f"cam_{num}" in fname + or f"camera_{num}" in fname + or f"camera{num}" in fname + or f"_c{num}" in fname + ): + matches.append(f) + if not matches: result.skipped[camera_id] = "video_not_found" continue if len(matches) > 1: - result.skipped[camera_id] = "multiple_matches" + result.skipped[camera_id] = f"multiple_matches ({[m.name for m in matches]})" continue result.found[camera_id] = matches[0] diff --git a/test_run.ds b/test_run_folder/test_run.ds similarity index 100% rename from test_run.ds rename to test_run_folder/test_run.ds diff --git a/test_run.sh b/test_run_folder/test_run.sh similarity index 75% rename from test_run.sh rename to test_run_folder/test_run.sh index 9d5188f..7c54ac7 100755 --- a/test_run.sh +++ b/test_run_folder/test_run.sh @@ -2,14 +2,19 @@ set -u SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" -# Use project-local venv if available -if [ -f "$SCRIPT_DIR/venv/bin/python" ]; then - PYTHON="$SCRIPT_DIR/venv/bin/python" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" + +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" else PYTHON="python3" fi - dates=( "2026-06-10" "2026-06-11" "2026-06-12" "2026-06-13" "2026-06-14" "2026-06-15" "2026-06-16" "2026-06-17" "2026-06-18" "2026-06-19" diff --git a/test_run_hybrid.sh b/test_run_folder/test_run_hybrid.sh similarity index 66% rename from test_run_hybrid.sh rename to test_run_folder/test_run_hybrid.sh index 65cca7f..f5235c0 100755 --- a/test_run_hybrid.sh +++ b/test_run_folder/test_run_hybrid.sh @@ -2,19 +2,20 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" -# Use project-local venv if available -if [ -f "$SCRIPT_DIR/venv/bin/python" ]; then - PYTHON="$SCRIPT_DIR/venv/bin/python" +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" else PYTHON="python3" fi - -# Allow passing a specific date as $1 (e.g. ./test_run_hybrid.sh 2026-06-18) -# Or default to all dates 2026-06-10 through 2026-06-19 -if [ -n "${1:-}" ]; then - dates=("$1") +dates=("$1") else dates=( "2026-06-10" "2026-06-11" "2026-06-12" "2026-06-13" "2026-06-14" diff --git a/test_run_mortality.sh b/test_run_folder/test_run_mortality.sh similarity index 100% rename from test_run_mortality.sh rename to test_run_folder/test_run_mortality.sh diff --git a/test_run_tensor_batch.sh b/test_run_folder/test_run_tensor_batch.sh similarity index 65% rename from test_run_tensor_batch.sh rename to test_run_folder/test_run_tensor_batch.sh index 494b6fe..8d0c1b7 100755 --- a/test_run_tensor_batch.sh +++ b/test_run_folder/test_run_tensor_batch.sh @@ -2,19 +2,20 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" -# Use project-local venv if available -if [ -f "$SCRIPT_DIR/venv/bin/python" ]; then - PYTHON="$SCRIPT_DIR/venv/bin/python" +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" else PYTHON="python3" fi - -# Allow passing a specific date as $1 (e.g. ./test_run_tensor_batch.sh 2026-06-18) -# Or default to all dates 2026-06-10 through 2026-06-19 -if [ -n "${1:-}" ]; then - dates=("$1") +dates=("$1") else dates=( "2026-06-10" "2026-06-11" "2026-06-12" "2026-06-13" "2026-06-14" diff --git a/test_run_video_2026-05-23.sh b/test_run_folder/test_run_video_2026-05-23.sh similarity index 74% rename from test_run_video_2026-05-23.sh rename to test_run_folder/test_run_video_2026-05-23.sh index 3d5e9a4..20e6db0 100755 --- a/test_run_video_2026-05-23.sh +++ b/test_run_folder/test_run_video_2026-05-23.sh @@ -2,15 +2,19 @@ set -u SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" -# Use project-local venv if available -if [ -f "$SCRIPT_DIR/venv/bin/python" ]; then - PYTHON="$SCRIPT_DIR/venv/bin/python" +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" else PYTHON="python3" fi - dates=( "2026-05-23" ) diff --git a/test_run_video_2026-05-24.sh b/test_run_folder/test_run_video_2026-05-24.sh similarity index 72% rename from test_run_video_2026-05-24.sh rename to test_run_folder/test_run_video_2026-05-24.sh index 9ac3c3d..0096db9 100755 --- a/test_run_video_2026-05-24.sh +++ b/test_run_folder/test_run_video_2026-05-24.sh @@ -2,9 +2,19 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" -PYTHON="python3" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" +else + PYTHON="python3" +fi dates=( "2026-05-24" ) diff --git a/test_run_video_2026-06-06.sh b/test_run_folder/test_run_video_2026-06-06.sh similarity index 75% rename from test_run_video_2026-06-06.sh rename to test_run_folder/test_run_video_2026-06-06.sh index 38d4308..78dc630 100755 --- a/test_run_video_2026-06-06.sh +++ b/test_run_folder/test_run_video_2026-06-06.sh @@ -2,15 +2,19 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" -# Use project-local venv if available -if [ -f "$SCRIPT_DIR/venv/bin/python" ]; then - PYTHON="$SCRIPT_DIR/venv/bin/python" +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" else PYTHON="python3" fi - dates=( "2026-06-06" ) diff --git a/test_run_video_2026-06-16.sh b/test_run_folder/test_run_video_2026-06-16.sh similarity index 72% rename from test_run_video_2026-06-16.sh rename to test_run_folder/test_run_video_2026-06-16.sh index 6038e5c..47fd14f 100755 --- a/test_run_video_2026-06-16.sh +++ b/test_run_folder/test_run_video_2026-06-16.sh @@ -2,9 +2,19 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" -PYTHON="python3" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" +else + PYTHON="python3" +fi dates=( "2026-06-16" ) diff --git a/test_run_video_2026-06-18.sh b/test_run_folder/test_run_video_2026-06-18.sh similarity index 72% rename from test_run_video_2026-06-18.sh rename to test_run_folder/test_run_video_2026-06-18.sh index 592d9a7..e670523 100755 --- a/test_run_video_2026-06-18.sh +++ b/test_run_folder/test_run_video_2026-06-18.sh @@ -2,9 +2,19 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export PYTHONPATH="$SCRIPT_DIR/src" -PYTHON="python3" +if [ -d "$SCRIPT_DIR/../src" ]; then + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +else + PROJECT_ROOT="$SCRIPT_DIR" +fi +cd "$PROJECT_ROOT" +export PYTHONPATH="$PROJECT_ROOT/src" +if [ -f "$PROJECT_ROOT/venv/bin/python" ]; then + PYTHON="$PROJECT_ROOT/venv/bin/python" +else + PYTHON="python3" +fi dates=( "2026-06-18" )