diff --git a/vision.py b/vision.py index f02dec0..0481e44 100644 --- a/vision.py +++ b/vision.py @@ -692,6 +692,15 @@ centering, that range should be genuinely wide (e.g. 6-9), not cosmetic. where you could assess all four categories; "medium" when one or two \ categories are unassessable; "low" when the photo mainly supports identifying \ the card rather than grading it. + Centering and edge whitening are measured directly from the pixels when a \ +MEASURED CENTERING / MEASURED EDGE WHITENING block is supplied above — that's \ +a materially stronger basis than reading either by eye, and the reverse is \ +true too: if NEITHER was measurable on this card (both missing or refused, \ +with a reason given), don't call this "high" even if the photo itself is \ +sharp and well-lit. A crisp photo of a card whose finish defeats the \ +measurement (foil, refractor, die-cut, or the margin problem described \ +above) still leaves you assessing two of the four categories by eye alone, \ +which is exactly what "medium" is for. - limitations: list each specific thing the photo prevented you from checking \ ("back not shown, so back centering and back corners are unknown", "resolution \ too low to see print lines or light surface scratches"). @@ -993,6 +1002,32 @@ def grade_card(images, api_key=None, model=None, effort=None, zoom_details=True) if card_type not in ("pokemon", "sports", "other_tcg", "other"): card_type = "other" + confidence = parsed.get("confidence") or "low" + limitations = [l for l in (parsed.get("limitations") or []) if l] + + # "high" confidence is defined to the model as being able to confidently + # assess all four categories — but centering and edge whitening are the + # two this app can measure from pixels rather than ask the model to + # judge by eye, and a self-reported "high" doesn't reliably account for + # whether that measurement was actually available on THIS card. Capped + # here rather than left to the prompt alone: the model juggles a long + # instruction list already, and this is exactly the kind of objective, + # checkable fact code should enforce rather than hope gets weighed + # correctly every time. Only caps a "high" claim down to "medium" when + # NEITHER was measured — one of two still measured is a real basis the + # model may legitimately be confident from, so that's left to its own + # judgment. + def _measured_ok(m): + return bool(m) and m.get("reliable", True) is not False + + if confidence == "high" and not _measured_ok(centering) and not _measured_ok(measured): + confidence = "medium" + limitations.append( + "Confidence capped at medium: neither centering nor edge " + "whitening could be measured from the pixels on this photo, so " + "the estimate leans on the photo alone for two of PSA's four " + "categories rather than a direct pixel measurement for either.") + return { "closeups": len(crops), "card_type": card_type, @@ -1004,14 +1039,14 @@ def grade_card(images, api_key=None, model=None, effort=None, zoom_details=True) "estimated_grade": _clean_grade(parsed.get("estimated_grade")), "grade_low": low, "grade_high": high, - "confidence": parsed.get("confidence") or "low", + "confidence": confidence, "categories": { "centering": _clean_category(parsed.get("centering")), "corners": _clean_category(parsed.get("corners")), "edges": _clean_category(parsed.get("edges")), "surface": _clean_category(parsed.get("surface")), }, - "limitations": [l for l in (parsed.get("limitations") or []) if l], + "limitations": limitations, "note": parsed.get("note") or "", "usage": usage, }