# Critique helper: colour-class runs down columns (or along rows) of the sheet and of the model render, both in SHEET pixels. # usage: runs.py view col|row v1 v2 ... (view: top side front rear) import sys, numpy as np import os SHEET_IMG=os.environ.get('SHEET','critique/sheet_clean.png') from PIL import Image BOX = dict(top=(30,40,970,520), side=(30,530,970,1065), front=(970,30,1410,530), rear=(960,545,1410,1030)) def cls(p, a=255): import colorsys r,g,b = [int(x) for x in p[:3]] if a < 128: return '.' h,s,v = colorsys.rgb_to_hsv(r/255,g/255,b/255); h*=360 if v>0.9 and s<0.12: return '.' # paper if v<0.30: return 'K' # black / dark line if s>0.45 and (h<12 or h>345) and r>150 and g<90: return 'R' # red mark if s>0.40 and 12<=h<24: return 'J' # jaw red-orange if s>0.40 and 24<=h<50: return 'O' # orange if 170<=h<=225 and s>0.25 and v>0.55: return 'G' # glass blue if v<0.50: return 'D' # dark slate if s<0.30: return 'g' # grey (incl. blue-grey) return '?' def load(view, which): b = BOX[view]; w,h = b[2]-b[0], b[3]-b[1] if which=='sheet': im = Image.open(SHEET_IMG).convert('RGBA').crop(b) else: im = Image.open(f'previews/compare/lk_{view}.png').convert('RGBA').resize((w,h), Image.LANCZOS) return np.array(im), b def runs(seq): out=[]; s=0 for i in range(1,len(seq)+1): if i==len(seq) or seq[i]!=seq[s]: if seq[s]!='.' : out.append(f'{seq[s]}{s}-{i-1}') s=i return out view, mode = sys.argv[1], sys.argv[2] for v in map(int, sys.argv[3:]): for which in ('sheet','model'): a,b = load(view, which) if mode=='col': col = a[:, v-b[0]]; seq=[cls(p,p[3]) for p in col]; off=b[1] else: row = a[v-b[1]]; seq=[cls(p,p[3]) for p in row]; off=b[0] r = runs(seq) # merge tiny runs (<3 px) out r = [x for x in r if (lambda s,e: e-s>=2)(*map(int,x[1:].split('-')))] print(f'{mode} {v} {which:5s}: ' + ' '.join(f'{x[0]}{int(x[1:].split("-")[0])+off}-{int(x.split("-")[1])+off}' for x in r))