component-studio-coordinator / scripts /reference_registration_raster.py
mantrakp's picture
Add bounded reference registration evidence to isolated refinement
876fa4c verified
Raw History Blame Contribute Delete
3.78 kB
"""Unlit orthographic raster evidence; bounding-box alignment is not registration."""
import numpy as np
def rasterize(parts, bounds, shape, front):
height, width = shape
front = np.asarray(front, dtype=float)
right = -np.cross(front, [0, 0, 1])
all_vertices = np.concatenate([p['vertices'] for p in parts])
lo = np.array([(all_vertices @ right).min(), all_vertices[:, 2].min()])
span = np.array([np.ptp(all_vertices @ right), np.ptp(all_vertices[:, 2])])
if np.any(span <= 0):
raise ValueError('Degenerate projection bounds')
x0, y0, x1, y1 = bounds
rgb = np.full((height, width, 3), 220, dtype=np.uint8)
depth = np.full((height, width), -np.inf)
front_facing = np.zeros((height, width), dtype=bool)
covered = np.zeros((height, width), dtype=bool)
occluded = np.zeros((height, width), dtype=bool)
for part in parts:
vertices, faces, uvs, texture = [part[k] for k in ('vertices', 'faces', 'uvs', 'texture')]
mode = part.get('alpha_mode', 'OPAQUE')
if mode not in {'OPAQUE', 'MASK'}:
raise ValueError('Unsupported diagnostic alpha mode: ' + mode)
factor = np.asarray(part.get('base_color_factor', [1, 1, 1, 1]), dtype=float)
screen = np.c_[(vertices @ right - lo[0]) / span[0] * (x1-x0) + x0,
y1 - (vertices[:, 2] - lo[1]) / span[1] * (y1-y0)]
for face, uv in zip(faces, uvs, strict=True):
p = screen[face]
xmin, ymin = np.maximum(np.floor(p.min(0)).astype(int), 0)
xmax, ymax = np.minimum(np.ceil(p.max(0)).astype(int), [width-1, height-1])
if xmin > xmax or ymin > ymax:
continue
xx, yy = np.meshgrid(np.arange(xmin, xmax+1), np.arange(ymin, ymax+1))
d = (p[1,1]-p[2,1])*(p[0,0]-p[2,0])+(p[2,0]-p[1,0])*(p[0,1]-p[2,1])
if abs(d) < 1e-12:
continue
a = ((p[1,1]-p[2,1])*(xx-p[2,0])+(p[2,0]-p[1,0])*(yy-p[2,1]))/d
b = ((p[2,1]-p[0,1])*(xx-p[2,0])+(p[0,0]-p[2,0])*(yy-p[2,1]))/d
bary = np.stack([a,b,1-a-b], axis=-1)
inside = (bary >= -1e-7).all(-1)
xx, yy, bary = xx[inside], yy[inside], bary[inside]
z = bary @ (vertices[face] @ front)
coords = bary @ uv
tx = np.clip(np.rint(coords[:,0]*(texture.shape[1]-1)).astype(int),0,texture.shape[1]-1)
ty = np.clip(np.rint((1-coords[:,1])*(texture.shape[0]-1)).astype(int),0,texture.shape[0]-1)
if mode == 'MASK':
visible = texture[ty, tx, 3] / 255 * factor[3] >= part.get('alpha_cutoff', .5)
xx, yy, z, tx, ty = xx[visible], yy[visible], z[visible], tx[visible], ty[visible]
previous = depth[yy, xx]
occluded[yy, xx] |= np.isfinite(previous) & (np.abs(previous-z)>1e-6)
keep = z > previous
xx, yy, z, tx, ty = xx[keep], yy[keep], z[keep], tx[keep], ty[keep]
# Convert only visible sampled texels, never the full potentially 16K atlas.
srgb = texture[ty, tx, :3].astype(float) / 255
linear = np.where(srgb <= .04045, srgb / 12.92, ((srgb + .055) / 1.055) ** 2.4)
linear *= factor[:3]
corrected = np.where(linear <= .0031308, 12.92 * linear,
1.055 * np.maximum(linear, 0) ** (1/2.4) - .055)
rgb[yy,xx] = np.clip(np.rint(corrected * 255), 0, 255).astype(np.uint8)
depth[yy,xx] = z
normal = np.cross(vertices[face[1]]-vertices[face[0]],vertices[face[2]]-vertices[face[0]])
front_facing[yy,xx] = normal @ front > 0
covered[yy,xx] = True
return dict(rgb=rgb, coverage=covered, front_facing=front_facing, occluded=occluded)