# Stage 35 (read-only): does the approved concept turnaround describe OUR body? # # blender --background --python 35_silhouette.py -- # # This decides how the turnaround can be used. If the silhouettes match, the concept renders could # be projected onto the mesh as a texture source. If they do not, they are a style and tone # reference only — and quietly projecting them would smear one body's paint onto another's shape. # Widths are normalised by figure height, so framing and resolution cancel. import bpy, sys, os import numpy as np argv = sys.argv[sys.argv.index("--") + 1:] def load(p): im = bpy.data.images.load(p) w, h = im.size b = np.empty(w * h * 4, dtype=np.float32) im.pixels.foreach_get(b) a = b.reshape(h, w, 4)[:, :, :3].copy() bpy.data.images.remove(im) return a[::-1] def profile(p): A = load(p) sat = A.max(axis=2) - A.min(axis=2) m = (sat > 0.06) & (A.max(axis=2) > 0.15) ys, xs = np.nonzero(m) y0, y1 = ys.min(), ys.max() Hf = float(y1 - y0) cx = xs.mean() out = [] for t in np.arange(0.02, 1.0, 0.02): r = int(round(y0 + t * Hf)) row = np.nonzero(m[r])[0] if len(row) < 2: out.append((t, np.nan, np.nan)) continue # full extent (includes arms where they are out), and the CENTRAL run (the torso/leg) full = (row.max() - row.min()) / Hf # central run: walk out from the pixel nearest the body's x centre k = row[np.argmin(np.abs(row - cx))] lo = k while lo - 1 in set(row.tolist()) if False else (lo - 1 >= 0 and m[r, lo - 1]): lo -= 1 hi = k while hi + 1 < m.shape[1] and m[r, hi + 1]: hi += 1 out.append((t, full, (hi - lo) / Hf)) return np.array(out), Hf A, ha = profile(argv[0]) B, hb = profile(argv[1]) print(f"concept figure height {ha:.0f} px ours {hb:.0f} px") print("\n t concept_full ours_full concept_core ours_core core_diff") diffs = [] for (t, fa, ca), (_, fb, cb) in zip(A, B): if np.isnan(ca) or np.isnan(cb): continue d = cb - ca # the central run is only meaningful where it is a single body part: # below the arms (t>0.30) and above the ankles flag = "" if 0.30 < t < 0.95: diffs.append(d) if abs(d) > 0.02: flag = " <-- differs" print(f" {t:.2f} {fa:9.3f} {fb:9.3f} {ca:10.3f} {cb:9.3f} {d:+8.3f}{flag}") if diffs: diffs = np.array(diffs) print(f"\nCORE WIDTH (torso/legs, t 0.30-0.95), ours minus concept, as fraction of height:") print(f" mean {diffs.mean():+.4f} mean|d| {np.abs(diffs).mean():.4f} " f"max|d| {np.abs(diffs).max():.4f}") print(f" bands differing by >2% of height: {int((np.abs(diffs)>0.02).sum())} of {len(diffs)}") print("SILHOUETTE_DONE")