fix: restore reliable authenticated live recognition
This commit is contained in:
parent
0e23425ef0
commit
934615b593
40 changed files with 2556 additions and 820 deletions
|
|
@ -49,6 +49,88 @@ MILK_SHELF_LABELS = {
|
|||
}
|
||||
|
||||
|
||||
def enrollment_report(stock: str, csv_name: str = "stock.csv") -> dict:
|
||||
"""Catalog versus enrolled views. Does not load a model or write files.
|
||||
|
||||
A SKU with no readable view cannot be recognized. One view is reported, not
|
||||
treated as enough: the measured knee is two views. Duplicate CSV rows and a
|
||||
head whose class list disagrees with the catalog are blockers.
|
||||
"""
|
||||
import csv
|
||||
import re
|
||||
|
||||
from gallery import ProductGallery
|
||||
|
||||
names = []
|
||||
try:
|
||||
names = os.listdir(stock)
|
||||
except OSError:
|
||||
names = []
|
||||
images = [name for name in names if os.path.splitext(name)[1].lower() in (".jpeg", ".jpg", ".png")]
|
||||
rows = []
|
||||
csv_path = os.path.join(stock, csv_name)
|
||||
if os.path.exists(csv_path):
|
||||
with open(csv_path, encoding="utf-8") as handle:
|
||||
rows = list(csv.DictReader(handle))
|
||||
sku_ids = []
|
||||
seen = set()
|
||||
duplicate_rows = []
|
||||
for row in rows:
|
||||
sku_id = (row.get("SKU_ID") or "").strip()
|
||||
if not sku_id:
|
||||
continue
|
||||
if sku_id in seen:
|
||||
duplicate_rows.append(sku_id)
|
||||
seen.add(sku_id)
|
||||
sku_ids.append(sku_id)
|
||||
gallery = ProductGallery(stock, sku_ids or [])
|
||||
views = {sku_id: gallery._views_for(sku_id) for sku_id in dict.fromkeys(sku_ids)}
|
||||
missing = [sku_id for sku_id, paths in views.items() if not paths]
|
||||
single = [sku_id for sku_id, paths in views.items() if len(paths) == 1]
|
||||
claimed = {os.path.basename(path) for paths in views.values() for path in paths}
|
||||
orphans = sorted(name for name in images if name not in claimed)
|
||||
# ponytail: prefix collision check. A view named sku_001 must not also match sku_00.
|
||||
collisions = []
|
||||
for sku_id, paths in views.items():
|
||||
bare = [os.path.basename(path) for path in paths if os.path.splitext(os.path.basename(path))[0] != sku_id
|
||||
and not re.fullmatch(re.escape(sku_id) + r"[_-]\d+", os.path.splitext(os.path.basename(path))[0])]
|
||||
if bare:
|
||||
collisions.append(sku_id)
|
||||
blockers = []
|
||||
if duplicate_rows:
|
||||
blockers.append("duplicate_csv_sku")
|
||||
if missing:
|
||||
blockers.append("sku_without_view")
|
||||
if collisions:
|
||||
blockers.append("view_prefix_collision")
|
||||
head_path = os.path.join(stock, "identity_head.pt")
|
||||
head_note = None
|
||||
if os.path.exists(head_path):
|
||||
head_note = "present_not_opened"
|
||||
# Do not torch.load here: the head can be large, and this report must stay cheap.
|
||||
# Class-list drift is reported when the caller passes class_ids.
|
||||
return {
|
||||
"stock": stock,
|
||||
"sku_ids": list(dict.fromkeys(sku_ids)),
|
||||
"images": len(images),
|
||||
"missing": missing,
|
||||
"single_view": single,
|
||||
"orphans": orphans,
|
||||
"duplicate_rows": duplicate_rows,
|
||||
"collisions": collisions,
|
||||
"blockers": blockers,
|
||||
"head": head_note,
|
||||
}
|
||||
|
||||
|
||||
def enrollment_report_text(report: dict) -> str:
|
||||
return (
|
||||
f"{report['stock']}: {len(report['sku_ids'])} SKUs, {report['images']} images, "
|
||||
f"missing={len(report['missing'])}, single_view={len(report['single_view'])}, "
|
||||
f"orphans={len(report['orphans'])}, blockers={report['blockers'] or 'none'}"
|
||||
)
|
||||
|
||||
|
||||
def build_model(image_size: int = 518):
|
||||
import timm
|
||||
import torch
|
||||
|
|
@ -149,13 +231,13 @@ def train_from_stock(args) -> None:
|
|||
from gallery import ProductGallery
|
||||
|
||||
stock = args.stock
|
||||
gallery = ProductGallery(stock, [])
|
||||
# the SKUs are whatever the stock directory holds
|
||||
skus = sorted({os.path.splitext(name)[0].rsplit("_", 1)[0] if os.path.splitext(name)[0].rsplit("_", 1)[-1].isdigit()
|
||||
else os.path.splitext(name)[0]
|
||||
for name in os.listdir(stock)
|
||||
if os.path.splitext(name)[1].lower() in (".jpeg", ".jpg", ".png")})
|
||||
views = {sku: gallery._views_for(sku) for sku in skus}
|
||||
report = enrollment_report(stock)
|
||||
print(enrollment_report_text(report))
|
||||
if report["blockers"]:
|
||||
print(f"{stock}: not training; enrollment blockers: {', '.join(report['blockers'])}")
|
||||
return
|
||||
gallery = ProductGallery(stock, report["sku_ids"])
|
||||
views = {sku: gallery._views_for(sku) for sku in report["sku_ids"]}
|
||||
views = {sku: paths for sku, paths in views.items() if paths}
|
||||
if len(views) < 2:
|
||||
print(f"{stock}: needs at least two products with views, found {len(views)}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue