Chicken Counter
First-pass Python pipeline for Jetson-style chicken counting using Ultralytics YOLO tracking with BoT-SORT, ROI/gate-based counting, backward-motion detection from background optical flow, and an OpenCV overlay that matches the provided reference visual.
What Is Included
- Modular runtime under
src/chicken_counter/ - Sample camera config in
configs/cameras/example_camera.yaml - BoT-SORT tracker settings in
configs/trackers/botsort_chicken.yaml - Complete Entity Relationship Diagram & DB Architecture:
ERD.md - CLI entrypoint:
chicken-counter
Pipeline Stages
- Capture frames from a video file or camera stream
- Run
model.track(..., persist=True)with class filtering for chickens only - Maintain per-track state, track live occupancy inside the counting box, and count unique box entries
- Estimate backward motion from sparse optical flow on background features
- Render an OpenCV overlay with ROI, white gates, IDs, trails, and both live/cumulative counts
Project Layout
configs/
floor_config/ ← Lightweight floor configs (K1-L1 to K5-L2, kandang-atas)
K1-L1.yaml .. K5-L2.yaml ← Extends cycle7_batch_optimized.yaml
kandang-atas.yaml ← Extends cycle7_batch_optimized.yaml
cameras/example_camera.yaml
cycle7_batch.yaml
cycle7_batch_optimized.yaml ← Base batch processing config
mortality_config.yaml
trackers/botsort_chicken.yaml
src/chicken_counter/
weight/ ← Weight estimation, calibration, FatChicken curation & parquet storage
calibration.py ← Metric px/cm conversion & morphometrics (eccentricity, diameter, area)
curation.py ← Track deduplication & age-adaptive MAD z-score outlier filter
aggregation.py ← Camera-day statistical distribution & multi-camera FUSED synthesis
growth_model.py ← Gompertz growth curves & Day 1+ broiler prediction
storage.py ← Standardized Parquet/CSV export (detections, features, predictions)
daily_size_comparison.py ← Multi-sheet comparative workbook (daily_size_comparison.xlsx)
batch_discovery.py
batch_runner.py
capture.py
cli.py
compress.py
config.py
counting.py
db_utils.py ← SQLite WAL connection management & recovery handlers
discovery.py
engine_utils.py
mortality.py
motion.py
overlay.py
pipeline.py
report.py
storage_lifecycle.py ← Automated raw video purging, artifact validation & retention policy
tracking.py
types.py
video_writer.py
dashboard.py
export_engine.py
export_excel_report.py
run_all_coops.sh ← Master multi-coop batch runner
run_mortality_all.sh ← Master multi-coop mortality carcass scanner
start_dashboard.sh ← Live dashboard launcher
test_run_folder/ ← Archive of test run scripts and logs
Install
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt
venv/bin/pip install -e .
For Jetson deployment you will usually want a Jetson-compatible OpenCV and PyTorch stack already installed, then install the rest of the package around that environment.
Tip: See
RUN.mdfor a full step-by-step quickstart guide for new developers.
Run
Update configs/cameras/example_camera.yaml with:
source: your input video path, RTSP URL, or camera indexdetection.model_path: your TensorRT.engineor.ptcheckpoint- ROI coordinates and gate lines calibrated for the real camera
Then run:
chicken-counter --config configs/cameras/example_camera.yaml
Press q to quit the preview window.
For headless Jetson MP4 runs, set display.show_window: false and keep
display.output_path enabled so the annotated video is written without opening a GUI.
The video writer tries a Jetson GStreamer hardware encoder first when
display.encoder: auto or gstreamer, then falls back to OpenCV codecs in
display.codec_preference order (default: avc1, mp4v, H264).
Config Notes
Detection
The sample config restricts inference to class 0 and keeps ignored classes explicit:
classes: [0]ignored_classes: [1, 2]confandiouare exposed for real-footage tuningmin_box_area_pxcan be used to reject very small partial detections from validationdevice: "0"should be set explicitly on Jetson CUDAimgszmust match the size used when a TensorRT.enginewas exported
For TensorRT deployments, point detection.model_path at your .engine file and keep
performance.half: false (precision is already baked into the engine build).
Tracking
The supplied tracker config enables:
tracker_type: botsortgmc_method: nonefor fixed-camera MP4 runs (avoids duplicate optical flow)with_reid: false
Re-enable gmc_method: sparseOptFlow in configs/trackers/botsort_chicken.yaml only if
the camera mount moves or footage is shaky enough that track IDs drift without GMC.
Starting thresholds match the prompt defaults and can be tuned in
configs/trackers/botsort_chicken.yaml.
Periodic Runtime Feedback
You can enable checkpoint-style progress feedback every N frames with the feedback
config block:
feedback:
enabled: true
every_n_frames: 300
save_images: true
image_output_dir: output/checkpoints
log_to_terminal: true
When enabled, the pipeline will:
- print a periodic progress line with frame number, elapsed time, processing FPS, ETA, and total count
- save the current annotated frame as a checkpoint image (when
save_images: true)
This is especially useful on Jetson when processing MP4 files headlessly, because you can verify progress from the terminal and inspect saved snapshot images without needing an on-device display.
Counting ROI And Gates
The overlay is intended to resemble the reference image while staying easy to read:
- no outer green ROI outline
- one visible counting rectangle that is slightly smaller and cleaner than the previous broad region
- orange chicken bounding boxes that are visually distinct from the counting guides
- per-bird numeric labels based on count sequence, not raw tracker ID, using a non-white color
- short centroid trails
- one bold
TOTAL ENTEREDcaption as the main count display, using a non-white color
The green ROI should be treated as the actual middle counting box. The current counting semantics are:
Inside Box: how many currently tracked chickens have their centroids inside the ROITotal Entered: how many unique tracked chickens have entered the ROI at least once- a chicken is only valid for
Total Enteredif its bounding-box area meetsmin_box_area_px - if backward motion is confirmed, the current frame is finalized and then the pipeline stops
- validated chickens receive a stable visible sequence number
1, 2, 3, ...in entry order - unvalidated chickens are tracked internally but do not show a visible sequence number yet
The implementation still assumes normal travel is bottom_to_up.
Calibration Workflow
- Start with a representative frame from the real camera.
- Set
roi.pointsso the counting rectangle spans the intended middle counting box only. - If the displayed rectangle feels too large or small, tighten or expand
roi.pointsdirectly. - Run a short clip and compare
Inside Boxagainst the visible birds currently in that box. - Increase
min_box_area_pxif small partial chickens are being counted too early. - Verify
Total Enteredonly increases when a new tracked bird enters the box during forward motion and is large enough to be valid. - Verify that once backward motion is confirmed, the output video ends at that point and the MP4 is finalized cleanly.
- Verify that the highest displayed sequence number matches
Total Entered. - Verify the final freeze frame stays on screen long enough to read the last total clearly.
Backward-Motion Tuning
The stop trigger is separate from chicken tracks. It measures background motion while masking detected chicken boxes.
Tune these values against real footage:
motion.forward_signmotion.ema_alphamotion.reverse_enter_thresholdmotion.reverse_exit_thresholdmotion.debounce_framesmotion.min_featuresmotion.stride_frames(run flow every N frames;2is faster)motion.flow_scale(downscale ROI gray before flow;0.5is faster)motion.max_corners(fewer corners = faster; try80)
Important: confirm the actual sign convention from real cart footage before treating the configured forward direction as final.
Jetson Performance Speedups
For long batch runs, enable inference and motion stride in config:
performance:
inference_stride: 2 # run YOLO+BoT-SORT every 2nd frame; reuse tracks in between
motion:
stride_frames: 2 # run optical flow every 2nd frame
flow_scale: 0.5 # half-resolution flow inside ROI crop
max_corners: 80
detection:
imgsz: 640 # keep 640 while using existing TensorRT .engine
configs/cycle7_batch.yaml and configs/cycle7_batch_optimized.yaml already use these production defaults.
configs/cycle7_batch_optimized.yaml adds per-camera parallelism, trimmed inference settings, and optimized YAML structure for the Sukawarna enclosure.
Validation: run a short clip with stride enabled, then compare total_entered against
inference_stride: 1 and motion.stride_frames: 1. Watch checkpoint fps= logs for
speedup. Box positions may lag by up to one frame on skipped inference frames.
Set inference_stride: 1 or motion.stride_frames: 1 to restore full per-frame accuracy
for tuning.
Known Limits In This First Pass
- No DeepStream integration yet
- No multi-process or multi-camera scheduler yet
- Counting currently assumes vertical motion and
bottom_to_uptravel - The live box count depends on stable tracking centroids inside the ROI
- The optical-flow trigger is vision-first, though the config structure leaves room for a future controller/encoder integration path
Cross-Machine Portability & Model Compatibility
The pipeline supports ONNX (.onnx), PyTorch (.pt), and hardware-accelerated TensorRT (.engine) models:
- Default ONNX
.onnxWeights: By default, batch counting configurations usemodels/chicken-detection-model-v26n-300e-best-2026-05-02-NEW.onnxwithCUDAExecutionProviderfor high-throughput (~318 FPS) cross-hardware GPU acceleration. - Mortality Detection: Strictly uses the dedicated segmentation model
models/chicken-detection-model-v26n-seg-300e-best-2026-04-18.pt. - Engine Verification Toggle (
--verify-engine): When using.enginemodels across different GPUs/machines, pass--verify-engine(or setverify_engine: truein YAML) to trigger automated hardware compatibility checking and self-healing recompilation. - Runtime Fail-Safe: If an
.enginemodel fails to load at runtime,DetectionTrackerautomatically falls back to the corresponding.ptor.onnxweights inmodels/. - Manual Export Tool: You can compile TensorRT engines anytime using
export_engine.py:./venv/bin/python export_engine.py models/chicken-detection-model-v26n-300e-best-2026-05-02-NEW.pt --half --workspace 4
Multi-Stage Growth Cycles & Day 0 Configuration
The pipeline dynamically adjusts detection, counting, and weight estimation based on flock age:
- Day 0 (
cycle_start_date): Day-Old Chicks (DOC) arrival date (default:"2026-05-22").- Day 0 Rule: Counting ONLY (initial stocking headcount inventory). Vision weight estimation is bypassed on Day 0 as each cycle has its own specific DOC arrival weigh-in.
- Day 1+ Rule: Counting AND Weight Estimation runs daily across all camera videos.
early_cycle(Days 0–15): Automatically applies high-sensitivity detection thresholds (conf: 0.12,min_box_area_px: 200,min_overlap_ratio: 0.25) for small fast-moving chicks.mid_cycle(Days 16+): Preserves standard tuned per-camera defaults (conf: 0.35–0.50,min_box_area_px: 2500–3000).
Weight Estimation, Track Curation & Measurement Storage
The edge pipeline aggregates physical metric measurements and predicts chicken body weights during daily counting runs:
- Per-Track Measurements (
detections_{date}_{camera_id}.parquet):- Captures 1 row per validated chicken with exact pixel bounding boxes and physical centimeter measurements (
bbox_area_cm2,width_cm,height_cm,minor_axis_cm,major_axis_cm,aspect_ratio_cm,centroid_x_cm,centroid_y_cm,perimeter_cm).
- Captures 1 row per validated chicken with exact pixel bounding boxes and physical centimeter measurements (
- FatChicken Curation & MAD Outlier Elimination (
curation_{date}_{camera_id}.json):- Deduplicates observations by
track_id(best observation upon count trigger). - Eliminates anomalous bounding box areas via age-adaptive MAD Z-score (
z = 0.6745 \times (x - \text{median}) / \text{MAD}).
- Deduplicates observations by
- Statistical Distribution Aggregation (
cage_day_features.parquet):- Calculates distribution metrics (
mean,median,p25,p75,std,cv,iqr) for all dimensions includingminor_axis_cm_medianandmajor_axis_cm_medianper camera and the multi-cameraFUSEDrow.
- Calculates distribution metrics (
- Audited Weight Prediction (
predictions_{date}.parquet):- Predicts flock body weights using Ross 308 / Cobb 500 Gompertz curves combined with vision allometric gain (
predicted_weight_g,doc_weight_g,gompertz_baseline_g,vision_gain_g).
- Predicts flock body weights using Ross 308 / Cobb 500 Gompertz curves combined with vision allometric gain (
- SQLite Database Storage (
db/chicken_counts.db):- Persists all counting and weight statistics into the
batch_runstable (area_cm2_median,minor_axis_cm_median,major_axis_cm_median,predicted_weight_g,gompertz_baseline_g,vision_gain_g,n_weight_samples,rejection_rate).
- Persists all counting and weight statistics into the
CREATE TABLE batch_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
location TEXT NOT NULL,
camera_id TEXT NOT NULL,
total_entered INTEGER NOT NULL DEFAULT 0,
frames_processed INTEGER NOT NULL DEFAULT 0,
elapsed_seconds REAL NOT NULL DEFAULT 0.0,
stopped_reason TEXT NOT NULL DEFAULT '',
source_video TEXT NOT NULL DEFAULT '',
generated_at TEXT NOT NULL DEFAULT '',
cycle_day INTEGER NOT NULL DEFAULT 0,
area_cm2_median REAL NOT NULL DEFAULT 0.0,
minor_axis_cm_median REAL NOT NULL DEFAULT 0.0,
major_axis_cm_median REAL NOT NULL DEFAULT 0.0,
predicted_weight_g REAL NOT NULL DEFAULT 0.0,
gompertz_baseline_g REAL NOT NULL DEFAULT 0.0,
vision_gain_g REAL NOT NULL DEFAULT 0.0,
n_weight_samples INTEGER NOT NULL DEFAULT 0,
rejection_rate REAL NOT NULL DEFAULT 0.0,
UNIQUE(date, location, camera_id)
);
Storage Lifecycle & Automated Raw Video Purging
To prevent edge NVMe drives from running out of disk space during multi-week rearing cycles:
- Strict Multi-Camera Artifact Validation: Before deleting any raw video,
src/chicken_counter/storage_lifecycle.pyvalidates that all daily Parquet files, compressed MP4s, andcounts_*.jsonexist and are uncorrupted. - Automated Post-Validation Purge: Set
purge_raw_videos: truein batch YAML or pass--purge-raw-videosto delete large uncompressed raw camera inputs automatically once processing succeeds. - On-Demand Retention Cleanup: Run
chicken-counter cleanup --days 7to remove raw videos older than N days while preserving all datasets and compressed MP4s.
Mortality Detection
A separate pipeline detects carcasses (dead birds) from still photos. Supports multi-image daily runs and date subfolders (mortality/YYYY-MM-DD/).
# Run across all coops & floors (auto-discovers mortality directories)
./run_mortality_all.sh
# Run on a specific date (auto-creates/routes to mortality/2026-05-23/)
chicken-counter mortality --date 2026-05-23
Key features:
- Multi-Image & Multi-Day Support: Processes multiple images per day (e.g. morning/afternoon scans), aggregates the grand total carcass count (
total_mortality_count), and saves outputs into date-isolated directories. - Direct High-Precision Segmentation (Default): Uses the segmentation model (
models/chicken-detection-model-v26n-seg-300e-best-2026-04-18.pt) directly. 2-pass Detect & Refine (two_pass: false) is disabled by default because direct segmentation achieves higher accuracy and avoids false rejection on real farm photos. - Optional 2-Pass Refine (
--two-pass): An optional mode combining initial segmentation candidate proposals withcv2.matchTemplatesimilarity refinement. - Containment filtering: boxes where
IoA > 0.50against a larger box are suppressed. - Centroid deduplication: detections whose centroids are within
dedupe_radius_pxof each other are merged to prevent counting the same carcass twice.
Outputs for each daily run:
output_<name>.jpg— annotated images with bounding boxes and carcass IDsmortality_report.json— full summary report withtotal_mortality_count, per-image breakdown, and detection coordinates
Config: configs/mortality_config.yaml
Headless Jetson MP4 Example
For a headless run that saves both output video and periodic checkpoint images, use a config shaped like this:
display:
show_window: false
output_path: output/coop_cam_03_overlay.mp4
encoder: auto
output_bitrate_kbps: 4000
feedback:
enabled: true
every_n_frames: 300
save_images: true
image_output_dir: output/checkpoints
log_to_terminal: true
40-Minute Jetson Recipe
For long headless runs (~72,000 frames at 30 FPS), use the production-oriented settings
in configs/cameras/example_camera.yaml:
detection:
device: "0"
imgsz: 640
model_path: /path/to/your-model.engine
overlay:
show_track_trails: false
show_track_ring: false
motion:
max_corners: 80
stride_frames: 2
flow_scale: 0.5
display:
show_window: false
output_path: /home/asus/.Codes/try-sukawarna-vis.mp4
encoder: auto
output_bitrate_kbps: 4000
performance:
half: false
overlay_buffer_reuse: true
inference_stride: 2
feedback:
enabled: true
every_n_frames: 900
log_to_terminal: true
save_images: false
Tracker YAML should use gmc_method: none for fixed-camera footage.
Lower display.output_bitrate_kbps produces smaller MP4 files with more compression
artifacts. Start at 4000 and adjust after inspecting output quality.
Checkpoint logs look like:
[checkpoint] frame=9000/72000 elapsed=18m12s fps=8.2 total_entered=142 eta=2h05m status=running
When backward motion is confirmed, the pipeline now:
- finishes the current annotated frame
- writes that frame to the output video
- logs the backward-stop event
- appends a short freeze frame so the final total is readable
- exits immediately afterward, so the output MP4 ends there
Daily Cycle7 Multi-Camera Batch
For everyday processing of 4 cameras, use configs/cycle7_batch.yaml.
Input folder layout
Place today's videos in a VIDEOS folder adjacent to the project directory (i.e. ../VIDEOS relative to the project root):
../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
kandang_1_camera_3_2026-06-18_121012.mp4
kandang_1_camera_4_2026-06-18_121530.mp4
Date folders use YYYY-MM-DD. Camera files are matched by camera_num using the
pattern kandang_*_camera_{num}_*.mp4.
Run commands
# Process today's folder using default parallel process mode
chicken-counter batch --config configs/cycle7_batch_optimized.yaml
# Process a specific date using Model-Level Tensor Batching mode
chicken-counter batch --config configs/cycle7_batch_optimized.yaml --date 2026-06-18 --mode tensor_batching
# Process a specific date using Hybrid mode (Threaded CPU + Batched GPU)
chicken-counter batch --config configs/cycle7_batch_optimized.yaml --date 2026-06-18 --mode hybrid
# Run automated batch script across all coops & floors
./run_all_coops.sh # Process all discovered coops for today
./run_all_coops.sh 2026-06-18 # Process all coops for a specific date
# Run automated batch scripts (archived under test_run_folder/)
./test_run_folder/test_run.sh # Parallel sliding-window pool runner
./test_run_folder/test_run_tensor_batch.sh 2026-06-18 # Tensor Batching
./test_run_folder/test_run_hybrid.sh 2026-06-18 # Hybrid CPU/GPU execution mode
Execution Modes (execution_mode)
Set batch.execution_mode in configs/cycle7_batch_optimized.yaml or override via --mode:
parallel_processes(Default): Launches every camera in its own OS process viaProcessPoolExecutor(spawn), one worker per camera sharing the GPU. No tensor batching — each worker runs its own sequential pipeline. Verified on 4 cameras: ~3.4 min wall vs ~6.7 min serial (~2× speedup), identical counts.tensor_batching: Synchronizes camera frame streams and executes a single batched GPU model inference pass across all cameras (batch_size=N).hybrid: Combines multi-threaded CPU frame capture, optical flow, and rendering across CPU cores with a single synchronized batched GPU forward pass (batch_size=N).
Single-camera mode still works:
chicken-counter --config configs/cameras/example_camera.yaml
chicken-counter run --config configs/cameras/example_camera.yaml
Daily Full-Cycle Simulation (cron)
simulate_daily_cycle.sh runs the full counting + weight pipeline daily at 08:00 WIB:
0 8 * * * flock -n /tmp/chicken-full-cycle-sim.lock /home/asus/Project_FOLDER/.Codes/chicken-counting-sukawarna-det/simulate_daily_cycle.sh >> /home/asus/Project_FOLDER/.Codes/chicken-counting-sukawarna-det/logs/daily-full-cycle-sim.log 2>&1
- Maps today's date onto archived source videos under
../VIDEOS/cycle7/FULL_Cycle_Kandang_Atas_Test/(2026-05-22..2026-07-09, 49 days, loops back after the end date) and symlinks them into../VIDEOS/cycle7/FULL_CYCLE_1_FLOOR_SIM/<date>/. - Config
configs/floor_config/K1-L1.yaml(location=K1-L1,coop=K1), cycle day computed from--cycle-start-date,execution_mode=parallel_processes. - Skip-if-exists;
flockprevents overlapping runs.
Output layout
../VIDEOS/cycle7/kandang-atas/2026-06-18/output/
CC1_vis.mp4
CC1_compressed.mp4
CC2_vis.mp4
CC2_compressed.mp4
...
checkpoints/CC1/frame_003000.jpg
checkpoints/CC2/frame_006000.jpg
counts_2026-06-18.json
After all 4 cameras finish counting, the batch runner compresses each annotated video
to under batch.compress_max_mb (default 200 MB) using ffmpeg.
Per-camera counting boxes
| Camera | ROI points |
|---|---|
| CC1 | [250,330], [1650,330], [1650,720], [250,720] |
| CC2 | [20,380], [1880,380], [1880,720], [20,720] |
| CC3 | [20,330], [1880,330], [1880,720], [20,720] |
| CC4 | [50,330], [1450,330], [1450,720], [50,720] |
Tune these in configs/cycle7_batch.yaml if a lane drifts after camera maintenance.
JSON report format
counts_{date}.json contains per-camera totals and the sum across all 4 cameras:
{
"date": "2026-07-09",
"generated_at": "2026-07-09T11:45:00+00:00",
"cameras": {
"CC1": {
"total_entered": 142,
"source_video": "kandang_1_camera_1_2026-07-09_120056.mp4",
"vis_video": "CC1_vis.mp4",
"compressed_video": "CC1_compressed.mp4",
"compressed_size_mb": 187.4,
"frames_processed": 68432,
"stopped_reason": "backward",
"elapsed_seconds": 8234.5
}
},
"total_entered_sum": 580
}
Checkpoint images
Batch mode saves review images every checkpoint_every_n_frames (default 3000) per camera.
For a ~72k frame run that is about 24 images per camera.
Cron example
0 7 * * * cd /path/to/chicken-counting-sukawarna-det && ./run_all_coops.sh >> logs/cycle7-batch.log 2>&1
Full-Cycle Parquet & Weight Analytics Consolidation
To aggregate all daily outputs into unified full-cycle datasets:
# Consolidate all processed date folders under a dataset root directory:
chicken-counter consolidate-parquets --root-dir /path/to/cycle7/FULL_Cycle_Kandang_Atas_Test
Generated Files in weight_and_parquet_data/:
weight_and_parquet_data/
├── full_cycle_detections.parquet (Master curated detections across all cameras)
├── full_cycle_detections.csv (CSV fallback)
├── full_cycle_cage_features.parquet (Full multi-camera & FUSED distribution ledger)
├── full_cycle_cage_features.csv (CSV fallback)
├── full_cycle_predictions.parquet (Chronological Gompertz vs vision weight predictions)
├── full_cycle_predictions.csv (CSV fallback)
├── full_cycle_curation_summary.json (Consolidated outlier rejection metrics)
└── daily_size_comparison.xlsx (Multi-sheet Excel with manual_g & error analysis)
daily_size_comparison.xlsx:all_days: FUSED daily medians, manual scale weights (manual_g), vision predictions (vision_pred_g), and% error vs manual.per_camera: Size morphometrics split across all 4 cameras (CC1toCC4).legend: Metric descriptions and post-market thinning highlights (day >= 36).
Dashboard & API
Start the live dashboard and API server:
# Portable launcher (recommended) — auto-discovers mortality dir
./start_dashboard.sh
# Or start manually
PYTHONPATH=src venv/bin/python dashboard.py \
--port 8090 \
--db db/chicken_counts.db \
--mortality-dir ../VIDEOS/cycle7/kandang-atas/mortality
Key API endpoints (see API.md for full schema):
| Endpoint | Description |
|---|---|
GET /api/db/summary |
Lifetime totals across all dates |
GET /api/db/history |
Per-date summary, newest first |
GET /api/db/date/<YYYY-MM-DD> |
Per-camera breakdown for a date |
GET /api/mortality/latest |
Latest carcass detection report |
GET /api/mortality/history |
All mortality reports, newest first |
GET /api/mortality/image/<name> |
Serve annotated output JPEG |
Next Jetson-Focused Improvements
- Add a hardware-aware video ingest path for CSI/GStreamer.
- Export richer event logs for per-bird count timestamps.
- Add a controller-signal adapter so encoder direction can override vision when available.
Recent work includes daily 4-camera batch processing, JSON count reports, post-run
compression under 200 MB, GStreamer hardware encoding, overlay buffer reuse,
duplicate optical-flow removal (gmc_method: none), long-run ETA logging, mortality
2-pass detection with feature similarity search, and a REST API via dashboard.py.