"""第136話 1コマ目の床の線を、絵の上に正確に描く（2026-11-05）
エイイチ「左の線が1本ない・大城さんもレーンの真ん中を通る」→ 線は絵のモデルに任せず、遠近の計算で4本引く。
大城さんの絵（床に何もない版）を切り抜いて、歩く人のレーンの真ん中に立たせる。マークは別の絵（071045）から切り出した黒い形。"""
import sys, numpy as np
from PIL import Image, ImageDraw
from scipy import ndimage
src, picsrc, out = sys.argv[1:4]
im = Image.open(src).convert("RGB"); W, H = im.size
a = np.asarray(im).astype(int)
green = (a[..., 1] - (a[..., 0] + a[..., 2]) / 2) > 40
fig = ndimage.binary_fill_holes(ndimage.binary_opening(~green, iterations=2))
ys, xs = np.nonzero(fig); t0, b0, l0, r0 = ys.min(), ys.max(), xs.min(), xs.max()
fx0 = xs[ys > b0 - 0.04 * H].mean()
vx, vy = 0.50 * W, -0.35 * H     # 消える点を画面の上の外に置き、線が集まりすぎないようにする
XB = [0.02 * W, 0.42 * W, 0.58 * W, 0.98 * W]          # 下の端での4本の線
def X(x, y): return vx + (x - vx) * (y - vy) / (H - vy)
FEET, HGT = 0.80 * H, 0.60 * H                          # 大城さんの足もとと背の高さ
cx = X((XB[0] + XB[1]) / 2, FEET)                      # 歩く人のレーンの真ん中
s = HGT / (b0 - t0)
lay = Image.new("L", (W, H), 0); d = ImageDraw.Draw(lay)
for x in XB:
    for k in range(80):
        y0 = 0.10 * H + 0.90 * H * k / 80; y1 = 0.10 * H + 0.90 * H * (k + 1) / 80 + 1
        w0 = w1 = 26                          # 4本とも同じ太さ
        d.polygon([(X(x, y0) - w0 / 2, y0), (X(x, y0) + w0 / 2, y0), (X(x, y1) + w1 / 2, y1), (X(x, y1) - w1 / 2, y1)], fill=255)
def icon(name, S=400):
    """床のマーク（黒一色のピクトグラム）を描く"""
    m = Image.new("L", (S, S), 0); g = ImageDraw.Draw(m); L = lambda pts, w: g.line(pts, fill=255, width=w, joint="curve")
    if name == "walk":
        g.ellipse((175, 10, 245, 80), fill=255)                          # 頭
        L([(200, 100), (175, 230)], 52)                                  # 胴
        L([(195, 120), (120, 185), (80, 160)], 34); L([(195, 120), (260, 175), (300, 210)], 34)   # 腕
        L([(175, 225), (120, 300), (60, 380)], 40); L([(175, 225), (240, 300), (270, 385)], 40)   # 脚
    else:
        L([(20, 40), (80, 40), (130, 280), (360, 280)], 26)              # 取っ手と台
        g.polygon([(110, 110), (380, 110), (350, 250), (135, 250)], fill=255)   # かご
        g.ellipse((120, 300, 190, 370), fill=255); g.ellipse((300, 300, 370, 370), fill=255)   # 車輪
    return m.crop(m.getbbox())
def put(m, lane, y, frac):
    lw = (XB[lane + 1] - XB[lane]) * (y - vy) / (H - vy); r = lw * frac / m.width
    m = m.resize((max(1, int(m.width * r)), max(1, int(m.height * r * 0.7))))
    c = X((XB[lane] + XB[lane + 1]) / 2, y)
    lay.paste(255, (int(c - m.width / 2), int(y - m.height / 2)), m)
put(icon("walk"), 0, 0.895 * H, 0.38)                   # 歩く人：大城さんの手前
put(icon("cart"), 2, 0.82 * H, 0.50)                    # 台車：となりのレーン
base = np.zeros((H, W, 3), np.uint8); base[:] = (0, 177, 64)
base[np.asarray(lay) > 128] = (0, 0, 0)
B = Image.fromarray(base)
f = im.crop((l0, t0, r0 + 1, b0 + 1)); mk = Image.fromarray((fig[t0:b0 + 1, l0:r0 + 1] * 255).astype(np.uint8))
f = f.resize((int(f.width * s), int(f.height * s)), Image.LANCZOS); mk = mk.resize(f.size, Image.LANCZOS)
B.paste(f, (int(cx - (fx0 - l0) * s), int(FEET - f.height)), mk)
B.save(out, quality=95)
print("レーンの真ん中", int(cx), "足もと", int(FEET), "縮め", round(s, 2))
