Files
animation/characters/female/lena_nude/hires_claude/35_silhouette.py
T

78 lines
2.8 KiB
Python
Raw Normal View History

# Stage 35 (read-only): does the approved concept turnaround describe OUR body?
#
# blender --background --python 35_silhouette.py -- <concept_front.png> <our_front.png>
#
# 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")