`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.
30 lines
1.7 KiB
Python
30 lines
1.7 KiB
Python
import sys,os,glob,json,time,numpy as np,cv2,random
|
|
sys.path.insert(0,'code'); sys.path.insert(0,os.path.dirname(__file__)); os.environ['YOLO_OFFLINE']='1'
|
|
from gallery import ProductGallery
|
|
from detector import ShelfProductDetector
|
|
from diag import crops, sku_of
|
|
from PIL import Image
|
|
S=os.path.dirname(__file__); random.seed(0)
|
|
g=ProductGallery('stock-lopia-clean',['sku_l01','sku_l02','sku_l03','sku_l05','sku_l06','sku_l09','sku_l11']); assert g.load()
|
|
refs=[(sku_of(os.path.basename(p)[:-4]),g.embed(cv2.imread(p))) for p in sorted(glob.glob('stock-lopia-clean/*.jpg'))]
|
|
det=ShelfProductDetector(); dis=[]
|
|
for p in sorted(glob.glob('add/*')):
|
|
if 'image6' in p: continue
|
|
im=cv2.cvtColor(np.array(Image.open(p).convert('RGB')),cv2.COLOR_RGB2BGR)
|
|
for j,b in enumerate(det.detect(im).get('boxes',[])):
|
|
x1,y1,x2,y2=[int(v) for v in b['box']]; c=im[max(0,y1):y2,max(0,x1):x2]
|
|
if min(c.shape[:2])>=40: dis.append((f'd{j}_{os.path.basename(p)}',c))
|
|
random.shuffle(dis)
|
|
D=[(k,g.embed(c)) for k,c in dis]
|
|
L=json.load(open(f'{S}/../../../../eval/labels/image6.json')); E={k for k,_ in refs}
|
|
Q=[(fc['label'],g.embed(c)) for fc,c in crops('add/image6.jpg',L,True) if fc['label'] in E]
|
|
t=time.perf_counter(); _=g.embed(Q and dis[0][1]); print('embed ms',round((time.perf_counter()-t)*1000,1))
|
|
for N in [0,50,200,len(D)]:
|
|
R=refs+D[:N]; M=np.stack([e for _,e in R]); names=[k for k,_ in R]
|
|
rec={1:0,5:0,10:0,20:0}
|
|
for truth,q in Q:
|
|
s=M@q; order=[]
|
|
for i in np.argsort(-s):
|
|
if names[i] not in order: order.append(names[i])
|
|
for K in rec: rec[K]+= truth in order[:K]
|
|
print(f'skus={len(set(names)):4d} DINO recall@1/5/10/20 '+'/'.join(str(rec[K]) for K in rec)+f' of {len(Q)}')
|