Spaces:
Runtime error
Runtime error
Commit History
-
Multi-car mode: detect every vehicle, identify each, group damage per car
New "Multi-car (group damage per car)" mode in the demo. Reuses existing pieces: - CarGate.detect_all(): returns every vehicle (box + mask), with a tiny-box filter and containment-aware NMS so one car isn't counted several times and a "whole-scene" super-box / sub-boxes are suppressed. - infer/multi_car.py: assign each damage to the best-overlapping vehicle (mask when available, else bbox), then per-car identify (crop -> identifier) + price via the catalog estimator; total = sum; non-overlapping damage -> unassigned. - viz.annotate_multicar(): one coloured box + label ("Car N: make/model $cost") per car, with that car's damage boxes in the same colour. Verified end-to-end on a 2-car composite (real models): the flat-tire car and the hood-scratch car are detected, grouped, and priced separately; background car shows $0. 8 new tests (grouping by mask/bbox, detect_all, containment NMS). Suite 142 -> 143; lint clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>fbfbcff
-
Fix demo error on 'no car' (and 'no image'): return 6 values to match outputs
The Gradio handler is wired to 6 outputs (annotated, id, variant_a, variant_b, variant_d, json), and the main return produces 6 — but the early "no image" and "no car" exits still returned 5 (pre-Variant-D arity). Gradio can't map 5 values to 6 outputs, so a no-car upload errored in the UI even though the gate correctly reported "no car" in the logs/JSON. Both early returns now return 6 values. Adds tests asserting every _estimate exit (no-image, no-car) returns 6 values. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>bcc0baa -
Variant D: parts-aware costing (YOLOv8-seg damage ∩ parts → catalog)
Turns the two Colab-trained YOLOv8-seg models into a better cost number, no retraining: - seg_inference.py: SegModel wrapper → per-instance masks resized to the original image so damage and part masks share a pixel grid. - parts_map.py: 23 carparts-seg classes → 15 canonical catalog parts; assign_damage_to_parts() overlaps each damage mask with the part masks (real "dent on front_bumper"), with a bbox-centre heuristic fallback so an unmatched damage still contributes a cost instead of dropping to $0. - variant_d.py: VariantDPipeline → damage∩parts → fallback_estimate (existing three-tier catalog) → calibrated INR/USD cost. - demo: "Variant D (parts-aware seg)" option with damage→part breakdown + overlay. - app.py: best-effort fetch of yoloseg.pt + parts.pt (no prod break if absent). Replaces the heuristic infer_part_from_damage with a learned part assignment for parts-keyed costing. Verified end-to-end on real CarDD images with the trained weights (tire_flat→wheel, scratch→…29bdc89
-
Add make/model confidence floor (0.30) to the deployable Phase 5 stack
Deployable-now branch: car-presence gate + ML identifier wiring (Phase 5) plus the confidence floor — all running on the existing v0.2.0 weights, no Phase 6/7 training apparatus and no Variant C. Below the 0.30 floor an out-of-distribution car's make/model guess is dropped to 'unknown' so cost falls back to body-type/segment pricing instead of mislabelling. Exposed as a demo slider. Suite 123 -> 125 green; deployable on v0.2.0 weights as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>a2fbdab -
Phase 5: activate make/model identification + car-presence gate
Wires up the make/model identifier that was trained in Phase 1.5 but never called, and adds a "is there a car, and where?" gate in front of it — both on the ResNet-50 family (the gate is a COCO Mask R-CNN; masks ignored, boxes used). New modules (ccdp.identification): - car_gate.py — CarGate (COCO maskrcnn_resnet50_fpn) + pure decide()/pad_box; returns the highest-scoring car/truck/bus box or has_car=False. - ml_identifier.py — MLIdentifier inference wrapper around the production identifier checkpoint; maps class idx -> make/model/year via a bundled Stanford Cars class-name resource (no dataset needed). - auto_identify.py — orchestrates gate -> crop -> identifier into one call, returning an IdentificationResult the cost pipelines accept. - data/stanford_cars_classes.json — bundled 196-class name map. Integrations: - car_identifier.identify() gains an opt-in ML stage (use_ml=, injectable), ordered af…c9f7f1e
-
Add Stanford Cars as 'no damage' negatives for the classifier
Root cause of the false-positive failure on undamaged cars: CarDD contains only damaged-car photos, so the classifier never sees a 'no damage' example and has no way to predict the empty label set. This commit teaches the trainer to mix Stanford Cars images (which are by-and-large undamaged) into the dataset with all-zero damage labels — Option A from the data-strategy discussion. What's new - iter_negatives() in data/loaders.py: scans Stanford Cars image directory, yields Records with damage_types=[] and dataset tag 'stanford_cars_negative'. Returns nothing (no error) if the directory is missing, so existing CarDD-only flows are unaffected. - mix_negatives() in damage_dataset.py: deterministic, ratio-controlled mixer. Caps at available negatives rather than oversampling with replacement (duplicating identical photos would just teach the model to memorise them). Doesn't mutate inputs. - TrainConfig.negative_ratio + --negative-ratio CLI flag. Default 0.0 preserves legacy …5da26fe -
Honor classifier threshold + show explicit no-detection feedback
Three real issues, surfaced when testing on real-world Indian street photos of a Hyundai Verna: 1. VariantAPipeline.predict(threshold=...) declared the parameter but _forward hardcoded >= 0.5, silently ignoring the override. Now _forward takes threshold as an arg and the parameter actually does what its name promises. New regression tests in tests/test_variant_a_threshold.py pin the behavior. 2. When the YOLOv8 detector found zero boxes, the demo silently returned the un-annotated image — visually indistinguishable from a draw failure. annotate_no_detections now stamps a translucent banner so the user can tell the detector ran and produced no result. 3. The Estimate tab gained a Sensitivity accordion with two sliders (classifier threshold default 0.6 raised from 0.5, detector conf default 0.20 lowered from 0.25) and the Variant B output now reports the box count. The About tab spells out the four real limitations: no 'undamaged' class in CarDD, domai…f2dc9af -
Add annotated image output + 7 beginner notebooks
ccdp.viz.overlay renders YOLO detector boxes onto a PIL image (per-class color palette, Pillow-only, 8 unit tests). Wired into: - Gradio demo: new Image component shows boxes alongside the JSON panel - FastAPI: new /estimate/annotated endpoint returns PNG bytes Beginner notebook series under notebooks/ (00-06) walks through every model from first principles: convolution math, residual connections, two-stage transfer learning, IoU/NMS/mAP, gradient boosting, the catalog calibration trick, end-to-end inference. Each notebook has a Colab bootstrap, demo-scale runnable training cells, and an opt-in full-training cell. Generated via notebooks/_build_notebooks.py (committed so future tweaks edit Python, not ipynb JSON). Tests: 84/84 passing (76 existing + 8 new). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>5b27429
-
checkpoint-3: comparison report, FastAPI, Gradio, HF Space deploy
Phase 3 brings the system to humans. Three deliverables, all reading from the existing v0.1.0 production registry — no new training, no model changes. ## New modules ccdp/preprocess (Stage A: deterministic downscale + quality report). - `normalize_for_inference` LANCZOS-resizes long edges to 1600 px max. - `quality_report` returns sharpness (Laplacian variance), brightness, contrast, low-resolution / low-light / blurry booleans. - Stage B (super-resolution) explicitly deferred to a later checkpoint. ccdp/eval — Variant A vs B head-to-head evaluator. - `metrics.py`: pure per-class P/R/F1 + regression metrics (no sklearn). - `comparison.py`: `build_comparison()` runs both pipelines on the seed=42 test split, returns a `Comparison` dataclass. - `report.py`: Jinja2 -> HTML always; WeasyPrint -> PDF when installed. - `reports/templates/report.html.j2`: A4-formatted report with headline table, per-variant detail, tier/latency, top failures, disclosures. ccdp/api…323fee6 -
Refactor: extract utils + base pipeline, expand docs
Code changes (KISS + single-responsibility): - src/ccdp/utils/{device,transforms}.py: deduplicate `pick_device`, `seed_everything`, IMAGENET stats, train/eval transforms (~120 lines previously copy/pasted across 5 files). - src/ccdp/infer/base.py: new `BaseVariantPipeline` holding the shared downstream — XGBoost loading, calibration, FX conversion, provenance construction. `VariantAPipeline` and `VariantBPipeline` now subclass it and only implement their vision-specific forward pass. - Wire trainers (`train_car_identifier`, `train_damage_classifier`) and the classifier feature extractor to import from `ccdp.utils` instead of holding private copies of the same helpers. - Fix latent bugs surfaced by the smoke test: * `_can_use_xgb` now also checks that the bundle actually loaded. * `BaseVariantPipeline` resolves the production symlink before deriving the bundle dir; previously it pointed at `checkpoints/production/` where `bundle.json` does not live. Do…76955d6 -
Initial commit: end-to-end car damage + repair-cost predictor
Variants A and B trained end-to-end on CarDD + comprehensive + iaai + Stanford Cars datasets, glued together by a versioned parts-cost catalog and a three-tier (exact / nearest-class / catalog-only) cost estimator with full provenance on every prediction. Production metrics (v0.1.0): - Identifier (ResNet50 + RandAugment + MixUp + CutMix): val acc 77.0% - Damage classifier (ResNet50, 6 damage types): val macro-F1 0.834 - YOLOv8n detector: mAP50 0.687, mAP50-95 0.540 - XGBoost(A) (image features only): val R² 0.630, test R² 0.642 - XGBoost(B) (+ bbox features): val R² 0.716, test R² 0.736 64 unit tests pass. Honest disclosures of synthetic-cost limitation in PLAN.md and progress/*.md.4ce7a7e