2026-07-29 14:57:14 +07:00
2026-07-29 14:57:14 +07:00
2026-07-29 14:57:14 +07:00
2026-07-29 14:57:14 +07:00
2026-07-29 14:57:14 +07:00
2026-07-29 14:57:14 +07:00
2026-07-29 14:55:55 +07:00

Weight Estimator

Offline experiment pipeline for estimating daily average broiler weight per cage from cam2/cam3 top-down video and physical scale readings.

Install

cd "/media/asus/One Touch/LABS/weight-estimator/try-weight-chicken"
python -m pip install -e .

# Chicken counter (YOLO + BoT-SORT) — required for extract / QA / predict:
python -m pip install -e "/media/asus/One Touch/LABS/chicken-counting/chicken-sukawarna-existing"
# Optional Jetson extras:
python -m pip install -e ".[jetson]"

Venv used on this machine:

source /media/asus/DATA/wchicken/bin/activate
cd "/media/asus/One Touch/LABS/weight-estimator/try-weight-chicken"

All commands below assume that venv is active and you are in try-weight-chicken. Config default: configs/weight_cc2_cc3.yaml.

Command guide

CLI map

Command Purpose GPU?
audit CSV ↔ video path check no
calibration-audit Save calibration frames no
validate-calibration Homography error check no
extract Unique-track feature extract → detections.parquet yes
aggregate Detections → cage_day_features.parquet no
train Fit vision-primary model → model_bundle.joblib no
evaluate Late-holdout GO/NO_GO + plots no
filter-overlay JPG overlays or single QA MP4 yes
qa-review-videos Fixed 5-date × CC2/CC3 review MP4s yes
predict One-day average weight from video(s) yes
compare-filters Strict vs legacy filter report no
run-all Full pipeline (optional --synthetic) maybe
python -m weight_estimator.cli --help
python -m weight_estimator.cli <command> --help
# If entrypoint installed:
weight-estimator <command> --help

1. Audit / calibration

python -m weight_estimator.cli audit --config configs/weight_cc2_cc3.yaml
python -m weight_estimator.cli calibration-audit --config configs/weight_cc2_cc3.yaml
python -m weight_estimator.cli validate-calibration --config configs/weight_cc2_cc3.yaml

2. Full extract (unique chicken / track along video)

Extract runs BoT-SORT every frame, keeps one best-confidence observation per track_id, target 400 unique tracks per camera/day, single forward pass, stops on target or optical-flow reverse.

mkdir -p logs
# Backup previous detections
cp -a output/detections.parquet "output/detections_pre_extract_$(date +%Y%m%d_%H%M).parquet"

tmux kill-session -t weight-extract 2>/dev/null || true
STAMP=$(date +%Y%m%d_%H%M)
tmux new-session -d -s weight-extract \
  "cd '/media/asus/One Touch/LABS/weight-estimator/try-weight-chicken' && \
   source /media/asus/DATA/wchicken/bin/activate && \
   PYTHONUNBUFFERED=1 python -m weight_estimator.cli extract \
     --config configs/weight_cc2_cc3.yaml \
     2>&1 | tee logs/extract_${STAMP}.log; \
   echo EXIT_CODE=\$? | tee -a logs/extract_${STAMP}.log; exec bash"

tail -f logs/extract_${STAMP}.log
# Done when you see: Extracted N detections -> .../detections.parquet  and EXIT_CODE=0

Useful log lines:

  • unique_track_mode track_every_n=1 … keep_best=True multipass=False target=400
  • reverse stop at frame=N
  • done valid=… unique=… replaced=… inferred=…

Key YAML knobs (sampling: in configs/weight_cc2_cc3.yaml):

Knob Typical Meaning
track_every_n_frames 1 YOLO/track every N frames
per_track_cap 1 Max stored obs per track_id
keep_best_per_track true Keep highest-confidence obs
target_detections / late 400 Unique-track target
allow_stride_multipass false No video re-open (keeps IDs)
stop_on_backward true Optical-flow reverse end

3. Aggregate → train → evaluate (after extract)

python -m weight_estimator.cli aggregate --config configs/weight_cc2_cc3.yaml
python -m weight_estimator.cli train --config configs/weight_cc2_cc3.yaml --model ridge
python -m weight_estimator.cli evaluate --config configs/weight_cc2_cc3.yaml
  • GO if late-holdout MAPE ≤ 15% vs consensus (berat_aktual_g).
  • Report: output/validation_report.json
  • Bundle: output/model_bundle.joblib

Re-apply size MAD only (no re-extract):

python -m weight_estimator.cli aggregate --config configs/weight_cc2_cc3.yaml --refilter-size

4. QA review videos (visual check of valid / unique OK)

Writes overlay MP4s for fixed dates: cycle days 4, 15, 21, 30, 47 × CC2 + CC3 (10 files).

Behavior matches extract tracking:

  • Track every frame
  • HUD: unique_OK=N/400 (cumulative unique accepted track_ids) + frame_OK / rej
  • Boxes: OK#track_id
  • Stops at target 400 (same as extract) or reverse-stop if target not reached
  • Encodes every --frame-stride frames (default 10)
mkdir -p logs output/qa_review_videos
STAMP=$(date +%Y%m%d_%H%M)
tmux kill-session -t weight-qa-videos 2>/dev/null || true
tmux new-session -d -s weight-qa-videos \
  "cd '/media/asus/One Touch/LABS/weight-estimator/try-weight-chicken' && \
   source /media/asus/DATA/wchicken/bin/activate && \
   PYTHONUNBUFFERED=1 python -m weight_estimator.cli qa-review-videos \
     --config configs/weight_cc2_cc3.yaml \
     --output-dir output/qa_review_videos \
     --frame-stride 10 \
     2>&1 | tee logs/qa_review_${STAMP}.log; \
   echo EXIT_CODE=\$? | tee -a logs/qa_review_${STAMP}.log; exec bash"

tail -f logs/qa_review_${STAMP}.log

Outputs:

  • output/qa_review_videos/{band}_day{N}_{date}_{CCx}.mp4
  • output/qa_review_videos/qa_review_summary.json

Single-video QA MP4 (same unique-OK / target logic):

python -m weight_estimator.cli filter-overlay \
  --config configs/weight_cc2_cc3.yaml \
  --video "/media/asus/One Touch/VIDEOS/cycle7/kandang-atas/2026-05-26/kandang_1_camera_2_2026-05-26_120057.mp4" \
  --camera-id CC2 \
  --cycle-day 4 \
  --frame-stride 10 \
  --write-video output/qa_review_videos/manual_day4_CC2.mp4

JPG frame overlays only (legacy tuning):

python -m weight_estimator.cli filter-overlay \
  --config configs/weight_cc2_cc3.yaml \
  --video /path/to/video.mp4 \
  --camera-id CC2 \
  --cycle-day 15 \
  --frame-stride 30 \
  --max-frames 50 \
  --output-dir output/filter_overlays

5. Predict (daily use)

Formula: DOC + max(0, vision_gain(features)) (vision-primary). DOC is required.

python -m weight_estimator.cli predict \
  --config configs/weight_cc2_cc3.yaml \
  --doc-weight 32 \
  --doc-date 2026-05-22 \
  --date 2026-06-15 \
  --model output/model_bundle.joblib \
  --cc2 /path/to/kandang_1_camera_2_XXXX.mp4 \
  --cc3 /path/to/kandang_1_camera_3_XXXX.mp4 \
  --output-dir output/predict_2026-06-15

Writes prediction.json under --output-dir.

6. tmux helpers

tmux ls
tmux attach -t weight-extract      # or weight-qa-videos
# detach: Ctrl+b then d
tmux kill-session -t weight-extract
tmux kill-session -t weight-qa-videos
nvidia-smi
pgrep -af 'weight_estimator.cli'

7. Comparison Excel (Manual / IoT / Vision)

After evaluate, refresh comparison under output/comparison_consensus/ (e.g. Perbandingan_Manual_IoT_Vision.xlsx: columns IoT → Manual → Vision, akurasi = 100% − |error|, green/yellow/red cell background).

Typical path after a new extract:

python -m weight_estimator.cli aggregate --config configs/weight_cc2_cc3.yaml
python -m weight_estimator.cli train --config configs/weight_cc2_cc3.yaml --model ridge
python -m weight_estimator.cli evaluate --config configs/weight_cc2_cc3.yaml
# then regenerate Perbandingan Excel from prediction_vs_manual_iot.csv (agent/script)

Quick start (synthetic demo)

When videos are not available locally:

python -m weight_estimator.cli run-all --config configs/weight_cc2_cc3.yaml --synthetic

Outputs land in output/:

  • audit_report.json
  • calibration_audit.json
  • detections.parquet
  • cage_day_features.parquet
  • model_bundle.joblib
  • validation_report.json
  • bland_altman_*.png

Configuration

Labels (consensus Manual+IoT): data/historical_weights_manual_iot_review.csv

Quality filters

Detections must pass all frame tiers before entering the weight sample:

  1. Class gate — class 0 only; reject not-chicken / half-chicken
  2. Containment — ROI overlap + sampling corridors
  3. Isolation — max IoU and min centroid distance
  4. Shape — confidence, aspect ratio, area vs frame median
  5. Frame gate — clean_ratio and ≥ 1 accepted bird
  6. Size distribution (second filter) — per camera-day MAD z-score on area_cm2

Rejection breakdown: output/filter_report.json.

YOLO vs post-filter: YOLO conf / iou live in cycle7_batch.yaml. Weight quality.conf_min must stay aligned with batch early_cycle. Re-extract after changing either.

python -m weight_estimator.cli compare-filters --config configs/weight_cc2_cc3.yaml

Design notes

  • DOC-essential: labels require Cycle ID, DOC Date, DOC Weight (g). Age = (Tanggal − DOC Date).days.
  • Vision-primary: predicted_avg_g = DOC + max(0, vision_gain(features)). Age-only / Gompertz is not used for GO/NO_GO or comparison reports.
  • GO/NO_GO: late-cycle holdout MAPE ≤ 15% vs consensus (berat_aktual_g).
  • Unit of analysis: one row per (cage, date); detections are aggregated, not individually labeled.
  • Unique-track sampling: track every frame; per_track_cap: 1 + keep_best_per_track; target 400 unique tracks; stop_on_backward; no multipass re-open. Diversity should be ~1.0. During extract, extract_overlay_every_n_frames: 1000 saves frame-tier overlays to output/extract_filter_overlays/{date}_{camera}/.
  • Size distribution: MAD z-score on area_cm2. Band z_max: early 3.0 (≤17), mid 2.5 (18–24), late 2.0 (≥25). Tune with aggregate --refilter-size when only size gates change.
  • Early / mid cycle: early_cycle.day_max 17, mid_cycle 18–24, then late/strict. Batch early YOLO: conf 0.12.
  • Camera fusion: gate cams below fuse_min_detections, weight by detection_count; else best cam. Re-aggregate after fusion changes (no re-extract).
  • Segmentation: YOLO-seg via detection.model_path; mask geometry preferred with bbox fallback. Production features: features.preset: mask_max.
  • Train ages: 3–35. Evaluate late holdout 29–35 (fit on 3–28).
  • Excluded days: exclude_cycle_days (cycle7: 0–2). DOC weight examples: 32 g.
S
Description
No description provided
Readme
574 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 100%