- detect_interval=2 on both pipelines; stabilizer 10-frame hold bridges skipped frames (GPU inference cut ~half during active batch) - sup_det_interval 5 -> 10 (still <= stabilizer hold / synthetic max_age) - preview_every_n default 2 -> 5 - AnnotatedVideoWriter: codec auto-chain gstreamer_nvenc -> avc1 -> mp4v, max_height downscale (even dims, INTER_AREA); backend logged - output_max_height plumbed Job -> web checkbox (720p) -> CLI --output-height - README: 720p option, CLI flag - 153 tests pass (+13)
Feedmill Recounter
AI video analysis tool for counting objects (sacks, boxes) in feedmill videos. Built on top of karung_counter_semarang.
Features
- CLI: Process videos from the command line with any model + class filter
- Web UI: Upload videos, select models, download annotated output on port 9050
- Live Preview: MJPEG streaming — watch processing in real-time (15+ FPS at 640p)
- Multiple Models: Run multiple model configurations on the same video for comparison
- Model Groups: Models grouped by stem (e.g.,
best.pt,best.engine) with format dropdown - Class Filtering: Choose which classes to count (sack, box, truck)
- Auto Truck Detection: Automatically loads
truck-detectorwhen selected model lacks truck class - Counting Zone Modes: Auto truck detection, fixed zone presets (zones.json), or auto-detect then freeze
- Sack/Box Counts: Separate in/out counts per object class in results
- Video Reuse: Re-analyze previously uploaded videos without re-uploading
- Annotated Output: Download MP4 videos with detection overlays for human review
- 720p Output Option: Downscale annotated MP4 to 720p for smaller files / faster encode
- Async Processing: Background job queue — upload and poll status
- Abort: Cancel running jobs mid-processing
Quick Start
pip install -e ".[dev]"
# List available models
recounter --list-models --models-dir ./models
# Process a single video via CLI
recounter --video input.mp4 --model v4-best.pt --filter sack --output-dir ./output
# Start web UI
recounter-web
# Open http://localhost:9050
CLI Reference
recounter --video PATH Input video file
--model NAME Model filename (repeatable for multiple)
--all-models Run all discovered models
--list-models List available models and exit
--filter NAME Class filter (repeatable): sack, box, truck
--sack-conf FLOAT Sack confidence threshold (default: 0.4)
--truck-conf FLOAT Truck confidence threshold (default: 0.5)
--output PATH Output path (single model only)
--output-dir DIR Output directory (default: ./output)
--output-height INT Downscale annotated output to this height (e.g. 720)
--models-dir DIR Models directory (default: ./models)
Web UI
- Port: 9050 (configurable via
WEB_PORTenv) - Upload: Drag-and-drop or click to upload video, or select from previously uploaded videos
- Model Selection: Grouped cards with format dropdown (
.engine/.pt/.onnx) and class filter - Live Preview: Real-time MJPEG stream during processing
- Job Status: Auto-refreshing progress page with abort button
- Download: Annotated MP4 per model result
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Upload form with model groups |
/upload |
POST | Start processing job |
/upload/reuse |
POST | Re-analyze an existing uploaded video |
/status/<job_id> |
GET | Job status with results |
/jobs |
GET | All jobs listing |
/download/<job_id>/<filename> |
GET | Download output video |
/preview/thumb/<job_id> |
GET | Thumbnail JPEG for a job's video |
/api/preview/<job_id> |
GET | MJPEG live stream during processing |
/api/models |
GET | List model groups (JSON) |
/api/videos |
GET | List uploaded videos (JSON) |
/api/videos/<filename> |
DELETE | Delete an uploaded video |
/api/jobs |
GET | List all jobs (JSON) |
/api/jobs/<job_id> |
GET | Job detail (JSON) |
/api/jobs/<job_id>/cancel |
POST | Cancel a running job |
/api/jobs/<job_id>/samples |
GET | Sample frame paths (JSON) |
Configuration
| Variable | Default | Description |
|---|---|---|
WEB_HOST |
0.0.0.0 |
Flask bind address |
WEB_PORT |
9050 |
Flask port |
FLASK_DEBUG |
false |
Flask debug mode |
MODELS_DIR |
./models |
Model weights directory |
UPLOAD_DIR |
./uploads |
Uploaded videos directory |
OUTPUT_DIR |
./output |
Annotated output directory |
Project Structure
src/
├── interfaces.py # Detection dataclass + protocols
├── detection.py # YOLO detectors with class filtering
├── tracking.py # ByteTrack/FastTrack tracker
├── synthetic_track.py # Synthetic IDs for untracked detections
├── stabilizer.py # Bbox smoothing + occlusion hold
├── truck_roi.py # Truck ROI detection + EMA smoothing
├── zone_config.py # Fixed-zone presets (zones.json) + scaling
├── counting.py # Line-crossing counter
├── batch.py # Batch lifecycle state machine
├── dashboard.py # Frame annotation overlay
├── video_writer.py # Annotated video writer
├── model_registry.py # Model discovery, grouping, class metadata
├── pipeline.py # Video processing pipeline
├── preview.py # Video probing, thumbnails, sample frames
└── job.py # Async job queue
Model Formats
Models are grouped by stem name. Each group has available formats:
| Format | Extension | Speed | Notes |
|---|---|---|---|
| TensorRT | .engine |
Fastest | Preferred on Jetson (GPU-optimized) |
| PyTorch | .pt |
Medium | Works everywhere |
| ONNX | .onnx |
Medium | Requires onnxruntime (not available on Jetson) |
Format preference order: .engine > .pt > .onnx
Truck Detection
The pipeline requires truck detection to start counting. If the selected model lacks a "truck" class (e.g., best.engine which only has "sack"), the system automatically loads the truck-detector model for truck detection.
Models with "truck" class: v4-best, model_karung_truk
Models without "truck" class: best, karung-dimuat, yolo11n-bbox (auto-loads truck-detector)
Counting Zone
The web upload form offers three zone modes:
- Auto-detect Truck (default): truck detector finds the main truck each ~15 frames, ROI + counting line follow it (EMA-smoothed with 10px deadband, sticky truck pick)
- Fixed Zone: static zone from
zones.jsonpresets, scaled from reference resolution to video resolution; never moves; presets editable viatools/calibrate_zone.py - Auto-detect + Freeze: auto-detects the truck then locks the ROI permanently after 3 consecutive detections (geometry fixed, truck-presence still tracked for batch state machine)
In multi-model runs, model #1 (primary, alphabetical order) is tracked every frame; supplementary models run every 5th frame; detections from supplementary models get synthetic track IDs so counts and overlays stay stable.