"""画像認識と物体検出（/series7）のトップのカード（3:2・1800×1200）を作る（2026-10-30）。
Log03 で撮った赤い箱の写真を4列に並べ、画素 → 白黒 → しきい値 → 輪郭 と処理が進む順に見せる。
背景の青い箱の角と紙の継ぎ目は、赤い箱と影だけを残して背景をなめらかな面に置き換えて消している。
元の写真: KIOXIA の vision/captured/Log03/IMG_7911.JPG（第1引数で渡す）
出力: 第2引数の PNG → public/site/series7-image-recognition-steps.webp
"""
import sys
import cv2, numpy as np
from PIL import Image, ImageOps, ImageDraw, ImageFont
src=np.array(ImageOps.exif_transpose(Image.open(sys.argv[1])).convert('RGB'))
cx,cy=2195,1019
cw,chh=885,2353
x0=cx-cw//2; y0=max(0,cy-int(chh*0.40))
crop=src[y0:y0+chh,x0:x0+cw]
img=cv2.resize(crop,(450,1200),interpolation=cv2.INTER_AREA)
# 赤い箱（と影）だけ残し、背景はなめらかな面に置き換える（青い箱の角・紙の継ぎ目を消す）
_h=cv2.cvtColor(img,cv2.COLOR_RGB2HSV)
_r=(((_h[...,0]<8)|(_h[...,0]>170))&(_h[...,1]>110)&(_h[...,2]>50)).astype(np.uint8)
_n,_l,_s,_=cv2.connectedComponentsWithStats(_r); _r=(_l==1+np.argmax(_s[1:,4])).astype(np.uint8)
_r=cv2.morphologyEx(_r,cv2.MORPH_CLOSE,np.ones((15,15),np.uint8))
_dark=(cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)<150).astype(np.uint8)
_kill=cv2.dilate(np.maximum(_r,_dark),np.ones((25,25),np.uint8))
_bg=cv2.inpaint(img,_kill,15,cv2.INPAINT_TELEA)
_bg=cv2.GaussianBlur(_bg,(0,0),40)
_keep=cv2.dilate(_r,np.ones((61,61),np.uint8))
_keep[:, :]=_keep*0+_keep  # 箱の近く（影を含む）
_yy,_xx=np.nonzero(_r); _ymax=_yy.max()
_keep[_ymax+40:,:]=0
_a=cv2.GaussianBlur(_keep.astype(np.float32),(0,0),12)[...,None]
img=(img*_a+_bg*(1-_a)).astype(np.uint8)
hsv=cv2.cvtColor(img,cv2.COLOR_RGB2HSV)
red=(((hsv[...,0]<8)|(hsv[...,0]>170))&(hsv[...,1]>110)&(hsv[...,2]>50)).astype(np.uint8)
red=cv2.morphologyEx(red,cv2.MORPH_OPEN,np.ones((5,5),np.uint8)); red=cv2.morphologyEx(red,cv2.MORPH_CLOSE,np.ones((11,11),np.uint8))
n,lab,st,_=cv2.connectedComponentsWithStats(red); k=1+np.argmax(st[1:,4]); red=(lab==k).astype(np.uint8)
gray=cv2.cvtColor(img,cv2.COLOR_RGB2GRAY); g3=np.stack([gray]*3,-1).astype(float)
BK=(17,17,17);WH=(241,240,236);RD=(224,38,44)
t1=g3*(1-red[...,None])+img*red[...,None]
t2=g3
t3=np.where(red[...,None]>0,np.array(WH),np.array(BK)).astype(float)
t4=np.full_like(img,WH)
cnts,_=cv2.findContours(red,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE); c=max(cnts,key=cv2.contourArea)
cv2.drawContours(t4,[c],-1,RD,4,lineType=cv2.LINE_AA)
x,y,w,h=cv2.boundingRect(c)
def dash(im,p0,p1,col,th=2,dl=12):
    p0=np.array(p0,float);p1=np.array(p1,float);L=np.linalg.norm(p1-p0);n=max(2,int(L//dl))
    for i in range(0,n,2):
        a=p0+(p1-p0)*i/n;b=p0+(p1-p0)*min(i+1,n)/n
        cv2.line(im,tuple(a.astype(int)),tuple(b.astype(int)),col,th,cv2.LINE_AA)
p=16
for a,b in [((x-p,y-p),(x+w+p,y-p)),((x+w+p,y-p),(x+w+p,y+h+p)),((x+w+p,y+h+p),(x-p,y+h+p)),((x-p,y+h+p),(x-p,y-p))]: dash(t4,a,b,BK)
M=cv2.moments(c);mx,my=int(M['m10']/M['m00']),int(M['m01']/M['m00'])
cv2.line(t4,(mx-14,my),(mx+14,my),BK,2);cv2.line(t4,(mx,my-14),(mx,my+14),BK,2)
out=np.concatenate([t1,t2,t3,t4.astype(float)],1)
im=Image.fromarray(out.clip(0,255).astype(np.uint8)); d=ImageDraw.Draw(im)
f=ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',34)
fs=ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',28)
labs=['PIXELS','GRAY','THRESHOLD','CONTOUR']
for i,l in enumerate(labs):
    col=WH if i==2 else BK
    d.text((i*450+36,1060),f'0{i+1}',font=fs,fill=col)
    d.text((i*450+36,1104),l,font=f,fill=col)
for e in [450,900,1350]: d.line((e,0,e,1200),fill=BK,width=2)
im.save(sys.argv[2])
