Files
malkuthro ae7abba73b docs: refresh shipped help and tooltips (#249)
* docs: refresh shipped help and tooltips

* docs: add snapshot transfer guide section

* docs: refresh guide screenshots
2026-06-19 01:01:30 +02:00

107 lines
3.7 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import List
COLUMN_COUNT = 19
DEFAULT_POSE_WIDTH = 1280.0
DEFAULT_POSE_HEIGHT = 720.0
def _parse_pose_line(line: str, line_number: int) -> List[float]:
tokens = line.strip().split()
if len(tokens) != COLUMN_COUNT:
raise ValueError(
f"Line {line_number} expected {COLUMN_COUNT} values but found {len(tokens)}."
)
try:
return [float(token) for token in tokens]
except ValueError as exc:
raise ValueError(f"Line {line_number} contains a non-numeric value.") from exc
class KoolookLoadCameraPosesAbsolute:
"""
Loads RealEstate10k-style camera pose TXT files from any absolute path without ComfyUI's
built-in strip_path restrictions. The file format matches the AnimateDiff CameraCtrl loader:
line 1 is metadata, and each following line contains 19 space-separated floats describing
timestamp, intrinsics, original width/height placeholders, and the flattened 3x4 pose.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"file_path": (
"STRING",
{
"default": "input/poses/example_track.txt",
"multiline": False,
"tooltip": "Absolute or user-expanded path to a RealEstate10k-style CameraCtrl pose TXT file.",
},
),
},
"optional": {
"pose_width": (
"FLOAT",
{
"default": DEFAULT_POSE_WIDTH,
"min": 1.0,
"max": 16384.0,
"step": 1.0,
"tooltip": "Width value written into each pose row before returning CAMERACTRL_POSES.",
},
),
"pose_height": (
"FLOAT",
{
"default": DEFAULT_POSE_HEIGHT,
"min": 1.0,
"max": 16384.0,
"step": 1.0,
"tooltip": "Height value written into each pose row before returning CAMERACTRL_POSES.",
},
),
},
}
RETURN_TYPES = ("CAMERACTRL_POSES",)
FUNCTION = "load_camera_poses"
CATEGORY = "Koolook/Camera"
DESCRIPTION = "Load CameraCtrl poses from a RealEstate10k-style TXT file located anywhere on disk."
def load_camera_poses(self, file_path: str, pose_width: float = DEFAULT_POSE_WIDTH, pose_height: float = DEFAULT_POSE_HEIGHT):
normalized_path = Path(file_path).expanduser()
if not normalized_path.is_file():
raise FileNotFoundError(f"Pose file not found: {normalized_path}")
with normalized_path.open("r", encoding="utf-8") as fh:
lines = fh.read().splitlines()
if len(lines) < 2:
raise ValueError("Pose file is missing data rows below the header/metadata line.")
pose_rows = []
for idx, raw_line in enumerate(lines[1:], start=2):
stripped = raw_line.strip()
if not stripped:
continue # allow blank separator lines
values = _parse_pose_line(stripped, idx)
values[5] = pose_width
values[6] = pose_height
pose_rows.append(values)
if not pose_rows:
raise ValueError("Pose file did not contain any valid pose rows.")
return (pose_rows,)
NODE_CLASS_MAPPINGS = {
"KoolookLoadCameraPosesAbsolute": KoolookLoadCameraPosesAbsolute,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"KoolookLoadCameraPosesAbsolute": "Koolook Load Camera Poses (Absolute Path)",
}