Some cleanup

This commit is contained in:
kijai 2026-06-01 00:44:15 +03:00
parent 84ae698281
commit ebd9c6e620
2 changed files with 54 additions and 97 deletions

View File

@ -144,8 +144,7 @@ class SAM3DBody_Predict(io.ComfyNode):
"bboxes", optional=True, force_input=True,
tooltip=(
"Per-frame person boxes (e.g. RT-DETR Detect with class_name='person'). "
"Used when no SAM3 track is wired — gives the top-down model a tight, "
"person-centered crop. Multi-person supported (one box = one person)."
"Use for better detection as alternative to SAM3 tracks."
),
),
io.Boolean.Input(
@ -154,7 +153,7 @@ class SAM3DBody_Predict(io.ComfyNode):
io.Float.Input(
"fov_degrees",
default=0.0, min=0.0, max=170.0, step=0.5,
tooltip=(
tooltip=( #TODO: get FoV from moge another way?
"Vertical FOV in degrees. Affects predicted depth (cam_t.z) and "
"absolute scale. 0 = use moge_geometry or fall back to ~53° (16:9). "
"Any non-zero value overrides moge_geometry."
@ -164,7 +163,7 @@ class SAM3DBody_Predict(io.ComfyNode):
"moge_geometry",
optional=True,
tooltip=(
"MoGe geometry (from MoGeInference), used to calculate camera field of view."
"MoGe geometry, used to calculate camera field of view."
"For batches choose the most representative frame, or leave unset"
),
),
@ -345,10 +344,9 @@ class SAM3DBody_FaceExpression(io.ComfyNode):
crop_factor = 1.2
# Auto-pick full-frame vs per-person crops. BlazeFace full-range needs
# ≥32px face in its 192px input; below that we escalate to per-person
# crops. Face height ≈ 20% of body-bbox height (rough but stable).
# ≥32px face in its 192px input; below that we escalate to per-person crops
H_img0, W_img0 = img_np.shape[1], img_np.shape[2]
min_bbox_px = 32.0 * max(H_img0, W_img0) / (192.0 * 0.20)
min_bbox_px = 32.0 * max(H_img0, W_img0) / (192.0 * 0.20) # Face height ≈ 20% of body-bbox height.
use_per_person_crops = any(
(p["bbox"][3] - p["bbox"][1]) < min_bbox_px
for persons in new_frames for p in persons
@ -400,10 +398,9 @@ class SAM3DBody_FaceExpression(io.ComfyNode):
pbar.update(1)
# Baseline subtraction. MP has subject-specific rest bias (e.g.
# naturally-raised brow at 0.15); without subtraction, strength
# multipliers bake that into every frame. Per-clip needs ~30 frames
# or it would zero out the expression.
# Baseline subtraction. MP has subject-specific rest bias, without subtraction, strength
# multipliers bake that into every frame.
# Per-clip needs ~30 frames or it would zero out the expression.
BASELINE_MIN_FRAMES = 30
if n_total_frames_with_persons >= BASELINE_MIN_FRAMES:
for pid in range(max_persons):
@ -417,7 +414,7 @@ class SAM3DBody_FaceExpression(io.ComfyNode):
f"got {n_total_frames_with_persons}. Skipping subtraction."
)
# Smooth raw signal AFTER baseline subtraction but BEFORE gap fill
# Smooth raw signal AFTER baseline subtraction but BEFORE gap fill
# MP's per-frame noise gets averaged out at the source.
bs_win = int(blendshape_smooth_window)
if bs_win > 1:
@ -484,9 +481,8 @@ class SAM3DBody_Smooth(io.ComfyNode):
options=["gaussian", "savgol"],
default="gaussian", advanced=True,
tooltip=(
"'gaussian': symmetric weighted average — phase-preserving "
"(no time-shift), best general-purpose smoother. "
"'savgol': sliding polynomial fit — preserves sharp peaks "
"'gaussian': symmetric weighted average, best general-purpose smoother. "
"'savgol': sliding polynomial fit, preserves sharp peaks."
),
),
io.Int.Input(
@ -498,10 +494,9 @@ class SAM3DBody_Smooth(io.ComfyNode):
"rotation_threshold_deg",
default=15.0, min=0.0, max=45.0, step=1.0, advanced=True,
tooltip=(
"Geometry smoothing drops to RAW above this root-rotation "
"rate (deg/frame) to preserve fast spins. 15° suits most "
"content; low values trigger on ordinary jitter and "
"silently sabotage smoothing. 0 = disable backoff."
"Disables smoothing for this root-rotation rate (deg/frame) to preserve fast spins. "
"15° suits most content, low values trigger on ordinary jitter and "
"silently sabotage smoothing. 0 = disable."
),
),
],
@ -908,12 +903,10 @@ class SAM3DBody_Render(io.ComfyNode):
],
tooltip=(
"'mesh' = 3D MHR mesh rasterized through the camera. "
"'silhouette' = binary mask of the mesh (white-on-black, "
"background ignored). 'openpose_2d' = flat 2D skeleton "
"from pred_keypoints_2d (DWPose look, ControlNet-ready). "
"'openpose_3d' = same skeleton as flat-shaded 3D capsules "
"(camera-aware, proper depth). 'scail' = SCAIL 3D capsules "
"via torch SDF ray-march (proper occlusion / depth)."
"'silhouette' = binary mask of the mesh. "
"'openpose_2d' = flat 2D skeleton "
"'openpose_3d' = openpose skeleton as flat-shaded 3D model "
"'scail' = SCAIL 3D capsules "
),
),
],

View File

@ -425,6 +425,21 @@ def rainbow_tilt_inputs():
]
def camera_translation_input():
"""Shared camera_translation combo (BuildPoseGLB + SavePoseBVH)."""
return IO.Combo.Input(
"camera_translation",
options=["off", "centered", "absolute"],
default="off",
tooltip=(
"Bake pred_cam_t into the root's translation "
"'off' = bind position "
"'centered' = delta from frame 0 "
"'absolute' = raw (Z is camera depth — usually meters away)."
),
)
class BuildPoseGLB(IO.ComfyNode):
"""Convert pose_data to an in-memory animated GLB"""
@ -438,24 +453,13 @@ class BuildPoseGLB(IO.ComfyNode):
inputs=[
IO.MultiType.Input(
"pose_data", types=[MHRPoseData, KimodoPoseData],
tooltip=(
"MHR pose data from SAM3DBody_Predict, or external-rig "
"pose data from Kimodo (`_skeleton_override`-augmented)."
),
tooltip=("MHR pose data from SAM3DBody_Predict, Kimodo. "),
),
SAM3DBodyModel.Input("sam3d_body_model", optional=True),
IO.DynamicCombo.Input(
"mesh_style",
options=[
IO.DynamicCombo.Option("body_mesh", [
IO.Int.Input(
"bone_smooth_window",
default=0, min=0, max=51, step=2,
tooltip=(
"Gaussian window on per-bone rotation keyframes. 0 = off. "
"7-15 helps cartwheels/spins where upstream Smooth misses spikes."
),
),
IO.DynamicCombo.Input(
"bone_vis",
options=[
@ -485,11 +489,7 @@ class BuildPoseGLB(IO.ComfyNode):
),
]),
],
tooltip=(
"Bone vis shape, rigidly skinned to each joint. "
"'octahedrons' = Blender-style directional bones (joint → "
"primary child); 'sticks' = thin lines."
),
tooltip=("Bone vis shape, rigidly skinned to each joint. "),
),
IO.DynamicCombo.Input(
"shader",
@ -500,7 +500,7 @@ class BuildPoseGLB(IO.ComfyNode):
IO.Float.Input(
"person_palette_falloff",
default=0.6, min=0.1, max=1.0, step=0.05,
tooltip="Per-person desaturation: track k gets (1 - falloff^k) pastel mix.",
tooltip="Per-person desaturation: each track gets (1 - falloff^k) pastel mix.",
),
]),
IO.DynamicCombo.Option("rainbow_face_normal", [
@ -508,7 +508,7 @@ class BuildPoseGLB(IO.ComfyNode):
IO.Float.Input(
"person_palette_falloff",
default=0.6, min=0.1, max=1.0, step=0.05,
tooltip="Per-person desaturation: track k gets (1 - falloff^k) pastel mix.",
tooltip="Per-person desaturation: each track gets (1 - falloff^k) pastel mix.",
),
]),
IO.DynamicCombo.Option("rainbow_face_semantic", [
@ -516,7 +516,7 @@ class BuildPoseGLB(IO.ComfyNode):
IO.Float.Input(
"person_palette_falloff",
default=0.6, min=0.1, max=1.0, step=0.05,
tooltip="Per-person desaturation: track k gets (1 - falloff^k) pastel mix.",
tooltip="Per-person desaturation: each track gets (1 - falloff^k) pastel mix.",
),
]),
],
@ -527,14 +527,6 @@ class BuildPoseGLB(IO.ComfyNode):
),
]),
IO.DynamicCombo.Option("bones_only", [
IO.Int.Input(
"bone_smooth_window",
default=0, min=0, max=51, step=2,
tooltip=(
"Gaussian window on per-bone rotation keyframes. 0 = off. "
"7-15 helps cartwheels/spins where upstream Smooth misses spikes."
),
),
IO.DynamicCombo.Input(
"bone_vis",
options=[
@ -571,14 +563,6 @@ class BuildPoseGLB(IO.ComfyNode):
),
]),
IO.DynamicCombo.Option("openpose", [
IO.Int.Input(
"bone_smooth_window",
default=0, min=0, max=51, step=2,
tooltip=(
"Gaussian window on keypoint tracks. 0 = off. "
"7-15 calms jitter where upstream Smooth misses spikes."
),
),
IO.Float.Input(
"marker_radius_m", default=0.010, min=0.005, max=0.1, step=0.001, advanced=True,
tooltip="Sphere radius in m.",
@ -618,14 +602,6 @@ class BuildPoseGLB(IO.ComfyNode):
),
]),
IO.DynamicCombo.Option("scail", [
IO.Int.Input(
"bone_smooth_window",
default=0, min=0, max=51, step=2,
tooltip=(
"Gaussian window on keypoint tracks. 0 = off. "
"7-15 calms jitter where upstream Smooth misses spikes."
),
),
IO.Float.Input(
"stick_radius_m", default=0.022, min=0.002, max=0.1, step=0.001, advanced=True,
tooltip=(
@ -667,28 +643,25 @@ class BuildPoseGLB(IO.ComfyNode):
]),
],
tooltip=(
"'body_mesh' = real Armature (127 bones, skinning, TRS "
"keyframes, 72 face morphs; needs model). "
"'body_mesh' = real Armature (127 bones, skinning, TRS keyframes, 72 face morphs; needs model). "
"'bones_only' = bone-shape primitives at each joint (preview armature). "
"'openpose' = OpenPose-18 3D skeleton from keypoints "
"(no model needed). 'scail' = SCAIL 3D capsule rig (open "
"cylinders capped flush by joint spheres)."
"'scail' = SCAIL 3D capsule rig (open cylinders capped flush by joint spheres)."
),
),
IO.Int.Input(
"bone_smooth_window",
default=0, min=0, max=51, step=2,
tooltip=(
"Gaussian smoothing window on per-bone rotation keyframes / keypoint "
"tracks. 0 = off. 7-15 calms spins/jitter where upstream Smooth misses spikes."
),
),
IO.Float.Input(
"fps", default=24.0, min=1.0, max=240.0, step=1.0,
tooltip="Animation frame rate.",
),
IO.Combo.Input(
"camera_translation",
options=["off", "centered", "absolute"],
default="off",
tooltip=(
"Bake pred_cam_t into per-track root translation. "
"'off' = origin; 'centered' = delta from frame 0; "
"'absolute' = raw (Z is camera depth — usually meters away)."
),
),
camera_translation_input(),
IO.Int.Input(
"track_index", default=-1, min=-1, max=15,
tooltip="-1 = all tracks; ≥0 = single track.",
@ -698,7 +671,7 @@ class BuildPoseGLB(IO.ComfyNode):
)
@classmethod
def execute(cls, pose_data, mesh_style, sam3d_body_model=None, fps=24.0, camera_translation="off", track_index=-1) -> IO.NodeOutput:
def execute(cls, pose_data, mesh_style, sam3d_body_model=None, bone_smooth_window=0, fps=24.0, camera_translation="off", track_index=-1) -> IO.NodeOutput:
mesh_style = mesh_style or {"mesh_style": "body_mesh"}
mode_key = mesh_style["mesh_style"]
# `shader` is nested in body_mesh; absent for bones_only.
@ -730,7 +703,7 @@ class BuildPoseGLB(IO.ComfyNode):
bone_vis_color = str(bone_vis_dict.get("bone_vis_color", "white"))
glb_bytes = build_glb_skeletal(
pose_data, sam3d_body_model,
bone_smooth_window=int(mesh_style.get("bone_smooth_window", 0)),
bone_smooth_window=int(bone_smooth_window),
bone_vis=bone_vis,
bone_vis_radius_m=bone_vis_radius_m,
bone_vis_color=bone_vis_color,
@ -754,7 +727,7 @@ class BuildPoseGLB(IO.ComfyNode):
face_marker_radius_m=float(mesh_style.get("face_marker_radius_m", 0.0)),
palette="openpose",
shape="ellipsoid",
bone_smooth_window=int(mesh_style.get("bone_smooth_window", 0)),
bone_smooth_window=int(bone_smooth_window),
)
elif mode_key == "scail":
# SCAIL rig: open cylinders capped flush by joint spheres (sphere
@ -781,7 +754,7 @@ class BuildPoseGLB(IO.ComfyNode):
# inside of the open cylinders shades sensibly at grazing angles.
material_roughness=float(mesh_style.get("material_roughness", 0.3)),
material_double_sided=True,
bone_smooth_window=int(mesh_style.get("bone_smooth_window", 0)),
bone_smooth_window=int(bone_smooth_window),
)
else:
raise ValueError(f"BuildPoseGLB: unknown mesh_style {mode_key!r}")
@ -813,16 +786,7 @@ class SavePoseBVH(IO.ComfyNode):
"fps", default=24.0, min=1.0, max=240.0, step=1.0,
tooltip="Animation frame rate (BVH `Frame Time`).",
),
IO.Combo.Input(
"camera_translation",
options=["off", "centered", "absolute"],
default="off",
tooltip=(
"Bake pred_cam_t into the root's position channels. "
"'off' = bind position; 'centered' = delta from frame 0; "
"'absolute' = raw (Z is camera depth — usually meters away)."
),
),
camera_translation_input(),
IO.Combo.Input(
"units",
options=["cm", "m"],