product-recognition-demo/recognization/docs/research/2026-09-28-recognition-architecture/evidence/ocr_exp.py
tngo b6c87c45ea Make the catalog hash honest, share the text helpers, drop the duplicate labels
`catalog_hash` hashed the CSV and the view *names*, so replacing
`sku_l09.jpg` with a different product's packshot left the hash unchanged and
a report could claim the same catalog while every l09 decision in it had
moved. It hashes each view's bytes now.

`identity_lanes` carried its own `normalize_text` and `bigrams`, near-copies
of the ones in `rebuild_catalog` that had already drifted (the lane's table
folded ユ to itself, the catalog's did not). The lane imports the catalog's
now, so one read scores the same either way.

`evidence/image6_v2.json` was byte-identical to `eval/labels/image6.json`; the
evidence scripts and the decision document point at the canonical file and the
copy is gone. `score_crop`'s docstring now states the contract its callers
already depend on: the dict's insertion order is the lane's ranking, and
re-sorting by value names a different SKU than the lane chose.

Tests: the lane is shared by the classify pool, so a concurrent-ranking test
pins that one BFMatcher does not cross crops under eight threads.
2026-09-29 00:48:03 +09:00

29 lines
1.8 KiB
Python

import sys,os,json,pickle,numpy as np,cv2,time
sys.path.insert(0,'code'); sys.path.insert(0,os.path.dirname(__file__))
from ocr import VisionOcr
from diag import crops
S=os.path.dirname(__file__)
L=json.load(open(f'{S}/../../../../eval/labels/image6.json'))
# distinguishing tokens per SKU (any hit counts); includes siblings for near-unknowns
KEY={'sku_l01':['エッグケア','卵を使って'],'sku_l02':['アマニ'],'sku_l03':['からし','Spicy'],'sku_l04':['めんたい'],
'sku_l05':['ゼロ','ノンコレ'],'sku_l06':['フィッテ','内臓脂肪'],'sku_l07':['ラッキー'],'sku_l08':['PERI','ペリ'],
'sku_l09':['コクうま','65'],'sku_l10':['ピュアセレクト','マヨネーズ'],'sku_l11':['ハーフ','1/2'],'kewpie450':['KEWPIE','マヨネーズ']}
o=VisionOcr(languages=['ja-JP','en-US']); out={}
for res in ['full','live']:
rows=[]
for fc,c in crops('add/image6.jpg',L,res=='live'):
if fc['label']=='exclude' or c.size==0 or min(c.shape[:2])<8: continue
for up in [1,2]:
cc=cv2.resize(c,None,fx=up,fy=up,interpolation=cv2.INTER_CUBIC) if up>1 else c
t=time.perf_counter(); r=o.read(cc); ms=(time.perf_counter()-t)*1000
txt=' '.join(l.text for l in r.lines)
rows.append(dict(index=fc['index'],truth=fc['label'],up=up,text=txt,ms=ms))
out[res]=rows
pickle.dump(out,open(f'{S}/ocr.pkl','wb'))
for res,rows in out.items():
for up in [1,2]:
rr=[r for r in rows if r['up']==up]; tr=[r for r in rr if r['truth'] in KEY]
hit=sum(any(k in r['text'] for k in KEY[r['truth']]) for r in tr)
print(res,'up',up,f'in-catalog key-token hit {hit}/{len(tr)}','median ms',round(np.median([r['ms'] for r in rr]),1))
for r in out['live']:
if r['up']==2 and r['truth']!='unknown': print(r['index'],r['truth'],'|',r['text'][:80])