3ba86b2ea8
Bulk import of the working lanes that were living untracked on the PC. Content: - characters/ Lena/male body lanes, bakes, texture work, run logs - clothing/ garment pipeline, configs, gates, contract docs - garments/ MD-authored garment sources (.zprj/.zpac) - UAL-Lib/ Universal Animation Library 2 source (.blend/.fbx/.glb) - tools/ blender_bridge, iclone_bridge, md_bridge, tailor, glm_agent - docs/, plans/, dev/, .agents/plans/ Repo hygiene: - .gitattributes: LFS now covers .blend, .zprj, .zpac, .obj, .npy and the Reallusion .iAvatar/.ccAvatar/.ccRestore containers. Without this the ~3.8 GB in this commit would land as raw blobs. .png/.jpg are left out on purpose — ~250 are already tracked raw and converting them would rewrite every one without shrinking history. - .gitignore: exclude /accurig/ (~1 GB AccuRig program files, redistributable from Reallusion, nothing authored here) and /dev/null/ (git-lfs hook copies dropped by a `>/dev/null` redirect on Windows). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
53 lines
2.3 KiB
Python
53 lines
2.3 KiB
Python
# Probe: was the rosy V repainted? Sample per-vertex basecolor in the V region vs belly,
|
|
# and re-run the rosy classifier to see who it caught.
|
|
# blender --background --python dbg_rosy.py -- <07_textured.blend>
|
|
import bpy, sys
|
|
import numpy as np
|
|
|
|
BLEND = sys.argv[sys.argv.index("--") + 1]
|
|
bpy.ops.wm.open_mainfile(filepath=BLEND)
|
|
ob = max([o for o in bpy.data.objects if o.type == 'MESH'],
|
|
key=lambda o: len(o.data.vertices))
|
|
me = ob.data
|
|
n_v = len(me.vertices)
|
|
co = np.empty(n_v * 3)
|
|
me.vertices.foreach_get("co", co)
|
|
co = co.reshape(-1, 3)
|
|
|
|
base = next(i for i in bpy.data.images if "basecolor" in i.name.lower())
|
|
w, h = base.size
|
|
buf = np.empty(w * h * 4, dtype=np.float32)
|
|
base.pixels.foreach_get(buf)
|
|
rgb = buf.reshape(h, w, 4)
|
|
|
|
loops_v = np.empty(len(me.loops), dtype=np.int32)
|
|
me.loops.foreach_get("vertex_index", loops_v)
|
|
uv = np.empty(len(me.loops) * 2)
|
|
me.uv_layers.active.data.foreach_get("uv", uv)
|
|
uv = uv.reshape(-1, 2)
|
|
lx = np.clip(uv[:, 0], 0, 1) * (w - 1)
|
|
ly = np.clip(uv[:, 1], 0, 1) * (h - 1)
|
|
first_loop = np.full(n_v, len(loops_v), dtype=np.int64)
|
|
np.minimum.at(first_loop, loops_v, np.arange(len(loops_v), dtype=np.int64))
|
|
first_loop = np.minimum(first_loop, len(loops_v) - 1)
|
|
vcol = rgb[ly[first_loop].astype(int), lx[first_loop].astype(int), :3]
|
|
|
|
regions = {
|
|
"V_chest (z .78-.85, y<-.02)": (np.abs(co[:, 0]) < 0.06) & (co[:, 1] < -0.02) & (co[:, 2] > 0.78) & (co[:, 2] < 0.85),
|
|
"throat (z .85-.90, y<-.02)": (np.abs(co[:, 0]) < 0.04) & (co[:, 1] < -0.02) & (co[:, 2] > 0.85) & (co[:, 2] < 0.90),
|
|
"cup_R (z .68-.74, x<-.03)": (co[:, 0] < -0.03) & (co[:, 0] > -0.09) & (co[:, 1] < -0.02) & (co[:, 2] > 0.68) & (co[:, 2] < 0.74),
|
|
"belly (z .55-.60)": (np.abs(co[:, 0]) < 0.06) & (co[:, 1] < 0) & (co[:, 2] > 0.55) & (co[:, 2] < 0.60),
|
|
"shoulder (z .84-.86, |x|>.10)": (np.abs(co[:, 0]) > 0.10) & (np.abs(co[:, 0]) < 0.16) & (co[:, 2] > 0.84) & (co[:, 2] < 0.86),
|
|
}
|
|
band = (co[:, 2] > 0.30) & (co[:, 2] < 0.905)
|
|
rg = vcol[:, 0] - vcol[:, 1]
|
|
print(f"whole-band med rg: {np.median(rg[band]):.3f}")
|
|
for name, m in regions.items():
|
|
if not m.any():
|
|
print(f"{name}: EMPTY")
|
|
continue
|
|
c = vcol[m]
|
|
print(f"{name}: n={m.sum():6d} mean RGB ({c[:,0].mean():.3f},{c[:,1].mean():.3f},{c[:,2].mean():.3f}) "
|
|
f"rg med {np.median(rg[m]):.3f}")
|
|
print("PROBE_DONE")
|