FaceAnything / src /faceanything /colorize.py
Umut Kocasari
Add FaceAnything Gradio demo app
4db294e
Raw
History Blame Contribute Delete
4.59 kB
"""Colorization helpers: depth -> jet, normals -> RGB, canonical -> RGB."""
from __future__ import annotations
import numpy as np
try:
import cv2
_HAS_CV2 = True
except Exception: # pragma: no cover
_HAS_CV2 = False
def _robust_minmax(values: np.ndarray, lo: float = 2.0, hi: float = 98.0):
finite = values[np.isfinite(values)]
if finite.size == 0:
return 0.0, 1.0
a, b = np.percentile(finite, [lo, hi])
if b <= a:
b = a + 1e-6
return float(a), float(b)
def depth_to_jet(depth: np.ndarray, valid: np.ndarray | None = None,
vmin: float | None = None, vmax: float | None = None,
bg=(255, 255, 255)) -> np.ndarray:
"""Colorize a (H, W) depth map with a JET colormap; invalid -> background."""
depth = np.asarray(depth, dtype=np.float32)
if valid is None:
valid = np.isfinite(depth) & (depth > 0)
if vmin is None or vmax is None:
vmin, vmax = _robust_minmax(depth[valid]) if valid.any() else (0.0, 1.0)
norm = np.clip((depth - vmin) / (vmax - vmin), 0, 1)
u8 = (norm * 255).astype(np.uint8)
if _HAS_CV2:
rgb = cv2.applyColorMap(u8, cv2.COLORMAP_JET)[:, :, ::-1] # BGR->RGB
else: # simple fallback
rgb = np.stack([u8, np.zeros_like(u8), 255 - u8], axis=-1)
rgb = rgb.copy()
rgb[~valid] = np.array(bg, dtype=np.uint8)
return rgb
def depth_to_jet_colors(depth_values: np.ndarray, vmin: float, vmax: float) -> np.ndarray:
"""JET colors (N, 3) uint8 for a flat array of depth values."""
depth_values = np.asarray(depth_values, dtype=np.float32)
norm = np.clip((depth_values - vmin) / (vmax - vmin), 0, 1)
u8 = (norm * 255).astype(np.uint8)
if _HAS_CV2:
lut = cv2.applyColorMap(np.arange(256, dtype=np.uint8)[:, None],
cv2.COLORMAP_JET)[:, 0, ::-1]
return lut[u8]
return np.stack([u8, np.zeros_like(u8), 255 - u8], axis=-1)
def normals_to_rgb(normals: np.ndarray) -> np.ndarray:
"""Standard normal-map RGB from OUTWARD camera-space normals (OpenCV frame).
Displays in the convention used by Sapiens and other normal papers:
facing camera = blue, +X right = red, +Y up = green. Input normals are the
toward-camera (outward) normals from ``pointmap_to_normals``.
"""
n = np.asarray(normals, dtype=np.float32)
disp = np.stack([n[..., 0], -n[..., 1], -n[..., 2]], axis=-1)
rgb = (disp + 1.0) * 0.5 * 255.0
return np.clip(rgb, 0, 255).astype(np.uint8)
def canonical_to_rgb(canonical: np.ndarray, valid: np.ndarray | None = None,
lo=2.0, hi=98.0, ranges=None, bg=(255, 255, 255)):
"""Map canonical XYZ coordinates to RGB via per-axis percentile stretch.
Returns ``(rgb, ranges)`` where ``ranges`` is the list of ``(min, max)`` per
axis, so a consistent mapping can be reused across frames.
"""
canonical = np.asarray(canonical, dtype=np.float32)
flat = canonical.reshape(-1, 3)
if valid is not None:
sel = flat[valid.reshape(-1)]
else:
sel = flat
if ranges is None:
ranges = []
for c in range(3):
vals = sel[:, c]
vals = vals[np.isfinite(vals)]
if vals.size == 0:
ranges.append((0.0, 1.0))
else:
a, b = np.percentile(vals, [lo, hi])
if b <= a:
b = a + 1e-6
ranges.append((float(a), float(b)))
out = np.zeros_like(flat)
for c in range(3):
a, b = ranges[c]
out[:, c] = np.clip((flat[:, c] - a) / (b - a), 0, 1)
rgb = (out * 255).astype(np.uint8).reshape(canonical.shape)
if valid is not None:
rgb = rgb.copy()
rgb[~valid] = np.array(bg, dtype=np.uint8)
return rgb, ranges
def canonical_colors(canonical_values: np.ndarray, ranges) -> np.ndarray:
"""RGB colors (N, 3) uint8 for flat canonical coords given fixed ranges."""
canonical_values = np.asarray(canonical_values, dtype=np.float32)
out = np.zeros_like(canonical_values)
for c in range(3):
a, b = ranges[c]
out[:, c] = np.clip((canonical_values[:, c] - a) / (b - a), 0, 1)
return (out * 255).astype(np.uint8)
def hsv_palette(n: int) -> np.ndarray:
"""A palette of ``n`` distinct bright RGB colors (uint8) via the HSV wheel."""
import colorsys
cols = []
for i in range(n):
r, g, b = colorsys.hsv_to_rgb(i / max(n, 1), 1.0, 1.0)
cols.append([int(r * 255), int(g * 255), int(b * 255)])
return np.array(cols, dtype=np.uint8)