import os, io, base64 from typing import List, Dict, Any import gradio as gr from PIL import Image from car_core.specs_model_space import build_label_space from car_core.models_zeroshot import ModelIDZeroShot from car_core.color_detect import dominant_color from car_core.issues import detect_issues from car_core.pricing import price_issues, load_regions from car_core.exporter import export_pdf, export_json LABEL_SPACE = build_label_space() MODEL = ModelIDZeroShot(LABEL_SPACE) REGIONS = load_regions() def _to_pil(obj): if isinstance(obj, dict) and "image" in obj: # Gradio File with base64 'image' key return Image.open(io.BytesIO(base64.b64decode(obj["image"].split(",")[-1]))) if isinstance(obj, str): return Image.open(obj) if hasattr(obj, "read"): return Image.open(obj) return obj def analyze(images: list, region: str): if not images: raise gr.Error("Upload at least one car image.") imgs = [] for it in images: try: imgs.append(_to_pil(it)) except Exception: pass if not imgs: raise gr.Error("Failed to decode images. Use JPG/PNG.") # Model decision: pick the most frequent top label across images votes = {} for im in imgs: lbl = MODEL.top_label(im) votes[lbl] = votes.get(lbl, 0) + 1 model_final = sorted(votes.items(), key=lambda kv: kv[1], reverse=True)[0][0] # Color decision: take the most frequent named color across images color_votes = {} for im in imgs: c = dominant_color(im)["name"] color_votes[c] = color_votes.get(c, 0) + 1 color_name = sorted(color_votes.items(), key=lambda kv: kv[1], reverse=True)[0][0] color_any = None # Recompute once to get rgb/hex for the chosen name for im in imgs: d = dominant_color(im) if d["name"] == color_name: color_any = d; break color_final = color_any or {"name": color_name, "rgb": (0,0,0), "hex": "#000000"} # Issues (deterministic) issues = detect_issues(imgs) # Pricing pricing = price_issues(issues, region_code=region) payload = { "vehicle": {"model": model_final, "color": color_final}, "region": region, "issues": issues, "pricing": pricing } os.makedirs("exports", exist_ok=True) pdf_path = "exports/report.pdf" json_path = "exports/report.json" export_pdf(payload, pdf_path) export_json(payload, json_path) def to_dl(path): with open(path, "rb") as f: return (os.path.basename(path), f.read()) # Deterministic, actionable output only result = { "vehicle": payload["vehicle"], "region": pricing["region"], "currency": pricing["currency"], "issues_with_solutions": [ { "issue": it["issue"], "solution": it["solution"], "labor_hours": it["labor_hours"], "labor_cost": it["labor_cost"], "parts_cost": it["parts_cost"], "line_total": it["line_total"] } for it in pricing["items"] ], "totals": { "subtotal": pricing["subtotal"], "tax": pricing["tax"], "grand_total": pricing["grand_total"] } } return result, to_dl(pdf_path), to_dl(json_path) with gr.Blocks(fill_height=True) as demo: gr.Markdown("## 🛠️ Car Analysis Advisor — multi‑image model/color/issue detection with exact pricing") with gr.Row(): with gr.Column(scale=1): imgs = gr.File(label="Upload car image(s)", file_count="multiple", file_types=["image"]) region = gr.Dropdown(choices=list(REGIONS["regions"].keys()), value=REGIONS.get("default_region","IN-HYD"), label="Region (pricing)") run = gr.Button("Analyze", variant="primary") with gr.Column(scale=1): out = gr.JSON(label="Actionable results") pdf = gr.File(label="Download PDF") jj = gr.File(label="Download JSON") run.click(analyze, inputs=[imgs, region], outputs=[out, pdf, jj]) if __name__ == "__main__": demo.launch()