ganemmah commited on
Commit
f6e4696
·
verified ·
1 Parent(s): 3a4e981

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -31,10 +31,11 @@ The trained numeric model uses an AutoScout24 dataset with EUR prices. For the S
31
 
32
  1. Enter vehicle details such as make, model, production year, mileage, fuel type, transmission, and power.
33
  2. Upload front, side, rear, and interior vehicle images.
34
- 3. Receive a base market price prediction.
35
- 4. Receive detected damage categories, image evidence, image-quality warnings, and a transparent damage adjustment.
36
- 5. Receive a recommended listing price range.
37
- 6. Generate an honest used car advertisement text.
 
38
 
39
  ## Repository Structure
40
 
@@ -58,6 +59,7 @@ Current implemented parts:
58
  - price preprocessing and feature engineering,
59
  - numeric price model training and evaluation,
60
  - OpenAI Vision damage analysis with manual fallback,
 
61
  - integrated Gradio app for price recommendation, damage adjustment, and listing generation.
62
 
63
  ## Local Commands
 
31
 
32
  1. Enter vehicle details such as make, model, production year, mileage, fuel type, transmission, and power.
33
  2. Upload front, side, rear, and interior vehicle images.
34
+ 3. Optionally use AI prefill to suggest make, model, year, and body type from the uploaded images.
35
+ 4. Receive a base market price prediction.
36
+ 5. Receive detected damage categories, image evidence, image-quality warnings, and a transparent damage adjustment.
37
+ 6. Receive a recommended listing price range.
38
+ 7. Generate an honest used car advertisement text.
39
 
40
  ## Repository Structure
41
 
 
59
  - price preprocessing and feature engineering,
60
  - numeric price model training and evaluation,
61
  - OpenAI Vision damage analysis with manual fallback,
62
+ - OpenAI Vision vehicle-field prefill with manual verification,
63
  - integrated Gradio app for price recommendation, damage adjustment, and listing generation.
64
 
65
  ## Local Commands
app/__pycache__/app.cpython-310.pyc CHANGED
Binary files a/app/__pycache__/app.cpython-310.pyc and b/app/__pycache__/app.cpython-310.pyc differ
 
app/__pycache__/integration.cpython-310.pyc CHANGED
Binary files a/app/__pycache__/integration.cpython-310.pyc and b/app/__pycache__/integration.cpython-310.pyc differ
 
app/__pycache__/vehicle_recognition.cpython-310.pyc ADDED
Binary file (5.47 kB). View file
 
app/app.py CHANGED
@@ -12,6 +12,7 @@ from app.integration import IntegratedAnalysis, run_integrated_analysis
12
  from app.nlp_generator import PromptStrategy
13
  from app.price_model import PricePrediction, VehicleFeatures
14
  from app.utils import EUR_TO_CHF_RATE, SWISS_MARKET_FACTOR, eur_to_chf, format_chf
 
15
 
16
 
17
  DAMAGE_CHOICES = ["scratch", "dent", "crack", "lamp broken", "glass shatter", "tire flat"]
@@ -35,6 +36,117 @@ TRANSMISSION_CHOICES = ["Automatic", "Manual", "Semi-automatic", "Unknown"]
35
  BODY_CHOICES = ["SUV", "Sedan", "Hatchback", "Station Wagon", "Coupe", "Convertible", "Van", "Other"]
36
 
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  def fallback_price_prediction(features: VehicleFeatures) -> PricePrediction:
39
  """Provide a conservative fallback if the trained model artifact is missing."""
40
  age = max(0, 2026 - features.production_year)
@@ -365,6 +477,23 @@ CUSTOM_CSS = """
365
  margin: 6px 0 0 18px;
366
  padding: 0;
367
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  .flow-line {
369
  display: grid;
370
  grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -489,6 +618,10 @@ def build_demo() -> gr.Blocks:
489
  label="Manual fallback damage labels",
490
  )
491
  gr.HTML(damage_weights_html())
 
 
 
 
492
 
493
  prompt_strategy = gr.Radio(
494
  ["Sales-optimized but honest", "Neutral factual"],
@@ -531,6 +664,22 @@ def build_demo() -> gr.Blocks:
531
  prompt_strategy,
532
  ]
533
  outputs = [metrics, flow, vision_evidence, listing, explanation, damage_json, prompt_trace]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  run_button.click(predict_listing, inputs=inputs, outputs=outputs, api_name="predict_listing")
535
  return demo
536
 
 
12
  from app.nlp_generator import PromptStrategy
13
  from app.price_model import PricePrediction, VehicleFeatures
14
  from app.utils import EUR_TO_CHF_RATE, SWISS_MARKET_FACTOR, eur_to_chf, format_chf
15
+ from app.vehicle_recognition import VehicleRecognition, recognize_vehicle_from_images
16
 
17
 
18
  DAMAGE_CHOICES = ["scratch", "dent", "crack", "lamp broken", "glass shatter", "tire flat"]
 
36
  BODY_CHOICES = ["SUV", "Sedan", "Hatchback", "Station Wagon", "Coupe", "Convertible", "Van", "Other"]
37
 
38
 
39
+ def map_make_to_ui(make: str | None) -> tuple[str, str]:
40
+ """Map recognized make to dropdown/custom make fields."""
41
+ if not make:
42
+ return "Other", ""
43
+ for choice in MAKE_CHOICES:
44
+ if choice.lower() == make.lower():
45
+ return choice, ""
46
+ return "Other", make
47
+
48
+
49
+ def map_body_type_to_ui(body_type: str | None) -> str:
50
+ """Map recognized body type to dropdown value."""
51
+ if not body_type:
52
+ return "Other"
53
+ normalized = body_type.strip().lower()
54
+ aliases = {
55
+ "estate": "Station Wagon",
56
+ "wagon": "Station Wagon",
57
+ "combi": "Station Wagon",
58
+ "saloon": "Sedan",
59
+ "convertible": "Convertible",
60
+ "cabriolet": "Convertible",
61
+ "hatch": "Hatchback",
62
+ }
63
+ if normalized in aliases:
64
+ return aliases[normalized]
65
+ for choice in BODY_CHOICES:
66
+ if choice.lower() == normalized:
67
+ return choice
68
+ return "Other"
69
+
70
+
71
+ def recognition_html(recognition: VehicleRecognition | None, error: str | None = None) -> str:
72
+ """Render vehicle-recognition status for the app."""
73
+ if error:
74
+ return f"<div class='evidence-warning'><b>AI prefill failed</b><p>{error}</p></div>"
75
+ if recognition is None:
76
+ return "<div class='empty-state'>Upload a vehicle image and click AI prefill to suggest vehicle fields.</div>"
77
+
78
+ warnings = "".join(f"<li>{warning}</li>" for warning in recognition.warnings)
79
+ warning_block = f"<ul>{warnings}</ul>" if warnings else "<span>No recognition warnings.</span>"
80
+ fields = [
81
+ f"Make: {recognition.make or 'unknown'}",
82
+ f"Model: {recognition.model or 'unknown'}",
83
+ f"Year: {recognition.production_year or 'unknown'}",
84
+ f"Body: {recognition.body_type or 'unknown'}",
85
+ ]
86
+ return f"""
87
+ <div class="prefill-panel">
88
+ <div class="result-label">AI vehicle prefill</div>
89
+ <p><b>{' | '.join(fields)}</b></p>
90
+ <p>Confidence: {recognition.confidence:.2f}</p>
91
+ <p>{recognition.evidence or 'No detailed recognition evidence returned.'}</p>
92
+ <div class="result-sub">Please verify before valuation.</div>
93
+ <div class="prefill-warnings">{warning_block}</div>
94
+ </div>
95
+ """.strip()
96
+
97
+
98
+ def prefill_vehicle_fields(
99
+ front_image: str | None,
100
+ side_image: str | None,
101
+ rear_image: str | None,
102
+ interior_image: str | None,
103
+ current_make: str,
104
+ current_custom_make: str,
105
+ current_model: str,
106
+ current_year: float,
107
+ current_body_type: str,
108
+ ) -> tuple[str, str, str, int, str, str]:
109
+ """Recognize vehicle identity and update form fields."""
110
+ image_paths = [
111
+ ("front", front_image),
112
+ ("side", side_image),
113
+ ("rear", rear_image),
114
+ ("interior", interior_image),
115
+ ]
116
+ image_paths = [(view, path) for view, path in image_paths if path]
117
+ try:
118
+ recognition = recognize_vehicle_from_images(image_paths)
119
+ except Exception as exc: # noqa: BLE001 - user-facing status
120
+ return (
121
+ current_make,
122
+ current_custom_make,
123
+ current_model,
124
+ int(current_year),
125
+ current_body_type,
126
+ recognition_html(None, str(exc)),
127
+ )
128
+
129
+ make_value, custom_make = map_make_to_ui(recognition.make)
130
+ if not recognition.make:
131
+ make_value = current_make
132
+ custom_make = current_custom_make
133
+ model_value = recognition.model or current_model
134
+ year_value = recognition.production_year or int(current_year)
135
+ body_value = (
136
+ map_body_type_to_ui(recognition.body_type)
137
+ if recognition.body_type
138
+ else current_body_type
139
+ )
140
+ return (
141
+ make_value,
142
+ custom_make,
143
+ model_value,
144
+ year_value,
145
+ body_value,
146
+ recognition_html(recognition),
147
+ )
148
+
149
+
150
  def fallback_price_prediction(features: VehicleFeatures) -> PricePrediction:
151
  """Provide a conservative fallback if the trained model artifact is missing."""
152
  age = max(0, 2026 - features.production_year)
 
477
  margin: 6px 0 0 18px;
478
  padding: 0;
479
  }
480
+ .prefill-panel {
481
+ border: 1px solid #c7d2fe;
482
+ border-radius: 8px;
483
+ background: #f8fafc;
484
+ padding: 12px;
485
+ margin: 10px 0 0;
486
+ }
487
+ .prefill-panel p {
488
+ margin: 4px 0;
489
+ color: #17202a;
490
+ }
491
+ .prefill-warnings ul {
492
+ margin: 8px 0 0 18px;
493
+ padding: 0;
494
+ color: #475569;
495
+ font-size: 13px;
496
+ }
497
  .flow-line {
498
  display: grid;
499
  grid-template-columns: repeat(3, minmax(0, 1fr));
 
618
  label="Manual fallback damage labels",
619
  )
620
  gr.HTML(damage_weights_html())
621
+ prefill_button = gr.Button("AI prefill vehicle fields", variant="secondary")
622
+ prefill_status = gr.HTML(
623
+ "<div class='empty-state'>Upload at least one vehicle image to suggest make, model, year, and body type.</div>"
624
+ )
625
 
626
  prompt_strategy = gr.Radio(
627
  ["Sales-optimized but honest", "Neutral factual"],
 
664
  prompt_strategy,
665
  ]
666
  outputs = [metrics, flow, vision_evidence, listing, explanation, damage_json, prompt_trace]
667
+ prefill_button.click(
668
+ prefill_vehicle_fields,
669
+ inputs=[
670
+ front_image,
671
+ side_image,
672
+ rear_image,
673
+ interior_image,
674
+ make,
675
+ custom_make,
676
+ model,
677
+ production_year,
678
+ body_type,
679
+ ],
680
+ outputs=[make, custom_make, model, production_year, body_type, prefill_status],
681
+ api_name="prefill_vehicle_fields",
682
+ )
683
  run_button.click(predict_listing, inputs=inputs, outputs=outputs, api_name="predict_listing")
684
  return demo
685
 
app/vehicle_recognition.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Vehicle identification from uploaded images."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import json
7
+ import mimetypes
8
+ import os
9
+ import re
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from app.damage_model import DEFAULT_OPENAI_VISION_MODEL
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class VehicleRecognition:
19
+ """Structured vehicle identification suggestion from image evidence."""
20
+
21
+ make: str | None = None
22
+ model: str | None = None
23
+ production_year: int | None = None
24
+ body_type: str | None = None
25
+ confidence: float = 0.0
26
+ evidence: str = ""
27
+ warnings: list[str] = field(default_factory=list)
28
+
29
+
30
+ def _image_to_data_url(image_path: Path) -> str:
31
+ """Encode an image as a data URL for OpenAI vision input."""
32
+ mime_type = mimetypes.guess_type(str(image_path))[0] or "image/jpeg"
33
+ encoded = base64.b64encode(image_path.read_bytes()).decode("ascii")
34
+ return f"data:{mime_type};base64,{encoded}"
35
+
36
+
37
+ def _extract_json_object(text: str) -> dict[str, Any]:
38
+ """Parse a JSON object from model output, even if wrapped in text."""
39
+ try:
40
+ parsed = json.loads(text)
41
+ if isinstance(parsed, dict):
42
+ return parsed
43
+ except json.JSONDecodeError:
44
+ pass
45
+
46
+ match = re.search(r"\{.*\}", text, flags=re.DOTALL)
47
+ if not match:
48
+ raise ValueError("Vehicle recognition response did not contain a JSON object.")
49
+ parsed = json.loads(match.group(0))
50
+ if not isinstance(parsed, dict):
51
+ raise ValueError("Vehicle recognition response JSON was not an object.")
52
+ return parsed
53
+
54
+
55
+ def _clean_optional_text(value: Any) -> str | None:
56
+ """Normalize optional text values from a model response."""
57
+ if value is None:
58
+ return None
59
+ text = str(value).strip()
60
+ if not text or text.lower() in {"unknown", "null", "none", "not visible"}:
61
+ return None
62
+ return text
63
+
64
+
65
+ def _parse_year(value: Any) -> int | None:
66
+ """Parse a plausible production-year estimate."""
67
+ if value is None:
68
+ return None
69
+ match = re.search(r"(19|20)\d{2}", str(value))
70
+ if not match:
71
+ return None
72
+ year = int(match.group(0))
73
+ if 1990 <= year <= 2026:
74
+ return year
75
+ return None
76
+
77
+
78
+ def recognize_vehicle_from_images(
79
+ image_paths: list[tuple[str, str | Path]],
80
+ model: str | None = None,
81
+ ) -> VehicleRecognition:
82
+ """Suggest make, model, approximate year, and body type from vehicle images."""
83
+ existing_paths = [
84
+ (view, Path(path))
85
+ for view, path in image_paths
86
+ if path and Path(path).exists()
87
+ ]
88
+ if not existing_paths:
89
+ raise FileNotFoundError("Upload at least one vehicle image before using AI prefill.")
90
+
91
+ api_key = os.getenv("OPENAI_API_KEY")
92
+ if not api_key:
93
+ raise RuntimeError("OPENAI_API_KEY is not configured.")
94
+
95
+ from openai import OpenAI
96
+
97
+ model_name = model or os.getenv("OPENAI_VISION_MODEL", DEFAULT_OPENAI_VISION_MODEL)
98
+ prompt = """
99
+ You are a careful used-car identification assistant. Inspect the uploaded car images and infer only visible or strongly likely vehicle facts.
100
+ Return only valid JSON with this schema:
101
+ {
102
+ "make": "manufacturer or null",
103
+ "model": "model name or null",
104
+ "production_year": 2018,
105
+ "body_type": "SUV|Sedan|Hatchback|Station Wagon|Coupe|Convertible|Van|Other|null",
106
+ "confidence": 0.0,
107
+ "evidence": "short visual evidence, such as badges, grille, shape, lights, or interior clues",
108
+ "warnings": ["uncertainties such as no badge visible, partial image, similar generations, model year approximate"]
109
+ }
110
+
111
+ Rules:
112
+ - Be conservative. If make/model/year is uncertain, use null or add a warning.
113
+ - Estimate production_year as the most likely visual model year, not registration year.
114
+ - Do not infer mileage, service history, transmission, power, accident history, or fuel type from the image.
115
+ - Prefer broad body_type over exact trim.
116
+ """.strip()
117
+
118
+ content: list[dict[str, str]] = [{"type": "input_text", "text": prompt}]
119
+ for view, path in existing_paths:
120
+ content.append({"type": "input_text", "text": f"View: {view}"})
121
+ content.append({"type": "input_image", "image_url": _image_to_data_url(path)})
122
+
123
+ client = OpenAI(api_key=api_key)
124
+ response = client.responses.create(
125
+ model=model_name,
126
+ input=[
127
+ {
128
+ "role": "user",
129
+ "content": content,
130
+ }
131
+ ],
132
+ )
133
+ payload = _extract_json_object(response.output_text)
134
+ warnings = payload.get("warnings", [])
135
+ if isinstance(warnings, str):
136
+ warnings = [warnings]
137
+
138
+ confidence = float(payload.get("confidence", 0.0) or 0.0)
139
+ confidence = max(0.0, min(confidence, 1.0))
140
+ if confidence < 0.55:
141
+ warnings.append("Vehicle recognition confidence is low; please verify the fields manually.")
142
+
143
+ return VehicleRecognition(
144
+ make=_clean_optional_text(payload.get("make")),
145
+ model=_clean_optional_text(payload.get("model")),
146
+ production_year=_parse_year(payload.get("production_year")),
147
+ body_type=_clean_optional_text(payload.get("body_type")),
148
+ confidence=confidence,
149
+ evidence=str(payload.get("evidence", "")).strip(),
150
+ warnings=[str(item) for item in warnings if str(item).strip()],
151
+ )
documentation.md CHANGED
@@ -35,9 +35,10 @@ The three AI blocks are integrated into one decision flow rather than executed i
35
  Vehicle attributes -> Numeric ML model -> Base market price
36
  Vehicle images -> Computer vision model -> Damage categories + evidence + quality warnings + damage severity score
37
  Base price + damage score + user notes -> NLP component -> Explanation + sales listing
 
38
  ```
39
 
40
- The numeric model provides the base price. The computer vision model provides additional information about the vehicle's visible condition. The damage score is then used as a transparent adjustment factor. Finally, the NLP component combines the structured vehicle information, the base price, the damage findings, and the adjusted price into a user-facing explanation and listing text.
41
 
42
  ### Planned Application Output
43
 
@@ -50,7 +51,8 @@ For a given vehicle, the application should return:
50
  - final recommended listing price range,
51
  - generated listing text,
52
  - short explanation of the recommendation,
53
- - limitations and confidence notes.
 
54
 
55
  ### Scope
56
 
@@ -330,6 +332,17 @@ The vision prompt asks the model to return structured JSON with visible damage l
330
 
331
  The app shows the visual evidence directly in the normal result area, not only in the technical details. If images are blurry, dark, cropped, missing important views, or if model confidence is low, the app adds a warning such as "Damage analysis uncertain". This prevents the interface from overclaiming visual certainty.
332
 
 
 
 
 
 
 
 
 
 
 
 
333
  Damage-score weights:
334
 
335
  | Damage label | Weight |
@@ -378,6 +391,7 @@ Vehicle data -> numeric ML model -> base market price
378
  Image/manual damage labels -> CV damage component -> evidence + damage score
379
  Base price + damage score -> adjusted listing price
380
  Vehicle data + price outputs + damage findings -> NLP -> listing + explanation
 
381
  ```
382
 
383
  The integration is implemented in `app/integration.py` and exposed through the Gradio interface in `app/app.py`.
@@ -386,6 +400,7 @@ Visible interaction between the blocks:
386
 
387
  - the numeric model provides the base market price,
388
  - the damage component changes the price through the damage score,
 
389
  - the NLP component receives both the numeric model output and the damage output,
390
  - the final listing explicitly explains the base price, damage adjustment, visual evidence, Swiss calibration, and recommended listing price.
391
 
@@ -561,6 +576,7 @@ The deployed Hugging Face Space should be tested with at least three cases:
561
  | Clean vehicle | normal mileage, clear exterior images, no visible damage | damage score close to 0, no damage discount, listing mentions no visible damage |
562
  | Minor visible damage | front/side images with scratch or dent | damage label appears, CHF price is adjusted downward, listing mentions the damage transparently |
563
  | Low-quality images | dark, blurry, cropped, or interior-only images | app shows image-quality warning and avoids overconfident damage claims |
 
564
 
565
  ### Screenshots
566
 
@@ -569,7 +585,8 @@ Screenshots for the final report should be stored in `reports/screenshots/`:
569
  - `01_app_input_multi_image.png`: vehicle form with front, side, rear, and interior upload fields,
570
  - `02_app_result_clean_vehicle.png`: result with CHF recommendation and no visible damage,
571
  - `03_app_result_damage_detected.png`: result with damage score, visual evidence, and adjusted price,
572
- - `04_app_quality_warning.png`: result showing an uncertain image-quality warning.
 
573
 
574
  ## 7. Ethical Considerations
575
 
@@ -579,6 +596,7 @@ To reduce misleading outputs, the app:
579
 
580
  - explicitly states that the damage score is heuristic,
581
  - shows visual evidence and image-quality warnings,
 
582
  - keeps manual fallback labels separate from AI image analysis,
583
  - avoids claiming exact repair costs,
584
  - generates listing text that mentions visible damage instead of hiding it.
@@ -593,7 +611,8 @@ Current limitations:
593
  - the Swiss-market factor is transparent but not learned from a Swiss transaction dataset,
594
  - OpenAI Vision provides image-level visual assessment, not bounding-box localization,
595
  - image analysis cannot detect hidden mechanical or structural issues,
596
- - generated listings are deterministic templates and not full LLM-generated marketplace copy.
 
597
 
598
  Future work:
599
 
 
35
  Vehicle attributes -> Numeric ML model -> Base market price
36
  Vehicle images -> Computer vision model -> Damage categories + evidence + quality warnings + damage severity score
37
  Base price + damage score + user notes -> NLP component -> Explanation + sales listing
38
+ Vehicle images -> OpenAI Vision prefill -> Suggested make/model/year/body type -> User verification -> Numeric ML model
39
  ```
40
 
41
+ The numeric model provides the base price. The computer vision model provides additional information about the vehicle's visible condition. In addition, an optional OpenAI Vision prefill step can suggest make, model, approximate production year, and body type from uploaded images. The user can verify or edit these fields before valuation. The damage score is then used as a transparent adjustment factor. Finally, the NLP component combines the structured vehicle information, the base price, the damage findings, and the adjusted price into a user-facing explanation and listing text.
42
 
43
  ### Planned Application Output
44
 
 
51
  - final recommended listing price range,
52
  - generated listing text,
53
  - short explanation of the recommendation,
54
+ - limitations and confidence notes,
55
+ - optional AI vehicle-field suggestions with confidence and warnings.
56
 
57
  ### Scope
58
 
 
332
 
333
  The app shows the visual evidence directly in the normal result area, not only in the technical details. If images are blurry, dark, cropped, missing important views, or if model confidence is low, the app adds a warning such as "Damage analysis uncertain". This prevents the interface from overclaiming visual certainty.
334
 
335
+ ### Computer Vision: Vehicle Field Prefill
336
+
337
+ The optional vehicle-prefill component is implemented in `app/vehicle_recognition.py`. It uses uploaded vehicle images to suggest:
338
+
339
+ - make,
340
+ - model,
341
+ - approximate production year,
342
+ - body type.
343
+
344
+ This feature improves usability but does not replace manual input. The app explicitly shows confidence, evidence, and warnings, and the user can correct all fields before the numeric price model is executed. The prefill prompt is conservative: it may return unknown values if the badge, lights, body shape, or model generation are not clear.
345
+
346
  Damage-score weights:
347
 
348
  | Damage label | Weight |
 
391
  Image/manual damage labels -> CV damage component -> evidence + damage score
392
  Base price + damage score -> adjusted listing price
393
  Vehicle data + price outputs + damage findings -> NLP -> listing + explanation
394
+ Vehicle images -> optional prefill -> editable vehicle fields -> numeric ML model
395
  ```
396
 
397
  The integration is implemented in `app/integration.py` and exposed through the Gradio interface in `app/app.py`.
 
400
 
401
  - the numeric model provides the base market price,
402
  - the damage component changes the price through the damage score,
403
+ - the optional vehicle-prefill component can populate structured fields from visual evidence,
404
  - the NLP component receives both the numeric model output and the damage output,
405
  - the final listing explicitly explains the base price, damage adjustment, visual evidence, Swiss calibration, and recommended listing price.
406
 
 
576
  | Clean vehicle | normal mileage, clear exterior images, no visible damage | damage score close to 0, no damage discount, listing mentions no visible damage |
577
  | Minor visible damage | front/side images with scratch or dent | damage label appears, CHF price is adjusted downward, listing mentions the damage transparently |
578
  | Low-quality images | dark, blurry, cropped, or interior-only images | app shows image-quality warning and avoids overconfident damage claims |
579
+ | Vehicle prefill | clear front/side image with visible badge/body shape | make/model/year/body suggestions appear and remain editable before final analysis |
580
 
581
  ### Screenshots
582
 
 
585
  - `01_app_input_multi_image.png`: vehicle form with front, side, rear, and interior upload fields,
586
  - `02_app_result_clean_vehicle.png`: result with CHF recommendation and no visible damage,
587
  - `03_app_result_damage_detected.png`: result with damage score, visual evidence, and adjusted price,
588
+ - `04_app_quality_warning.png`: result showing an uncertain image-quality warning,
589
+ - `05_app_ai_prefill.png`: AI prefill result with suggested vehicle fields and confidence.
590
 
591
  ## 7. Ethical Considerations
592
 
 
596
 
597
  - explicitly states that the damage score is heuristic,
598
  - shows visual evidence and image-quality warnings,
599
+ - treats image-based vehicle recognition as editable suggestions rather than verified facts,
600
  - keeps manual fallback labels separate from AI image analysis,
601
  - avoids claiming exact repair costs,
602
  - generates listing text that mentions visible damage instead of hiding it.
 
611
  - the Swiss-market factor is transparent but not learned from a Swiss transaction dataset,
612
  - OpenAI Vision provides image-level visual assessment, not bounding-box localization,
613
  - image analysis cannot detect hidden mechanical or structural issues,
614
+ - generated listings are deterministic templates and not full LLM-generated marketplace copy,
615
+ - image-based make/model/year recognition can be wrong for visually similar generations or modified vehicles.
616
 
617
  Future work:
618
 
reports/screenshots/README.md CHANGED
@@ -6,5 +6,6 @@ Use this folder for final documentation screenshots from the deployed Hugging Fa
6
  - `02_app_result_clean_vehicle.png`: CHF recommendation with no visible damage.
7
  - `03_app_result_damage_detected.png`: damage score, visual evidence, and adjusted CHF price.
8
  - `04_app_quality_warning.png`: uncertain image-quality warning for blurry, dark, cropped, or incomplete images.
 
9
 
10
  These screenshots demonstrate the deployed user workflow required in the semester project documentation.
 
6
  - `02_app_result_clean_vehicle.png`: CHF recommendation with no visible damage.
7
  - `03_app_result_damage_detected.png`: damage score, visual evidence, and adjusted CHF price.
8
  - `04_app_quality_warning.png`: uncertain image-quality warning for blurry, dark, cropped, or incomplete images.
9
+ - `05_app_ai_prefill.png`: AI vehicle-prefill result with suggested make, model, year, body type, confidence, and warnings.
10
 
11
  These screenshots demonstrate the deployed user workflow required in the semester project documentation.