cat > /workspace/scratch/finalmasks.py <<'PY'
import cv2,numpy as np,json,tifffile,os,sys
from scipy import ndimage as ndi
from PIL import Image
from pathlib import Path
cv2.setNumThreads(2)
sources=json.load(open('/workspace/scratch/sources.json'));soft=json.load(open('/workspace/input/page_plan.json'))['soft_edge_skus']
Path('/workspace/scratch/finalmasks').mkdir(exist_ok=True)
for s in sys.argv[1:] or list(sources):
 folders=['refined','auto','hard'] if s!='HH-2342' else ['auto']
 p=next(f'/workspace/scratch/{f}/{s}.png' for f in folders if os.path.exists(f'/workspace/scratch/{f}/{s}.png'))
 m=cv2.imread(p,0)>127
 if s in ['HH-1041','HH-4402','HH-4461','HH-7150','HH-8256','HH-9395']:
  n,lab,st,ct=cv2.connectedComponentsWithStats(m.astype('uint8'),8);m=lab==1+np.argmax(st[1:,4])
 if s=='HH-9374':
  poly=np.array([[0,0],[2400,0],[2400,580],[1750,708],[1590,662],[1400,623],[1200,602],[1010,614],[840,633],[710,668],[600,735],[0,735]],'int32')
  reg=np.zeros(m.shape,'uint8');cv2.fillPoly(reg,[poly],1);m[reg>0]=0
  di=ndi.distance_transform_edt(m);do=ndi.distance_transform_edt(~m)
  gc=np.where(m,3,2).astype('uint8');gc[di>15]=1;gc[do>15]=0
  aa=(tifffile.imread('/workspace/input/'+sources[s])/257).astype('uint8')
  cv2.grabCut(aa,gc,None,np.zeros((1,65)),np.zeros((1,65)),2,cv2.GC_INIT_WITH_MASK)
  m=np.isin(gc,[1,3]);m=ndi.binary_fill_holes(m)
 if s in soft:
  a=tifffile.imread('/workspace/input/'+sources[s]).astype('float32')/65535
  # Narrow-band colour matting. Opaque core is never altered.
  di=ndi.distance_transform_edt(m);do=ndi.distance_transform_edt(~m)
  fg=di>6;bg=do>10
  _,fi=ndi.distance_transform_edt(~fg,return_indices=True);_,bi=ndi.distance_transform_edt(~bg,return_indices=True)
  smooth=cv2.GaussianBlur(a,(3,3),.7);F=smooth[fi[0],fi[1]];B=smooth[bi[0],bi[1]]
  D=F-B;al=np.clip(np.sum((a-B)*D,2)/(np.sum(D*D,2)+1e-5),0,1)
  al[fg]=1;al[bg]=0
  # retain mechanical wooden parts' hard edges on upholstered seating
  if s in ['HH-8248','HH-8285']:
   cutoff=900 if s=='HH-8248' else 650
   hard=cv2.GaussianBlur(m.astype('float32'),(3,3),.4);al[cutoff:]=hard[cutoff:]
  al[al<.02]=0
 else:
  al=cv2.GaussianBlur(m.astype('float32'),(3,3),.4)
 cv2.imwrite('/workspace/scratch/finalmasks/'+s+'.png',np.rint(al*255).astype('uint8'))
 print(s,flush=True)
PY
OPENBLAS_NUM_THREADS=1 python /workspace/scratch/finalmasks.py