diff --git a/nuke_CAM_exporter/FIXES_AND_IMPROVEMENTS.md b/nuke_CAM_exporter/FIXES_AND_IMPROVEMENTS.md deleted file mode 100644 index 66b57aa..0000000 --- a/nuke_CAM_exporter/FIXES_AND_IMPROVEMENTS.md +++ /dev/null @@ -1,208 +0,0 @@ -# Camera Conversion Pipeline - Fixes and Improvements - -## Summary - -This document outlines the issues found and fixes applied to the Nuke → ComfyUI camera conversion pipeline. - -## Critical Issue Found: Missing Coordinate System Conversion - -### Problem -The converter was **not converting from Nuke's Y-up coordinate system to OpenCV's Y-down coordinate system**. This causes camera motion to appear incorrect in ComfyUI, as the coordinate axes don't match what the Wan model expects. - -### Root Cause -- **Nuke uses**: +X right, +Y up, +Z forward -- **OpenCV/RealEstate10k uses**: +X right, +Y down, +Z forward -- The converter was building rotation matrices in Nuke's coordinate system but not transforming them to OpenCV's system - -### Fix Applied -Added `convert_yup_to_ydown()` function that properly transforms rotation matrices: - -```python -def convert_yup_to_ydown(rotation: List[List[float]]) -> List[List[float]]: - """ - Convert rotation matrix from Nuke's Y-up coordinate system to OpenCV's Y-down. - - Transformation: R_opencv = T * R_nuke * T, where T = diag(1, -1, 1) - This negates the Y row and Y column of the rotation matrix. - """ - r = rotation - return [ - [r[0][0], -r[0][1], r[0][2]], # X row: keep X, negate Y, keep Z - [-r[1][0], r[1][1], -r[1][2]], # Y row: negate X, keep Y, negate Z - [r[2][0], -r[2][1], r[2][2]], # Z row: keep X, negate Y, keep Z - ] -``` - -This function is now called in `main()` before computing translations and writing pose data. - -### Impact -- **Before**: Camera motion would be flipped/mirrored on the Y axis -- **After**: Camera motion matches the original Nuke camera path - -## Code Improvements - -### 1. Added Documentation -- Added docstrings to key functions explaining coordinate system transformations -- Clarified the purpose of `camera_center_to_translation()` function - -### 2. Improved Code Structure -- Separated coordinate system conversion into its own function -- Made the transformation explicit and testable - -## Testing Tools Created - -### 1. `test_nuke_scene.py` -Generates a Nuke script (.nk) with: -- A simple camera animation (forward dolly + pan) -- Known lens parameters (35mm focal, 36x24mm filmback) -- Reference geometry (grid) for visualization -- Easy to validate motion - -**Usage:** -```bash -python test_nuke_scene.py --output test_scene.nk --frames 50 -``` - -### 2. `validate_conversion.py` -Validates the conversion by: -- Reading both ASCI input and pose output -- Checking rotation matrix validity (orthogonality, determinant = 1) -- Verifying coordinate system conversion (Y-up → Y-down) -- Validating translation conversion (camera center → world-to-camera) -- Reporting any issues found - -**Usage:** -```bash -python validate_conversion.py inputs/camera.asci outputs/camera_converted.txt --unit-scale 0.1 -``` - -### 3. `test_pipeline.sh` -Automated test script that runs the full pipeline: -1. Generates test scene -2. (Manual) Export from Nuke -3. Converts ASCI to pose -4. Validates conversion - -### 4. `README_TESTING.md` -Comprehensive testing guide with: -- Step-by-step workflow -- Common issues and fixes -- Integration instructions for ComfyUI - -## How to Test the Fix - -### Quick Test -1. **Generate test scene:** - ```bash - python test_nuke_scene.py --output test_scene.nk - ``` - -2. **Export camera from Nuke:** - - Open `test_scene.nk` in Nuke - - Select Camera2 node - - Export Ascii with columns: `[translate.x, rotate.x, translate.y, rotate.y, translate.z, rotate.z]` - - Save as `inputs/test_camera.asci` - -3. **Convert:** - ```bash - python nuke_ASCI-2-Pose_converter.py \ - inputs/test_camera.asci \ - outputs/test_camera_converted.txt \ - --fps 25.0 \ - --focal-length-mm 35.0 \ - --sensor-width-mm 36.0 \ - --sensor-height-mm 24.0 \ - --width 1920 \ - --height 1080 \ - --unit-scale 1.0 - ``` - -4. **Validate:** - ```bash - python validate_conversion.py \ - inputs/test_camera.asci \ - outputs/test_camera_converted.txt \ - --unit-scale 1.0 - ``` - -### Test with Existing Data -To test with your existing `camTrack_1.7m_v02.asci`: - -```bash -# Convert with fix -python nuke_ASCI-2-Pose_converter.py \ - inputs/camTrack_1.7m_v02.asci \ - outputs/camTrack_1.7m_v02_converted_FIXED.txt \ - --config configs/camera_config_v01.json - -# Validate -python validate_conversion.py \ - inputs/camTrack_1.7m_v02.asci \ - outputs/camTrack_1.7m_v02_converted_FIXED.txt \ - --unit-scale 0.1 - -# Compare with old output -python validate_conversion.py \ - inputs/camTrack_1.7m_v02.asci \ - outputs/camTrack_1.7m_v02_converted.txt \ - --unit-scale 0.1 -``` - -## Expected Results - -### Before Fix -- Validation would show coordinate system conversion warnings -- Camera motion in ComfyUI would be flipped/mirrored on Y axis -- Rotation matrices would be in Nuke's coordinate system - -### After Fix -- Validation should pass coordinate system checks -- Camera motion should match original Nuke camera path -- Rotation matrices properly converted to OpenCV coordinate system -- Translation values correctly computed using converted rotation matrix - -## Additional Considerations - -### Unit Scale -The `unit_scale` parameter is critical for correct motion: -- Measure real-world distance traveled by camera -- Calculate: `unit_scale = real_distance_meters / asci_delta` -- Example: Camera moves 1.7m, ASCI Tz delta is 17.0 → `unit_scale = 0.1` - -### Lens Parameters -Ensure these match your Nuke camera: -- `--focal-length-mm`: Focal length in millimeters -- `--sensor-width-mm`: Horizontal filmback/aperture -- `--sensor-height-mm`: Vertical filmback/aperture - -### Frame Range -Match the frame range between: -- Nuke ASCI export -- ComfyUI render -- Use `--start-frame` and `--end-frame` if needed - -## Next Steps - -1. **Test the fix** with your existing data -2. **Validate** using the validation script -3. **Compare** old vs new output in ComfyUI -4. **Adjust** `unit_scale` if motion scale is still off -5. **Verify** lens parameters match production data - -## Files Modified - -- `nuke_ASCI-2-Pose_converter.py`: Added coordinate system conversion -- `test_nuke_scene.py`: New test scene generator -- `validate_conversion.py`: New validation tool -- `test_pipeline.sh`: New automated test script -- `README_TESTING.md`: New testing documentation - -## Questions? - -If you encounter issues: -1. Run `validate_conversion.py` to identify problems -2. Check `README_TESTING.md` for common issues -3. Verify `unit_scale` calculation -4. Ensure lens parameters match Nuke camera settings - - diff --git a/nuke_CAM_exporter/README_TESTING.md b/nuke_CAM_exporter/README_TESTING.md deleted file mode 100644 index 5873ae4..0000000 --- a/nuke_CAM_exporter/README_TESTING.md +++ /dev/null @@ -1,155 +0,0 @@ -# Testing the Camera Conversion Pipeline - -This document explains how to test and validate the Nuke → ComfyUI camera conversion pipeline. - -## Quick Test Workflow - -### 1. Generate Test Scene - -```bash -python test_nuke_scene.py --output test_scene.nk --frames 81 -``` - -This creates a Nuke script with: -- A camera that moves forward (dollies in) from Z=-5 to Z=-3 -- A slight pan (Y rotation from -10° to -15°) -- Known lens parameters (35mm focal, 36x24mm filmback) -- A reference grid for visualization - -### 2. Export Camera from Nuke - -1. Open `test_scene.nk` in Nuke -2. Select the `Camera2` node -3. Right-click → **Export Ascii** -4. Configure export: - - **Columns**: `translate.x`, `rotate.x`, `translate.y`, `rotate.y`, `translate.z`, `rotate.z` - - **Rotation order**: ZXY (Nuke's default) - - **Rotation units**: Degrees -5. Save as `inputs/test_camera.asci` - -### 3. Convert to Pose Format - -```bash -python nuke_ASCI-2-Pose_converter.py \ - inputs/test_camera.asci \ - outputs/test_camera_converted.txt \ - --fps 25.0 \ - --focal-length-mm 35.0 \ - --sensor-width-mm 36.0 \ - --sensor-height-mm 24.0 \ - --width 1920 \ - --height 1080 \ - --unit-scale 1.0 -``` - -Or use a config file: - -```bash -python nuke_ASCI-2-Pose_converter.py --config configs/test_camera.json -``` - -### 4. Validate Conversion - -```bash -python validate_conversion.py \ - inputs/test_camera.asci \ - outputs/test_camera_converted.txt \ - --unit-scale 1.0 -``` - -This script checks: -- Rotation matrix validity (orthogonality, determinant = 1) -- Coordinate system conversion (Y-up → Y-down) -- Translation conversion (camera center → world-to-camera translation) -- Frame count consistency - -## Common Issues and Fixes - -### Issue: Camera motion doesn't match original plate - -**Possible causes:** - -1. **Missing coordinate system conversion** - - ✅ Fixed: The converter now includes `convert_yup_to_ydown()` function - - Check validation output for coordinate system warnings - -2. **Camera solver rotated 90° (or any large rotation)** - - **This is normal!** Nuke's camera solver may orient the camera with large rotations (e.g., 90° Y-axis) - - The converter preserves all rotations correctly, including 90° rotations - - Use `python debug_rotation.py inputs/your_camera.asci` to analyze rotations - - If motion still doesn't match, check `unit_scale` and lens parameters (see below) - -3. **Incorrect unit_scale** - - Measure real-world distance traveled by camera - - Calculate: `unit_scale = real_distance_meters / asci_delta` - - Example: Camera moves 1.7m, ASCI Tz delta is 17.0 → `unit_scale = 0.1` - -4. **Wrong lens parameters** - - Verify focal length matches Nuke camera settings - - Check sensor size (filmback) matches camera node - - Use `--focal-length-mm` and `--sensor-width-mm` / `--sensor-height-mm` flags - -5. **Frame range mismatch** - - Ensure ASCI export includes all frames you want - - Use `--start-frame` and `--end-frame` to slice if needed - -### Issue: Validation shows coordinate system errors - -If `validate_conversion.py` reports coordinate system issues: - -1. Check that `convert_yup_to_ydown()` is being called in the converter -2. Verify the Y-row and Y-column negation pattern: - - `R_opencv[1,0] ≈ -R_nuke[1,0]` - - `R_opencv[1,2] ≈ -R_nuke[1,2]` - - `R_opencv[0,1] ≈ -R_nuke[0,1]` - - `R_opencv[2,1] ≈ -R_nuke[2,1]` - -### Issue: Translation values look wrong - -The converter uses `t = -R * C` where: -- `R` is the rotation matrix (after Y-up→Y-down conversion) -- `C` is the camera center scaled by `unit_scale` - -If translations are off: -1. Verify `unit_scale` is correct -2. Check that camera center values from ASCI are being read correctly -3. Ensure rotation matrix is valid (orthogonal, det=1) - -## Testing with Real Production Data - -For production shots: - -1. **Measure reference distance** - - Use CameraTracker's "Set Reference Distance" feature - - Or measure a known object size in the scene - - Calculate `unit_scale` from this measurement - -2. **Verify lens metadata** - - Get focal length from camera report or EXIF data - - Get sensor size from camera specs (e.g., Full Frame = 36x24mm) - -3. **Export with correct frame range** - - Match the frame range you'll use in ComfyUI - - Use `--start-frame` and `--end-frame` to match exactly - -4. **Validate before using in ComfyUI** - - Run `validate_conversion.py` to catch issues early - - Check that rotation matrices are valid - - Verify coordinate system conversion - -## Integration with ComfyUI - -After conversion: - -1. Load the pose file in ComfyUI using `Koolook Load Camera Poses` -2. Connect to `WanVideo FunCamera Embeds` -3. Set render resolution to match your pose file's width/height -4. Test with a simple prompt first to verify motion - -If motion still doesn't match: -- Double-check `unit_scale` calculation -- Verify lens parameters match production data -- Check that ComfyUI render resolution matches pose file resolution -- Consider using camera projection in Nuke to validate the converted poses - - diff --git a/nuke_CAM_exporter/ROTATION_ANALYSIS.md b/nuke_CAM_exporter/ROTATION_ANALYSIS.md deleted file mode 100644 index c3f9e6e..0000000 --- a/nuke_CAM_exporter/ROTATION_ANALYSIS.md +++ /dev/null @@ -1,89 +0,0 @@ -# Understanding Camera Rotations and Coordinate Systems - -## The 90° Y-Axis Rotation Question - -If you notice that your solved camera has a **90° rotation on the Y-axis** (or any large rotation), this is **normal and expected**. Here's why: - -### Why Nuke Camera Solver May Rotate the Camera - -1. **Ambiguity in camera solving**: Camera tracking has inherent ambiguities - the solver can't always determine the absolute world orientation -2. **Coordinate system choice**: The solver picks a convenient coordinate system that may not align with your scene's "natural" orientation -3. **Plate orientation**: If your plate is rotated (e.g., portrait vs landscape), the solver may rotate the camera to match - -### Does This Affect the Conversion? - -**No!** The converter handles **any rotation correctly**, including: -- 90° rotations -- 180° rotations -- Arbitrary rotations - -The coordinate system conversion (`convert_yup_to_ydown`) is a **linear transformation** that preserves all rotations correctly. It only changes how the coordinate system is expressed (Y-up → Y-down), not the actual camera orientation. - -### How to Verify Rotations Are Correct - -Use the debug tool to analyze your camera rotations: - -```bash -python debug_rotation.py inputs/your_camera.asci --frames 10 -``` - -This will show: -- Rotation values for each frame -- Whether Y rotation is near ±90° (warns if so) -- Forward direction vectors (should be preserved after conversion) -- Rotation matrix determinants (should be ~1.0) - -### Test 90° Rotation Handling - -To verify the converter handles 90° rotations correctly: - -```bash -python debug_rotation.py inputs/your_camera.asci --test-90deg -``` - -This runs a specific test case with a 90° Y rotation to ensure the conversion preserves it correctly. - -### What If Motion Still Doesn't Match? - -If your camera has a 90° rotation but motion still doesn't match in ComfyUI, check: - -1. **Unit scale** - This is the most common issue - ```bash - # Measure real distance, calculate scale - unit_scale = real_distance_meters / asci_translation_delta - ``` - -2. **Lens parameters** - Must match Nuke camera exactly - - Focal length (mm) - - Sensor width/height (mm) - -3. **Coordinate system** - The converter handles this, but verify: - ```bash - python validate_conversion.py inputs/camera.asci outputs/camera_converted.txt --unit-scale 0.1 - ``` - -4. **Frame range** - Ensure you're comparing the same frames - -### Example: Analyzing Your Camera - -If your camera has rotations like: -``` -Frame 1: Y rotation = -102.4° -Frame 2: Y rotation = -102.6° -``` - -This is fine! The converter will: -1. Convert Euler angles → rotation matrix (preserving the -102° rotation) -2. Convert coordinate system (Y-up → Y-down) -3. Compute world-to-camera translation - -The **-102° rotation is preserved** - it's just expressed in OpenCV's coordinate system instead of Nuke's. - -### Key Takeaway - -**A 90° (or any) rotation in the solved camera is NOT a problem.** The converter preserves all rotations correctly. If motion doesn't match, look at: -- `unit_scale` (most common) -- Lens parameters -- Frame range -- Coordinate system validation (use `validate_conversion.py`) - diff --git a/nuke_CAM_exporter/configs/camera_config_template.json b/nuke_CAM_exporter/configs/camera_config_template.json index 4216c4a..036cecc 100644 --- a/nuke_CAM_exporter/configs/camera_config_template.json +++ b/nuke_CAM_exporter/configs/camera_config_template.json @@ -1,5 +1,7 @@ { "input": "../inputs/camTrack_v01.asci", + "input_translation": null, + "input_rotation": null, "output": "../outputs/camTrack_v01_converted.txt", "fps": 25.0, "unit_scale": 1.0, diff --git a/nuke_CAM_exporter/configs/camera_config_v01.json b/nuke_CAM_exporter/configs/camera_config_v01.json index 0ab08e4..24cd0bf 100644 --- a/nuke_CAM_exporter/configs/camera_config_v01.json +++ b/nuke_CAM_exporter/configs/camera_config_v01.json @@ -1,5 +1,7 @@ { "input": "../inputs/nukeCam_unit-1_wRotation.asci", + "input_translation": "../inputs/nukeCam_unit-1-split_tr.asci", + "input_rotation": "../inputs/nukeCam_unit-1-split_rot.asci", "output": "../outputs/nukeCam_unit-1_wRotation_converted.txt", "fps": 25.0, "unit_scale": 1, diff --git a/nuke_CAM_exporter/docs/README.md b/nuke_CAM_exporter/docs/README.md index 926c14d..406d889 100644 --- a/nuke_CAM_exporter/docs/README.md +++ b/nuke_CAM_exporter/docs/README.md @@ -6,6 +6,7 @@ This document captures the end-to-end workflow we established for converting Nuk - **Nuke camera**: standard `Camera` node with Translation / Rotation channels keyed over time. - **Export step**: use Nuke's *Export Ascii* tool with the column mapping shown in `ASCI_source_data/AnimationChannels_and_values_firstFrame_plus-ASCI-window.png`. - Column order: `[translate.x, rotate.x, translate.y, rotate.y, translate.z, rotate.z]`. +- Prefer to stick to the combined export unless you want to split the channels into two files. When splitting, export translations as `Tx Ty Tz` and rotations as `Rx Ry Rz` (degrees) and reference them via the config keys described below. - Rotations are exported in **degrees** using Nuke's **ZXY rotation order** (see `DataValidation-nuke.png`). - **Raw file**: `nuke_CAM_exporter/inputs/camTrack_v01.asci` (162 lines covering frames 1001–1161 in this example). @@ -55,6 +56,8 @@ Use the template in `configs/camera_config_template.json` (copy + rename per sho ```json { "input": "../inputs/camTrack_v01.asci", + "input_translation": null, + "input_rotation": null, "output": "../outputs/camTrack_v06_converted.txt", "fps": 25.0, "unit_scale": 1.0, @@ -77,6 +80,8 @@ Use the template in `configs/camera_config_template.json` (copy + rename per sho } ``` +Set `"input_translation"` / `"input_rotation"` when you export translations and rotations as two separate ASCI files. Leave them `null` (or omit entirely) when you rely on the combined `input` file. + Run: ```bash diff --git a/nuke_CAM_exporter/nuke_ASCI-2-Pose_converter.py b/nuke_CAM_exporter/nuke_ASCI-2-Pose_converter.py index cbf55d8..798835f 100644 --- a/nuke_CAM_exporter/nuke_ASCI-2-Pose_converter.py +++ b/nuke_CAM_exporter/nuke_ASCI-2-Pose_converter.py @@ -26,7 +26,7 @@ def parse_args() -> argparse.Namespace: type=Path, nargs="?", default=Path("inputs/camTrack_v01.asci"), - help="Path to Nuke ASCI export", + help="Path to Nuke ASCI export (combined order: Tx Rx Ty Ry Tz Rz)", ) parser.add_argument( "output", @@ -35,6 +35,16 @@ def parse_args() -> argparse.Namespace: default=Path("outputs/camTrack_v01_converted.txt"), help="Destination RealEstate-style TXT file", ) + parser.add_argument( + "--input-translation", + type=Path, + help="Optional ASCI file containing translation columns only (order: Tx Ty Tz)", + ) + parser.add_argument( + "--input-rotation", + type=Path, + help="Optional ASCI file containing rotation columns only (order: Rx Ry Rz in degrees)", + ) parser.add_argument("--config", type=Path, help="Optional JSON pipeline config") parser.add_argument("--fps", type=float, default=DEFAULT_FPS, help="Frame rate for timestamps") parser.add_argument("--fx", type=float, default=DEFAULT_FX, help="Normalized focal length x") @@ -65,7 +75,9 @@ def parse_args() -> argparse.Namespace: @dataclass class ConverterSettings: - input_path: Path + input_path: Path | None + translation_input_path: Path | None + rotation_input_path: Path | None output_path: Path fps: float fx: float @@ -138,6 +150,18 @@ def resolve_settings(args: argparse.Namespace) -> ConverterSettings: config = load_config(args.config) input_path = resolve_path(config, "input", args.input) + + def resolve_optional_path(config_key: str, arg_value: Path | None) -> Path | None: + if arg_value is not None: + return arg_value + cfg_val = config.get(config_key) + if cfg_val is None: + return None + return resolve_path(config, config_key, Path(cfg_val)) + + translation_input_path = resolve_optional_path("input_translation", args.input_translation) + rotation_input_path = resolve_optional_path("input_rotation", args.input_rotation) + output_path = resolve_path(config, "output", args.output) fps = config.get("fps", args.fps) @@ -157,6 +181,9 @@ def resolve_settings(args: argparse.Namespace) -> ConverterSettings: metadata = { "config": str(args.config) if args.config else None, + "input": str(input_path) if input_path else None, + "input_translation": str(translation_input_path) if translation_input_path else None, + "input_rotation": str(rotation_input_path) if rotation_input_path else None, "lens": config.get("lens"), "resolution": resolution_cfg or {"width_px": width, "height_px": height}, "unit_scale": unit_scale, @@ -169,6 +196,8 @@ def resolve_settings(args: argparse.Namespace) -> ConverterSettings: return ConverterSettings( input_path=input_path, + translation_input_path=translation_input_path, + rotation_input_path=rotation_input_path, output_path=output_path, fps=fps, fx=fx, @@ -255,6 +284,29 @@ def iter_asci_poses(path: Path) -> Iterator[Tuple[List[List[float]], Tuple[float yield euler_zxy_to_matrix(rx, ry, rz), (tx, ty, tz) +def iter_split_asci_poses( + translation_path: Path, rotation_path: Path +) -> Iterator[Tuple[List[List[float]], Tuple[float, float, float]]]: + translation_lines = [line.strip() for line in translation_path.read_text().splitlines() if line.strip()] + rotation_lines = [line.strip() for line in rotation_path.read_text().splitlines() if line.strip()] + if len(translation_lines) != len(rotation_lines): + raise ValueError( + f"Translation ({len(translation_lines)}) and rotation ({len(rotation_lines)}) ASCI files " + "must contain the same number of rows." + ) + + for idx, (t_line, r_line) in enumerate(zip(translation_lines, rotation_lines)): + t_values = t_line.split() + r_values = r_line.split() + if len(t_values) != 3: + raise ValueError(f"Expected 3 translation columns per line in {translation_path}, got {len(t_values)} (line {idx})") + if len(r_values) != 3: + raise ValueError(f"Expected 3 rotation columns per line in {rotation_path}, got {len(r_values)} (line {idx})") + tx, ty, tz = map(float, t_values) + rx, ry, rz = (math.radians(float(val)) for val in r_values) + yield euler_zxy_to_matrix(rx, ry, rz), (tx, ty, tz) + + def camera_center_to_translation( rotation: Sequence[Sequence[float]], camera_center: Tuple[float, float, float] ) -> Tuple[float, float, float]: @@ -288,7 +340,23 @@ def format_pose_rows( def main() -> None: args = parse_args() settings = resolve_settings(args) - poses = list(iter_asci_poses(settings.input_path)) + # Determine data source: single combined ASCI or split translation/rotation files. + if settings.translation_input_path and settings.rotation_input_path: + poses = list( + iter_split_asci_poses(settings.translation_input_path, settings.rotation_input_path) + ) + source_label = ( + f"translation:{settings.translation_input_path.as_posix()} " + f"rotation:{settings.rotation_input_path.as_posix()}" + ) + elif settings.translation_input_path or settings.rotation_input_path: + raise ValueError( + "Both input_translation and input_rotation must be provided together. " + "Either supply both split files or rely on the combined 'input' ASCI path." + ) + else: + poses = list(iter_asci_poses(settings.input_path)) + source_label = settings.input_path.as_posix() total_available = len(poses) start_idx = max(0, settings.frame_start) end_idx = settings.frame_end if settings.frame_end is not None else total_available @@ -302,7 +370,7 @@ def main() -> None: frame_time_us = int(round(1_000_000 / settings.fps)) header = ( - f"source:{settings.input_path.as_posix()} fps:{settings.fps} fx:{settings.fx} fy:{settings.fy} " + f"source:{source_label} fps:{settings.fps} fx:{settings.fx} fy:{settings.fy} " f"cx:{settings.cx} cy:{settings.cy} width:{settings.width} height:{settings.height} unit_scale:{settings.unit_scale}" ) diff --git a/nuke_CAM_exporter/outputs/nukeCam_unit-1_onlyZoom_converted.txt b/nuke_CAM_exporter/outputs/_bk/nukeCam_unit-1_onlyZoom_converted.txt similarity index 100% rename from nuke_CAM_exporter/outputs/nukeCam_unit-1_onlyZoom_converted.txt rename to nuke_CAM_exporter/outputs/_bk/nukeCam_unit-1_onlyZoom_converted.txt diff --git a/nuke_CAM_exporter/outputs/nukeCam_unit-1_wRotation_converted.txt b/nuke_CAM_exporter/outputs/_bk/nukeCam_unit-1_wRotation_converted.txt similarity index 100% rename from nuke_CAM_exporter/outputs/nukeCam_unit-1_wRotation_converted.txt rename to nuke_CAM_exporter/outputs/_bk/nukeCam_unit-1_wRotation_converted.txt diff --git a/nuke_CAM_exporter/test_nuke_scene.py b/nuke_CAM_exporter/test_nuke_scene.py deleted file mode 100644 index 4e6a42f..0000000 --- a/nuke_CAM_exporter/test_nuke_scene.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python3 -""" -Nuke script generator for testing camera export pipeline. - -This script generates a Nuke script (.nk) that creates a simple test scene with: -- A camera with known, predictable motion -- Simple geometry (a grid/card) for reference -- Camera animation that can be easily validated - -Usage: - python test_nuke_scene.py --output test_scene.nk -""" - -import argparse -from pathlib import Path - - -def generate_nuke_script(output_path: Path, num_frames: int = 50) -> None: - """Generate a Nuke script with a test camera animation.""" - - # Create a simple camera animation: - # - Start at (0, 0, -5) looking at origin - # - Move forward along Z axis (dolly in) - # - Slight pan left - # - Known focal length and filmback for easy validation - - script_lines = [ - "set cut_paste_input [stack 0]", - "version 15.0 v1", - "push $cut_paste_input", - "", - "# Test Camera - Simple forward dolly with pan", - "Camera2 {", - " inputs 0", - f" translate {{curve x {0.0} {num_frames-1} {0.0}; curve y {0.0} {num_frames-1} {0.0}; curve z {-5.0} {num_frames-1} {-3.0}}}", - f" rotate {{curve x {0.0} {num_frames-1} {0.0}; curve y {-10.0} {num_frames-1} {-15.0}; curve z {0.0} {num_frames-1} {0.0}}}", - " focal {35.0}", - " haperture {36.0}", - " vaperture {24.0}", - " near {0.1}", - " far {1000.0}", - " win_translate {{0.5 0.5}}", - " win_scale {{1 1}}", - " winroll {0}", - " fov {54.432}", - " f_stop {2.8}", - " focus_distance {5.0}", - " lens {35.0}", - " name Camera2", - " selected true", - " xpos 0", - " ypos 0", - "}", - "", - "# Reference Grid - helps visualize camera motion", - "Grid2 {", - " inputs 0", - " display {luminance}", - " name Grid2", - " selected false", - " xpos 0", - " ypos -100", - "}", - "", - "# Render node for validation", - "Write1 {", - " file {test_render_%04d.exr}", - " file_type exr", - " datatype {32 bit float}", - " compression {Zip (1 scanline)}", - " name Write1", - " selected false", - " xpos 0", - " ypos -200", - "}", - ] - - output_path.write_text("\n".join(script_lines)) - print(f"Generated Nuke script: {output_path}") - print(f" - {num_frames} frames") - print(f" - Camera starts at (0, 0, -5), ends at (0, 0, -3)") - print(f" - Camera rotates Y from -10° to -15°") - print(f" - Focal: 35mm, Filmback: 36x24mm") - - -def main(): - parser = argparse.ArgumentParser(description="Generate Nuke test scene") - parser.add_argument( - "--output", - type=Path, - default=Path("test_scene.nk"), - help="Output Nuke script path", - ) - parser.add_argument( - "--frames", - type=int, - default=50, - help="Number of frames in animation", - ) - args = parser.parse_args() - - generate_nuke_script(args.output, args.frames) - - -if __name__ == "__main__": - main() - diff --git a/nuke_CAM_exporter/test_pipeline.sh b/nuke_CAM_exporter/test_pipeline.sh deleted file mode 100644 index 4890d06..0000000 --- a/nuke_CAM_exporter/test_pipeline.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# Test pipeline script for validating the camera conversion workflow -# -# This script: -# 1. Generates a test Nuke scene -# 2. (Manual step) Export camera from Nuke as ASCI -# 3. Converts ASCI to pose format -# 4. Validates the conversion -# -# Usage: ./test_pipeline.sh - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" - -echo "=== Step 1: Generate Nuke test scene ===" -python3 test_nuke_scene.py --output test_scene.nk --frames 50 -echo "✓ Generated test_scene.nk" -echo "" -echo "=== Step 2: Manual step - Export camera from Nuke ===" -echo "1. Open test_scene.nk in Nuke" -echo "2. Select Camera2 node" -echo "3. Export Ascii with columns: [translate.x, rotate.x, translate.y, rotate.y, translate.z, rotate.z]" -echo "4. Save as: inputs/test_camera.asci" -echo "" -read -p "Press Enter when you've exported the ASCI file..." - -if [ ! -f "inputs/test_camera.asci" ]; then - echo "ERROR: inputs/test_camera.asci not found!" - exit 1 -fi - -echo "" -echo "=== Step 3: Convert ASCI to pose format ===" -python3 nuke_ASCI-2-Pose_converter.py \ - inputs/test_camera.asci \ - outputs/test_camera_converted.txt \ - --fps 25.0 \ - --focal-length-mm 35.0 \ - --sensor-width-mm 36.0 \ - --sensor-height-mm 24.0 \ - --width 1920 \ - --height 1080 \ - --unit-scale 1.0 - -echo "" -echo "=== Step 4: Validate conversion ===" -python3 validate_conversion.py \ - inputs/test_camera.asci \ - outputs/test_camera_converted.txt \ - --unit-scale 1.0 - -echo "" -echo "=== Pipeline test complete! ===" - - diff --git a/nuke_CAM_exporter/test_scene.nk b/nuke_CAM_exporter/test_scene.nk deleted file mode 100644 index 337cf94..0000000 --- a/nuke_CAM_exporter/test_scene.nk +++ /dev/null @@ -1,48 +0,0 @@ -set cut_paste_input [stack 0] -version 15.0 v1 -push $cut_paste_input - -# Test Camera - Simple forward dolly with pan -Camera2 { - inputs 0 - translate {curve x 0.0 80 0.0; curve y 0.0 80 0.0; curve z -5.0 80 -3.0} - rotate {curve x 0.0 80 0.0; curve y -10.0 80 -15.0; curve z 0.0 80 0.0} - focal {35.0} - haperture {36.0} - vaperture {24.0} - near {0.1} - far {1000.0} - win_translate {{0.5 0.5}} - win_scale {{1 1}} - winroll {0} - fov {54.432} - f_stop {2.8} - focus_distance {5.0} - lens {35.0} - name Camera2 - selected true - xpos 0 - ypos 0 -} - -# Reference Grid - helps visualize camera motion -Grid2 { - inputs 0 - display {luminance} - name Grid2 - selected false - xpos 0 - ypos -100 -} - -# Render node for validation -Write1 { - file {test_render_%04d.exr} - file_type exr - datatype {32 bit float} - compression {Zip (1 scanline)} - name Write1 - selected false - xpos 0 - ypos -200 -} \ No newline at end of file diff --git a/nuke_CAM_exporter/tools/check_mapping.py b/nuke_CAM_exporter/testing_tools/check_mapping.py similarity index 100% rename from nuke_CAM_exporter/tools/check_mapping.py rename to nuke_CAM_exporter/testing_tools/check_mapping.py diff --git a/nuke_CAM_exporter/debug_rotation.py b/nuke_CAM_exporter/testing_tools/debug_rotation.py similarity index 100% rename from nuke_CAM_exporter/debug_rotation.py rename to nuke_CAM_exporter/testing_tools/debug_rotation.py diff --git a/nuke_CAM_exporter/validate_conversion.py b/nuke_CAM_exporter/testing_tools/validate_conversion.py similarity index 100% rename from nuke_CAM_exporter/validate_conversion.py rename to nuke_CAM_exporter/testing_tools/validate_conversion.py