# The user's side-view canopy outline (red, main stroke) -> a smooth closed polygon in model (y, z) metres. # Per screen column: the stroke's top and bottom (centre of the top / bottom pixel cluster), then smoothing. import json import numpy as np from PIL import Image from collections import deque S = r"C:\Users\PC\AppData\Local\Temp\claude\D--HADAL---The-Descent\1fbd6834-d7f7-4606-97c8-f13f3d7e4dae" fit = json.load(open(S + r"\scratchpad\side_fit.json")) a, bu, bv = fit["a"], fit["bu"], fit["bv"] im = np.asarray(Image.open(S + r"\images\5.webp").convert("RGB")).astype(int) R, G, B = im[..., 0], im[..., 1], im[..., 2] red = (R > 170) & (G < 90) & (B < 90) red[:, 1730:] = False # the canard loop starts at u 1711 but only above v 290; cut it off cleanly: red2 = (R > 170) & (G < 90) & (B < 90) canard = np.zeros_like(red2) canard[:, 1705:] = red2[:, 1705:] canard[300:, :] = False red = red & ~canard cols = {} for u in range(int(np.nonzero(red.any(axis=0))[0].min()), int(np.nonzero(red.any(axis=0))[0].max()) + 1): vs = np.nonzero(red[:, u])[0] if len(vs) == 0: continue top = vs[vs <= vs.min() + 6].mean() bot = vs[vs >= vs.max() - 6].mean() cols[u] = (top, bot) us = np.array(sorted(cols)) tops = np.array([cols[u][0] for u in us]); bots = np.array([cols[u][1] for u in us]) def smooth(x, k=9): k = min(k, len(x) // 2 * 2 - 1) pad = np.pad(x, (k // 2, k // 2), mode="edge") return np.convolve(pad, np.ones(k) / k, mode="valid") tops_s, bots_s = smooth(tops, 15), smooth(bots, 15) step = 6 sel = list(range(0, len(us), step)) if sel[-1] != len(us) - 1: sel.append(len(us) - 1) top_yz = [((us[i] - bu) / a, (bv - tops_s[i]) / a) for i in sel] bot_yz = [((us[i] - bu) / a, (bv - bots_s[i]) / a) for i in sel] poly = top_yz + bot_yz[::-1][1:-1] json.dump({"poly_yz": poly, "top_yz": top_yz, "bot_yz": bot_yz}, open(S + r"\scratchpad\canopy_drawn.json", "w")) ys = [p[0] for p in poly]; zs = [p[1] for p in poly] print("points", len(poly), "y %.3f..%.3f z %.3f..%.3f" % (min(ys), max(ys), min(zs), max(zs))) for y, z in top_yz[::12]: print(" top y %.3f z %.3f" % (y, z)) for y, z in bot_yz[::12]: print(" bot y %.3f z %.3f" % (y, z))