`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.
57 lines
3.3 KiB
Python
57 lines
3.3 KiB
Python
import sys,os,json,pickle,csv,numpy as np
|
|
S=os.path.dirname(os.path.abspath(__file__)); sys.path.insert(0,S)
|
|
from ocr_fuse import norm,sim # noqa (module runs OCR of packshots on import; cheap)
|
|
sig=pickle.load(open(f'{S}/sig.pkl','rb')); O=pickle.load(open(f'{S}/ocr.pkl','rb'))
|
|
L={f['index']:f['label'] for f in json.load(open(f'{S}/../../../../eval/labels/image6.json'))['facings']}
|
|
def conf(ev):
|
|
r=sorted(ev.items(),key=lambda kv:-kv[1]['inliers']*(0.25+kv[1]['color']))
|
|
(k1,e1),(k2,e2)=r[0],r[1]; i1,i2=e1['inliers'],e2['inliers']
|
|
return k1, i1*(1-i2/max(i1,1))*e1['color']
|
|
for res,up,run in [('full',1,'v_ms_root_aff_full.pkl'),('live',2,'v_ms_root_aff_live.pkl')]:
|
|
ocr={r['index']:r['text'] for r in O[res] if r['up']==up}
|
|
M={x['index']:x for x in pickle.load(open(f'{S}/{run}','rb'))['rows']}
|
|
n_in=sum(1 for t in L.values() if t not in ('unknown','exclude'))
|
|
# OCR-alone identity among ALL catalog SKUs
|
|
ok=tot=0
|
|
for i,t in L.items():
|
|
if t in ('unknown','exclude') or i not in ocr: continue
|
|
tx=ocr[i];
|
|
if len(norm(tx))<4: continue
|
|
s={k:sim(tx,v) for k,v in sig.items()}; tot+=1; ok+= max(s,key=s.get)==t
|
|
print(f'== {res}: OCR-alone top-1 over all 12 catalog SKUs, on facings with text: {ok}/{tot}')
|
|
for delta in [None,0.0,0.1,0.2]:
|
|
line=[]
|
|
for tau in [0.5,1,2,3]:
|
|
c=a=0
|
|
for i,t in L.items():
|
|
if t=='exclude' or i not in M or not M[i]['ev']: continue
|
|
k,cf=conf(M[i]['ev'])
|
|
if cf<tau: continue
|
|
if delta is not None and len(norm(ocr.get(i,'')))>=4:
|
|
s={kk:sim(ocr[i],v) for kk,v in sig.items()}; j=max(s,key=s.get)
|
|
if j!=k and s[j]-s[k]>delta: continue # text names a different catalog product: veto
|
|
a+=1; c+= k==t
|
|
line.append(f'tau{tau}: {c}/{n_in} p={c/max(a,1):.0%}')
|
|
print(' veto' if delta is not None else ' matcher only', delta, ' | '.join(line))
|
|
print('== union: matcher (veto) OR OCR-only identity')
|
|
for res,up,run in [('full',1,'v_ms_root_aff_full.pkl'),('live',2,'v_ms_root_aff_live.pkl')]:
|
|
ocr={r['index']:r['text'] for r in O[res] if r['up']==up}
|
|
M={x['index']:x for x in pickle.load(open(f'{S}/{run}','rb'))['rows']}
|
|
n_in=sum(1 for t in L.values() if t not in ('unknown','exclude'))
|
|
for tau in [0.5,1,2]:
|
|
for sg,mu in [(0.5,0.15),(0.6,0.2),(0.7,0.25)]:
|
|
c=a=0; fa_unk=0; bysrc={'m':0,'o':0}
|
|
for i,t in L.items():
|
|
if t=='exclude' or i not in M: continue
|
|
tx=ocr.get(i,''); s={kk:sim(tx,v) for kk,v in sig.items()} if len(norm(tx))>=4 else {}
|
|
j=max(s,key=s.get) if s else None
|
|
ans=None
|
|
if M[i]['ev']:
|
|
k,cf=conf(M[i]['ev'])
|
|
if cf>=tau and not (j and j!=k and s[j]-s[k]>0): ans=k; src='m'
|
|
if ans is None and s:
|
|
r=sorted(s.values(),reverse=True)
|
|
if r[0]>=sg and r[0]-r[1]>=mu: ans=j; src='o'
|
|
if ans:
|
|
a+=1; c+= ans==t; bysrc[src]+= ans==t; fa_unk+= t=='unknown'
|
|
print(f' {res} tau{tau} ocr>={sg} m{mu}: correct {c}/{n_in} ({c/n_in:.0%}) precision {c/max(a,1):.0%} accepted-unknown {fa_unk}/48 from {bysrc}')
|