cat > /workspace/scratch/analyse.py <<'PY'
import cv2,numpy as np,tifffile,json,os
from pathlib import Path
from PIL import Image,ImageDraw
meta={}
for f in sorted(Path('/workspace/input/charts').glob('*')):
 im=cv2.imread(str(f),cv2.IMREAD_UNCHANGED)[:,:,::-1]/65535
 u=(im*255).astype('uint8');g=cv2.cvtColor(u,cv2.COLOR_RGB2GRAY)
 ed=cv2.Canny(g,40,100)
 cs,_=cv2.findContours(ed,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
 boxes=[]
 for c in cs:
  x,y,w,h=cv2.boundingRect(c)
  if 12<w<150 and 12<h<150 and .8<w/h<1.2 and cv2.contourArea(c)>w*h*.75 and 650<x<1650 and 350<y<1150:
   boxes.append([x,y,w,h])
 boxes=np.array(boxes)
 if len(boxes):
  # patches similar size within cluster
  sizes=boxes[:,2];med=np.median(sizes);boxes=boxes[abs(sizes-med)<med*.15]
  x0,y0=boxes[:,:2].min(0);x1,y1=(boxes[:,:2]+boxes[:,2:]).max(0)
  meta[f.stem.split('_')[0]]=[int(x0),int(y0),int(x1),int(y1)]
  vis=u.copy();cv2.rectangle(vis,(x0,y0),(x1,y1),(255,0,0),4)
  Image.fromarray(vis).resize((600,400)).save('/workspace/scratch/'+f.stem+'.jpg')
 print(f.name,len(boxes),meta.get(f.stem.split('_')[0]))
json.dump(meta,open('/workspace/scratch/chartboxes.json','w'),indent=2)
# contact chart all
out=Image.new('RGB',(1200,1600));d=ImageDraw.Draw(out)
for i,f in enumerate(sorted(Path('/workspace/scratch').glob('*_chart.jpg'))):
 im=Image.open(f).resize((300,200));out.paste(im,((i%4)*300,(i//4)*260));d.text(((i%4)*300+5,(i//4)*260+201),f.stem,fill='white')
out.save('/workspace/scratch/chartboxes.jpg')
for s in ['HH-9328','HH-9340']:
 im=Image.open('/workspace/input/photos/'+s+'.tif');im.crop((450,220,2000,1450)).save('/workspace/scratch/'+s+'crop.png')
PY
python /workspace/scratch/analyse.py