import cv2, numpy as np, json
from PIL import Image, ImageDraw, ImageFont
F='/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc'
def font(s): return ImageFont.truetype(F,s,index=0)  # JP
base=Image.open('crop.png').convert('RGB'); W,H=base.size
M={n:np.load(f'm_{n}.npy') for n in ['arm','red','blue']}
# arm: fill holes
a=(M['arm']*255).astype(np.uint8); cs,_=cv2.findContours(a,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
a2=np.zeros_like(a); cv2.drawContours(a2,cs,-1,255,-1); M['arm']=a2>0
def bbox(m): ys,xs=np.where(m); return [int(xs.min()),int(ys.min()),int(xs.max()),int(ys.max())]
B={n:bbox(m) for n,m in M.items()}
NAME={'arm':'アーム','red':'赤いブロック','blue':'青いブロック'}
KP=[(470,95),(470,265),(462,345),(458,405),(395,568),(508,568)]
def chip(d,xy,text,size=22,fg='white',bg=(20,20,20)):
    f=font(size); x,y=xy; tw=d.textlength(text,font=f); d.rectangle([x,y,x+tw+16,y+size+12],fill=bg); d.text((x+8,y+3),text,font=f,fill=fg)
def dim(im,k=0.55): return Image.blend(im,Image.new('RGB',im.size,(0,0,0)),1-k)
out={}
# 分類
im=base.copy(); d=ImageDraw.Draw(im); chip(d,(24,24),'机の上のロボットアーム',38); out['cls']=im
# 検出
im=base.copy(); d=ImageDraw.Draw(im)
for n,(x0,y0,x1,y1) in B.items():
    p=8; d.rectangle([x0-p,y0-p,x1+p,y1+p],outline='white',width=5); chip(d,(x0-p,y0-p-52),NAME[n],30)
out['det']=im
# セグメンテーション：暗くして、物の形だけ白く塗る
im=np.array(dim(base,0.45)).astype(float)
for n,v in [('arm',255),('red',255),('blue',255)]:
    m=M[n]; im[m]=im[m]*0.25+np.array([v,v,v])*0.75
im=Image.fromarray(im.astype(np.uint8)); d=ImageDraw.Draw(im)
for n in M:
    cs,_=cv2.findContours((M[n]*255).astype(np.uint8),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
    for c in cs: d.line([tuple(p[0]) for p in c]+[tuple(c[0][0])],fill='white',width=3)
out['seg']=im
# 姿勢推定
im=dim(base,0.6); d=ImageDraw.Draw(im)
d.line(KP[:4],fill='white',width=6); d.line([KP[3],KP[4]],fill='white',width=6); d.line([KP[3],KP[5]],fill='white',width=6)
for x,y in KP: d.ellipse([x-13,y-13,x+13,y+13],fill='white',outline=(20,20,20),width=4)
out['pose']=im
# 追跡（説明用：手の位置の通り道）
im=dim(base,0.6); d=ImageDraw.Draw(im)
trail=[(150,250),(230,300),(320,350),(400,390),(458,405)]
for i in range(len(trail)-1): d.line([trail[i],trail[i+1]],fill='white',width=5)
for x,y in trail[:-1]: d.ellipse([x-7,y-7,x+7,y+7],fill='white')
x0,y0,x1,y1=B['arm']; chip(d,(trail[-1][0]+20,trail[-1][1]-60),'ID 1  アームの手',22)
for n in ['red','blue']:
    x0,y0,x1,y1=B[n]; d.rectangle([x0-8,y0-8,x1+8,y1+8],outline='white',width=4); chip(d,(x0-8,y0-48),'ID 2' if n=='red' else 'ID 3',22)
out['trk']=im
# 深度（Depth Anything V2 Small の出力。近いほど白）
dp=np.load('depth.npy'); dn=(dp-dp.min())/(dp.max()-dp.min())
im=Image.fromarray((dn*255).astype(np.uint8)).convert('RGB'); out['dep']=im
for k,v in out.items(): v.save(f'A_{k}.png')
json.dump({'bbox':B,'kp':KP,'area':{n:int(M[n].sum()) for n in M}},open('meta.json','w'),ensure_ascii=False)
# データ案用：セグの粗い格子（24x16）
g=np.zeros((16,24),int)
for n,v in [('arm',1),('red',2),('blue',3)]:
    s=cv2.resize(M[n].astype(np.uint8),(24,16),interpolation=cv2.INTER_AREA); g[s>0]=v
json.dump(g.tolist(),open('grid.json','w'))
dd=cv2.resize(dn,(8,5),interpolation=cv2.INTER_AREA); json.dump(np.round(dd,2).tolist(),open('dgrid.json','w'))
# 輪郭の点の数（手で点を打つ数の目安）
pts={}
for n in M:
    cs,_=cv2.findContours((M[n]*255).astype(np.uint8),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
    c=max(cs,key=cv2.contourArea); pts[n]=len(cv2.approxPolyDP(c,2.0,True))
print(B,pts)
json.dump(pts,open('pts.json','w'))
