This commit is contained in:
Brett Emaus
2026-02-20 13:45:47 -05:00
parent dc791c542f
commit 2ac58a91bf
2 changed files with 45 additions and 13 deletions
+2 -6
View File
@@ -27,8 +27,6 @@ class OpenPoseRotator:
"optional": {
"pose_keypoint": ("POSE_KEYPOINT",),
"debug": ("BOOLEAN", {"default": False}),
"perspective": ("FLOAT", {"default": 0.0, "min": -0.5, "max": 0.5, "step": 0.01}),
"focal_length": ("FLOAT", {"default": 800.0, "min": 100.0, "max": 5000.0, "step": 50.0}),
},
}
@@ -40,11 +38,11 @@ class OpenPoseRotator:
mode: str,
pose_keypoint: list | None = None,
debug: bool = False,
perspective: float = 0.0,
focal_length: float = 800.0,
) -> tuple[torch.Tensor, list]:
"""
Rotate OpenPose figure(s) around torso. Processes batch of images.
Advanced mode uses a fixed 35° camera elevation and ~90% figure fill to
automatically derive perspective and focal-length — no manual tuning needed.
Returns (image_tensor, rotated_pose_keypoints_list).
"""
batch_size = image.shape[0]
@@ -72,8 +70,6 @@ class OpenPoseRotator:
image_index=i,
debug=debug,
mode=mode,
perspective=perspective,
focal_length=focal_length,
)
if not success:
+43 -7
View File
@@ -405,11 +405,17 @@ def _compute_adaptive_depth_scale(
return 0.4 * (120.0 / shoulder_width)
# Perspective scale_y: (cy - y) is in pixels; this scales the term so perspective has visible effect.
# At 0.3, perspective=0.5 with a point 200px above pivot contributes 30 z-units, shifting x by
# ~5px at 10° and ~21px at 45° — clearly perceptible without being overwhelming.
# Perspective scale_y: bridges the raw (cy - y) pixel value to z-units.
# In advanced mode the coefficient is derived geometrically from ADVANCED_CAMERA_ELEVATION_DEG;
# this constant keeps the formula consistent: z += perspective * (cy - y) * PERSPECTIVE_SCALE_Y.
PERSPECTIVE_SCALE_Y = 0.3
# Advanced mode: fixed physical camera / figure assumptions.
# Camera is elevated 35° above horizontal, looking at a standing figure that fills
# roughly 80-100% (average ~90%) of the image height.
ADVANCED_CAMERA_ELEVATION_DEG = 35.0
ADVANCED_FIGURE_FILL = 0.90
# Ankle y-parity threshold: fraction of figure height (neck→lower ankle) within which both ankles
# are considered to be at the same elevation (standing upright on flat ground).
UPRIGHT_ANKLE_THRESHOLD = 0.08
@@ -455,6 +461,28 @@ def _detect_upright_figure(
return True, neck, ankle_mid
def _compute_advanced_params(image_height: int) -> tuple[float, float]:
"""
Derive perspective and focal_length for advanced mode from fixed camera/figure assumptions.
Camera elevation θ = ADVANCED_CAMERA_ELEVATION_DEG (35°) above horizontal.
Figure fills ADVANCED_FIGURE_FILL (~90%) of image height.
perspective: maps vertical pixel distance from pivot to depth.
Formula: z += perspective * (cy - y) * PERSPECTIVE_SCALE_Y
For geometric accuracy: perspective * PERSPECTIVE_SCALE_Y = tan(θ)
→ perspective = tan(35°) / 0.3 ≈ 2.33
focal_length: perspective projection strength, scaled to image resolution.
From pinhole geometry: focal_length = image_height * fill / tan(θ)
At 35° with 90% fill → focal_length ≈ 1.29 × image_height
"""
tan_elev = math.tan(math.radians(ADVANCED_CAMERA_ELEVATION_DEG))
perspective = tan_elev / PERSPECTIVE_SCALE_Y
focal_length = image_height * ADVANCED_FIGURE_FILL / tan_elev
return perspective, focal_length
def rotate_keypoints_3d(
keypoints: dict[str, list[tuple[float, float, float]]],
pivot: tuple[float, float],
@@ -834,19 +862,27 @@ def rotate_openpose(
image_index: int = 0,
debug: bool = False,
mode: str = "simple",
perspective: float = 0.0,
focal_length: float = 800.0,
) -> tuple[np.ndarray, bool, dict[str, list[tuple[float, float, float]]] | None]:
"""
Main pipeline: extract/parse keypoints, detect torso, rotate, render.
Returns (output_image, success, rotated_keypoints). On failure, returns (input_image, False, None).
rotated_keypoints is internal format: {"body": [(x,y,c),...], "hand_left": [...], ...}
image_index: used for console logging when processing batches.
mode: "simple" or "advanced". perspective: 0=eye level, >0=camera above, <0=camera below.
focal_length: (advanced only) perspective strength; higher=flatter.
mode: "simple" uses orthographic projection at eye level (perspective=0).
"advanced" uses perspective projection with camera parameters derived from
ADVANCED_CAMERA_ELEVATION_DEG (35°) and ADVANCED_FIGURE_FILL (90%), which
gives geometrically correct depth inference and focal length for the assumed pose.
"""
h, w = image.shape[:2]
# Derive camera parameters from mode and fixed assumptions
if mode == "advanced":
perspective, focal_length = _compute_advanced_params(h)
else:
perspective = 0.0
focal_length = 800.0 # unused in simple (orthographic) mode
# Get keypoints
if pose_keypoint is not None:
keypoints = parse_pose_keypoint(pose_keypoint)