11 KiB
Chicken Counter API
Base URL: http://<jetson-ip>:8080
System Status & Live Monitoring
GET /api/status
Returns real-time pipeline activity status, active streaming cameras, latest processed date, and all-time total counts.
{
"status": "running",
"is_counting_active": true,
"active_cameras": ["CC1", "CC2", "CC3", "CC4"],
"latest_counted_date": "2026-06-18",
"total_chickens_all_time": 146418,
"cycle_start_date": "2026-05-22",
"timestamp": "2026-08-14T08:30:00.000000+00:00"
}
GET /
Returns the dashboard HTML page.
GET /api/cameras
List cameras currently writing to /dev/shm.
{"cameras": ["CC1", "CC2", "CC3"]}
GET /shm/<camera_id>/stats.json
Live stats for the active pipeline.
{
"frame_index": 5120,
"inside_box_count": 42,
"total_entered_count": 1858,
"track_count": 99,
"backward_active": false,
"smoothed_speed": 4.3,
"count_events": 0,
"run_date": "2026-06-10"
}
GET /shm/<camera_id>/frame.jpg
Live JPEG frame from the active pipeline.
Database
All endpoints require the dashboard to be started with --db <path>. If no DB exists, endpoints return [] or {}.
GET /api/config/cycle_start_date
Returns or updates the active Day 0 (cycle_start_date).
# Query active Day 0
GET /api/config/cycle_start_date
→ {"cycle_start_date": "2026-05-22"}
# Override Day 0 dynamically via query param or POST payload
GET /api/config/cycle_start_date?set=2026-05-22
POST /api/config/cycle_start_date {"cycle_start_date": "2026-05-22"}
→ {"status": "ok", "cycle_start_date": "2026-05-22"}
GET /api/db/summary
Overall totals across all dates and locations.
{
"days": 12,
"locations": 2,
"total_runs": 48,
"total_chickens": 125000,
"total_hours": 8.5
}
GET /api/db/history
Per-date summary, newest first (max 50 rows).
[
{
"date": "2026-06-10",
"location": "kandang-atas",
"cams": 4,
"total": 5570,
"minutes": 40.2
}
]
GET /api/db/date/<date>
Detail for a specific date. Format: YYYY-MM-DD.
{
"date": "2026-06-10",
"total": {"total": 5570, "minutes": 40.2},
"cameras": [
{
"camera_id": "CC1",
"total_entered": 1500,
"frames_processed": 24800,
"elapsed_seconds": 600.5,
"stopped_reason": "backward",
"source_video": "kandang_1_camera_1_2026-06-10_120056.mp4",
"location": "kandang-atas"
}
]
}
GET /api/db/camera/<camera_id>
History for a specific camera across all dates (max 50 rows).
[
{
"date": "2026-06-10",
"location": "kandang-atas",
"total_entered": 1500,
"frames_processed": 24800,
"elapsed_seconds": 600.5,
"stopped_reason": "backward"
}
]
GET /api/db/location/<location>
Summary and history for a specific location.
{
"location": "kandang-atas",
"summary": {"days": 5, "total": 25000, "hours": 3.2},
"history": [
{
"date": "2026-06-10",
"cameras": "CC1, CC2, CC3, CC4",
"total": 5570,
"minutes": 40.2
}
]
}
Database Schema
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 '',
UNIQUE(date, location, camera_id)
);
Data is inserted automatically by the batch runner when location and db_path are configured in the batch YAML, or manually via:
python3 store_results.py output/counts_2026-06-10.json --location kandang-atas --db chicken_counts.db
Mortality Detection
Mortality endpoints require the dashboard to be started with --mortality-dir <path>. The path must contain a mortality_report.json file generated by ./test_run_mortality.sh. Multiple directories can be registered with repeated --mortality-dir flags.
If multiple images are captured in a single day (e.g. morning and afternoon scans), the pipeline processes all images in the input directory, generates marked output JPEGs (output_<name>.jpg), and aggregates the daily total carcass count into total_mortality_count.
GET /api/mortality/coops
Returns a summary of all detected coops with their covered floors and mortality figures:
[
{
"coop": "K1",
"covered_floors": ["K1-L1", "K1-L2", "K1-L3"],
"total_carcasses": 12,
"total_scans": 3,
"latest_date": "2026-06-18",
"latest_count": 4
},
{
"coop": "K2",
"covered_floors": ["K2-L1", "K2-L2", "K2-L3"],
"total_carcasses": 8,
"total_scans": 2,
"latest_date": "2026-06-18",
"latest_count": 8
}
]
GET /api/mortality/coop/<coop_id>
Returns all mortality reports recorded for a specific coop (e.g. GET /api/mortality/coop/K1).
GET /api/mortality/latest
Returns the most recent mortality_report.json across all coop directories, enriched with coop and covered_floors:
{
"date": "2026-06-18",
"coop": "K1",
"location": "K1",
"covered_floors": ["K1-L1", "K1-L2", "K1-L3"],
"total_mortality_count": 19,
"total_images": 1,
"_dir": "/path/to/VIDEOS/cycle7/K1/mortality",
"results": [...]
}
GET /api/mortality/history
Returns all mortality_report.json files from all registered coop directories, sorted newest first. Each report contains coop, covered_floors, total_mortality_count, and total_images.
GET /api/mortality/date/<YYYY-MM-DD>
Returns all mortality scans and total carcass counts recorded on a specific date:
{
"date": "2026-06-18",
"total_mortality_count": 35,
"total_images": 2,
"reports": [...],
"results": [...]
}
POST /api/mortality/capture
Snaps a high-resolution still frame directly from the dedicated stationary inspection camera for a specific coop, runs YOLO carcass detection, saves the image into VIDEOS/cycle7/<coop>/mortality/<date>/capture_XX_<timestamp>.jpg, and returns the detection count.
Content-Type: application/json or multipart/form-data or Query Parameters
Request Parameters:
coop(string, required): Coop identifier (e.g.K1,K2,kandang-atas).date(string, optional): Date inYYYY-MM-DDformat (defaults to today).source(string, optional): Override camera RTSP stream / device URL. If omitted, uses the configured camera inconfigs/mortality_config.yaml.conf(float, optional): Confidence threshold override.
Response (200 OK):
{
"status": "success",
"message": "Captured photo #2 from K1 camera (12 carcasses)",
"coop": "K1",
"date": "2026-08-20",
"camera_source": "rtsp://admin:admin@192.168.1.101:554/live",
"captured_file": "capture_02_20260820_103000.jpg",
"batch_count": 12,
"total_images": 2,
"total_mortality_count": 30,
"results": [...]
}
GET /api/mortality/camera/preview?coop=<coop_id>
Returns a live JPEG snapshot frame from that coop's stationary camera so workers can verify chicken positioning before capturing.
GET /api/mortality/camera/preview?coop=K1
→ Content-Type: image/jpeg
GET /api/mortality/cameras
Returns the map of all registered stationary cameras per coop.
{
"cameras": {
"K1": "rtsp://admin:admin@192.168.1.101:554/live",
"K2": "rtsp://admin:admin@192.168.1.102:554/live",
"K3": "rtsp://admin:admin@192.168.1.103:554/live",
"K4": "rtsp://admin:admin@192.168.1.104:554/live",
"K5": "rtsp://admin:admin@192.168.1.105:554/live"
}
}
POST /api/mortality/upload
Uploads one or more mortality photos from disk for a specific coop, runs YOLO carcass detection immediately, saves results into VIDEOS/cycle7/<coop>/mortality/<date>/, and returns detection results.
Content-Type: multipart/form-data
Form Fields:
coop(string, required): Coop identifier (e.g.K1,K2,kandang-atas).date(string, optional): Date inYYYY-MM-DDformat (defaults to current date).conf(float, optional): Confidence threshold override (e.g.0.7).two_pass(boolean, optional): Settruefor 2-pass refinement.images(file/binary, repeatable): 1 or more image files (.jpg,.jpeg,.png).
Response (200 OK):
{
"status": "success",
"message": "Processed 2 image(s) for K1",
"coop": "K1",
"date": "2026-08-20",
"total_images": 2,
"total_mortality_count": 29,
"results": [
{
"input_image": "batch_1.jpg",
"output_image": "output_batch_1.jpg",
"count": 19,
"output_path": "/path/to/VIDEOS/cycle7/K1/mortality/2026-08-20/output_batch_1.jpg",
"detections": [...]
}
]
}
POST /api/mortality/scan
Triggers immediate re-scanning of an existing coop directory:
Request Body (application/json):
{
"coop": "K1",
"date": "2026-08-20"
}
GET /api/mortality/image/<filename>
Serves an annotated output JPEG by filename securely. Only files beginning with output_ are accessible for security.
GET /api/mortality/image/output_scan_01.jpg
→ Content-Type: image/jpeg
Starting the Dashboard
Use the portable launcher script which auto-discovers the mortality directory:
./start_dashboard.sh
Optional environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Port to listen on |
DB_PATH |
db/chicken_counts.db |
Path to SQLite database |
MORTALITY_DIRS |
auto-detected | Comma-separated mortality dirs |
Or start manually with full control:
PYTHONPATH=src venv/bin/python dashboard.py \
--port 8080 \
--db db/chicken_counts.db \
--mortality-dir /path/to/mortality \
--mortality-dir /path/to/another/mortality
Outbound System Notifications & Webhooks
When integrating with external management systems or cloud backends, you can query status or send automated event notifications (e.g. STARTED, COMPLETED, MORTALITY_DETECTED).
1. Polling Pipeline State
External systems can poll GET http://<jetson-ip>:8080/api/status every 5–10 seconds to detect if counting or mortality runs are currently in progress or finished.
2. Sending Outbound Webhook from Shell / Batch Scripts
To notify an external endpoint (e.g. https://your-server.com/api/notify) upon run lifecycle events:
# Example: Notify external server when counting starts
curl -X POST https://your-server.com/api/notify \
-H "Content-Type: application/json" \
-d '{"event": "COUNTING_STARTED", "date": "2026-06-18", "device": "jetson-sukawarna"}'
# Example: Notify external server when counting completes with JSON payload
curl -X POST https://your-server.com/api/notify \
-H "Content-Type: application/json" \
-d @/home/asus/.Codes/VIDEOS/cycle7/kandang-atas/2026-06-18/output/counts_2026-06-18.json
# Example: Notify external server when mortality scan finishes
curl -X POST https://your-server.com/api/notify \
-H "Content-Type: application/json" \
-d @/home/asus/.Codes/VIDEOS/cycle7/kandang-atas/mortality/mortality_report.json