From 8e33b3ebddc11728d1efae58bb8039690ca20359 Mon Sep 17 00:00:00 2001 From: kiyo9w Date: Thu, 24 Sep 2026 22:11:51 +0900 Subject: [PATCH] feat: carry the JAN and the price's provenance through the tag pipeline The card reader produces a JAN (the last three digits printed on the tag) and says whether a price was measured from the big digits or derived from the tax-included one; the session record and the wire payload dropped both, so the strongest identity anchor the tag carries never left the reader. Also reads each card at three scales rather than two. The tax-included price is small print and only resolves at 3x; without it the price falls back to the stylised digits, which misread (measured: 30 instead of 269, 20 instead of 199). Measured through the real still path on add/image6.jpg, the tags now attached to products read: 269/357, 379/400, 199/248, 209/500, 219/200, 349/150 - prices and JANs, where the same run previously reported tags that were not in the readable row at all and prices like 2630 from another tag's text. Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- recognization/code/tags.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/recognization/code/tags.py b/recognization/code/tags.py index 1d013b4..f4f2efd 100644 --- a/recognization/code/tags.py +++ b/recognization/code/tags.py @@ -142,6 +142,8 @@ class TagRecord: "discount": self.discount, "unit_price": self.unit_price, "name": self.name, + "jan": self.jan, + "price_source": self.price_source, "flags": list(self.flags), "confidence": round(float(self.confidence), 3), "observations": len(self.observations), @@ -510,7 +512,9 @@ class TagService: aspect = width / float(height) if not (TAG_MIN_ASPECT <= aspect <= TAG_MAX_ASPECT): continue - pooled = read_card_lines(self.engine, image, box, scales=(1.0, 2.0 * max(1.0, scale_bias))) + pooled = read_card_lines( + self.engine, image, box, scales=(1.0, 2.0, 3.0 * max(1.0, scale_bias)) + ) card = card_from_lines(pooled) if pooled else seed card.box = box if not card.fields.is_readable() and card.fields.price is None: @@ -588,6 +592,8 @@ class TagService: names: Dict[str, float] = {} units: Dict[float, float] = {} discounts: Dict[float, float] = {} + jans: Dict[str, float] = {} + sources: Dict[str, float] = {} flags: set = set() confidences: List[float] = [] for observation in record.observations: @@ -603,6 +609,11 @@ class TagService: units[fields.unit_price] = units.get(fields.unit_price, 0.0) + weight if fields.discount is not None: discounts[fields.discount] = discounts.get(fields.discount, 0.0) + weight + if getattr(fields, "jan", None): + jans[fields.jan] = jans.get(fields.jan, 0.0) + weight + sources[getattr(fields, "price_source", "none")] = ( + sources.get(getattr(fields, "price_source", "none"), 0.0) + weight + ) flags.update(fields.flags) confidences.append(observation.confidence) @@ -620,6 +631,13 @@ class TagService: record.name = top(names) record.unit_price = top(units) record.discount = top(discounts) + record.jan = top(jans) + # "measured" beats "derived" when both appear: a read is better evidence + # than an inference, even though the inference is usually right. + if sources.get("measured") and sources.get("derived"): + record.price_source = "measured" if sources["measured"] >= sources["derived"] else "derived" + else: + record.price_source = top(sources) or "none" record.flags = sorted(flags) record.confidence = sum(confidences) / len(confidences) if confidences else 0.0