cat > /workspace/scratch/masks1.py <<'PY'
import numpy as np,cv2,tifffile,json,os
from scipy import ndimage as ndi
from PIL import Image,ImageDraw
from pathlib import Path
cv2.setNumThreads(2)
boxes=json.load(open('/workspace/scratch/chartboxes.json'));sources=json.load(open('/workspace/scratch/sources.json'))
# Approximate isolation regions (full-resolution source coordinates)
limits={
'HH-1041':(1000,280,1380,1295), 'HH-1206':(470,180,1910,1270), 'HH-2203':(680,290,1780,1320),'HH-2342':(530,120,1920,1260),
'HH-3315':(720,340,1670,1270),'HH-3360':(910,250,1455,1325),'HH-4402':(890,295,1550,1320),'HH-4461':(885,295,1510,1310),
'HH-5547':(780,290,1660,1350),'HH-5593':(635,240,1810,1390),'HH-6618':(820,300,1550,1320),'HH-6672':(920,280,1430,1360),
'HH-7101':(570,210,1920,1400),'HH-7150':(840,310,1510,1335),'HH-7158':(430,220,1970,1390),'HH-7186':(780,255,1620,1360),
'HH-8205':(790,255,1600,1350),'HH-8248':(780,315,1630,1370),'HH-8256':(1030,240,1350,1320),'HH-8285':(710,225,1690,1320),
'HH-9328':(445,200,1930,1430),'HH-9340':(590,200,1840,1430),'HH-9374':(550,490,1810,1050),'HH-9395':(1000,265,1400,1340)}
Path('/workspace/scratch/masks').mkdir(exist_ok=True)
for s,src in sources.items():
 a=tifffile.imread('/workspace/input/'+src).astype('float32')/65535;b=tifffile.imread('/workspace/input/charts/'+s+'_chart.tif').astype('float32')/65535
 aa=cv2.GaussianBlur(a,(0,0),.5);bb=cv2.GaussianBlur(b,(0,0),.5)
 diff=np.linalg.norm(aa-bb,axis=2)
 mask=(diff>.018).astype('uint8')
 x0,y0,x1,y1=limits[s];reg=np.zeros(mask.shape,'uint8');reg[y0:y1,x0:x1]=1;mask*=reg
 cx0,cy0,cx1,cy1=boxes[s]; mask[cy0-15:cy1+15,cx0-15:cx1+15]=1
 # eliminate tiny isolated difference regions
 n,lab,stats,ctr=cv2.connectedComponentsWithStats(mask,8)
 keep=np.where(stats[:,4]>15)[0];keep=keep[keep!=0];mask=np.isin(lab,keep).astype('uint8')
 # for solids, fill all holes
 if s in ['HH-1206','HH-2203','HH-2342','HH-5593','HH-7101','HH-9328','HH-9340','HH-9374']:
  mask=ndi.binary_fill_holes(mask).astype('uint8')
 cv2.imwrite('/workspace/scratch/masks/'+s+'.png',mask*255)
 vis=a*.5+np.array([0,0.9,0.8])*mask[:,:,None]*.5
 Image.fromarray((np.clip(vis,0,1)*255).astype('uint8')).resize((1200,800)).save('/workspace/scratch/masks/'+s+'_overlay.jpg')
json.dump(limits,open('/workspace/scratch/limits.json','w'))
out=Image.new('RGB',(1600,6*295),'#dddddd');d=ImageDraw.Draw(out)
for i,s in enumerate(sources):
 im=Image.open('/workspace/scratch/masks/'+s+'_overlay.jpg').resize((400,267));x=(i%4)*400;y=(i//4)*295;out.paste(im,(x,y));d.text((x+5,y+267),s,fill='black')
out.save('/workspace/scratch/maskcontact.jpg')
PY
OPENBLAS_NUM_THREADS=1 python /workspace/scratch/masks1.py