reOrg B
This commit is contained in:
@@ -2,7 +2,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",
|
||||
"output": "../outputs/nukeCam_unit-1_2-files.txt",
|
||||
"fps": 25.0,
|
||||
"unit_scale": 1,
|
||||
"lens": {
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
# Camera → Wan Pipeline Cheatsheet
|
||||
|
||||
This is the quick reference for translating DCC camera exports (Nuke / Houdini) into something Wan understands.
|
||||
|
||||
### Mental model
|
||||
|
||||
**Nuke / Houdini (Scanline, Karma, etc.)**
|
||||
- Camera input: native camera node from the scene graph
|
||||
- Coordinate frame: whatever the scene uses
|
||||
- Intrinsics: stored on the camera node (filmback + focal)
|
||||
- Translation: camera center `C` in scene units
|
||||
|
||||
**Wan video pipeline**
|
||||
- Camera input: RealEstate10k TXT (`timestamp fx fy cx cy width height R\|t`)
|
||||
- Coordinate frame: fixed OpenCV axes (+X right, +Y down, +Z forward)
|
||||
- Intrinsics: requires normalized values (`fx = f_mm / sensor_w_mm`)
|
||||
- Translation: needs **world→camera** translation `t = –R · C` in meters
|
||||
|
||||
Wan was trained on RealEstate10k, so its camera encoder only understands that schema. Anything else (raw ASCI, FBX, etc.) must be converted first.
|
||||
|
||||
## Why the converter is mandatory
|
||||
|
||||
1. **Different math objects**
|
||||
- **Nuke’s ASCI** stores the camera center `(Tx, Ty, Tz)` plus ZXY Euler angles `(Rx, Ry, Rz)` in degrees. It tells you where the camera sits in world space and which way it’s pointing.
|
||||
- **Wan / RealEstate** wants the inverse: a 3×4 matrix `[R|t]` that transforms any world point into the camera’s local space. In other words, “rotate + shift the world so the camera becomes the origin.”
|
||||
- To bridge the two, we:
|
||||
1. Convert the Euler angles to a rotation matrix `R` (standard Z→X→Y order).
|
||||
2. Turn the camera center `C` into the translation part of the inverse matrix via `t = -R · C`.
|
||||
The path doesn’t change—we simply express the same motion from the camera’s frame of reference so Wan’s math matches Nuke’s.
|
||||
2. **Coordinate transforms**
|
||||
- Nuke: +X right, +Y up, +Z forward.
|
||||
- RealEstate: +X right, +Y down, +Z forward (OpenCV).
|
||||
- Wan’s visualizer applies another transform for Matplotlib’s +Y-up space.
|
||||
The converter reorients the axes so Wan’s `ray_condition()` can produce sensible rays.
|
||||
3. **Units**
|
||||
- Wan assumes translations are in meters.
|
||||
- Nuke scenes often live in centimeters or abstract units, so we apply `unit_scale = real_distance / asci_delta` before writing the TXT.
|
||||
|
||||
## Practical workflow
|
||||
|
||||
1. **Export Nuke camera as ASCI** (columns `[Tx Rx Ty Ry Rz Tz]`).
|
||||
2. **Measure real-world scale** so you can compute `unit_scale`.
|
||||
Example: camera dolly moves 3.5 m, ASCI `Tz` delta is `0.11` ⇒ `unit_scale = 3.5 / 0.11 = 31.82`.
|
||||
3. **Fill `configs/camera_config_*.json`** with:
|
||||
- Input/output paths
|
||||
- Lens data (focal + filmback) → converter derives `fx/fy/cx/cy`
|
||||
- Render resolution placeholders
|
||||
- `unit_scale` and optional frame caps
|
||||
4. **Run** `python nuke_ASCI-2-Pose_converter.py --config configs/<shot>.json`
|
||||
5. **Load TXT in ComfyUI** via `Koolook Load Camera Poses` → `WanVideoFunCameraEmbeds`.
|
||||
|
||||
Following this flow keeps the rendered motion aligned with the original plate, even though the raw numbers look different after conversion.***
|
||||
|
||||
## Where things happen inside the ComfyUI graph
|
||||
|
||||
1. **`Text Multiline` → `Koolook Load Camera Poses`**
|
||||
- You paste the RealEstate10k TXT path.
|
||||
- Koolook parses the header plus each row and returns `CAMERACTRL_POSES` (the same intrinsics + `[R|t]` data we exported).
|
||||
|
||||
2. **`WanVideo FunCamera Embeds`**
|
||||
This node performs all the heavy lifting described in the converter:
|
||||
- `process_poses()` rescales intrinsics to match the render width/height.
|
||||
- `get_relative_pose()` turns each `[R|t]` row into camera-to-world matrices.
|
||||
- `ray_condition()` converts those matrices into Plücker rays (origin + direction per pixel), the representation the Wan motion model was trained on.
|
||||
- The tensor is reshaped and averaged to match Wan’s token budget (21 camera tokens per chunk) and the VAE stride.
|
||||
- The result is a dictionary:
|
||||
```python
|
||||
{
|
||||
"target_shape": (channels, t, h/8, w/8),
|
||||
"num_frames": len(poses),
|
||||
"control_embeds": {
|
||||
"control_camera_latents": …,
|
||||
"control_camera_start_percent": …,
|
||||
"control_camera_end_percent": …,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
- This payload is what the node outputs on `image_embeds`.
|
||||
|
||||
3. **`WanVideo ImageToVideo Encode`**
|
||||
- Takes VAE/CLIP/start/end latents plus the `image_embeds` dict.
|
||||
- Reads `control_embeds["control_camera_latents"]` and injects those camera rays into the diffusion network so every generated frame follows the exact path from Nuke.
|
||||
- `target_shape` and `num_frames` keep the camera tokens aligned with the render resolution and animation length.
|
||||
|
||||
So all the conversions (intrinsics, `t = –R·C`, Plücker rays, token packing) happen before the encoder ever runs; by the time `image_embeds` hits Wan, it’s a set of “camera control instructions” telling the model where to point each ray for every frame.***
|
||||
|
||||
## Why Wan still drifts relative to the original plate
|
||||
|
||||
Even with perfect pose conversion, Wan isn’t a deterministic renderer—it’s still a diffusion model inventing geometry on the fly. Keep these limitations in mind:
|
||||
|
||||
- **Diffusion ≠ projection.** Wan reconstructs a scene that *statistically* fits the camera rays; it doesn’t ray-trace your tracked mesh, so pixels can land a little off compared to a Nuke render.
|
||||
- **No shared 3D points.** Your tracked points in Nuke are actual world markers. Wan fabricates its own depth, so there’s nothing forcing it to align to those anchors.
|
||||
- **Token downsampling.** Camera embeddings get compressed (Plücker rays → 21 tokens → VAE stride). That’s enough to guide global motion but not sub-pixel parallax.
|
||||
- **Scale sensitivity.** Any tiny error in focal length, sensor size, or `unit_scale` shows up as sliding when you composite back with live-action elements.
|
||||
|
||||
### Tips when you need zero sliding
|
||||
|
||||
1. **Lock scale & intrinsics.** Double-check the measurement and lens metadata; millimeter accuracy matters.
|
||||
2. **Treat Wan as a background pass.** Keep your foreground in Nuke and use Wan only for the new environment, then composite the two with projections/cards to fix residual offsets.
|
||||
3. **Use camera projections for critical geometry.** Project the plate (or a painted matte) onto simple geo driven by your tracked camera, and blend that with Wan’s output.
|
||||
4. **Post-warp if needed.** If the drift is small, track Wan’s render and apply a 2D/3D match-move or morph to align with the original plate.
|
||||
|
||||
Wan’s camera control ensures the *motion* matches; matching the *exact* pixel positions still requires traditional VFX techniques when you need perfectly locked foreground/background interaction.***
|
||||
|
||||
## Getting `unit_scale` and lens data dialed in
|
||||
|
||||
The most common reason for visible drift is a scale mismatch. Because Wan assumes the translations in `[R|t]` are in meters, a small error gets amplified—especially on wide lenses where foreground points travel further per degree of rotation.
|
||||
|
||||
### Why it matters
|
||||
|
||||
- **Focal length & filmback set the field of view.** If the RealEstate TXT says `fx = 33 mm / 36 mm = 0.916` but the real lens was 31 mm, Wan thinks the camera is *slightly tighter*. To match the same screen-space travel, it pushes the scene farther away, and your foreground doesn’t line up.
|
||||
- **`unit_scale` scales the entire translation vector.** Doubling `unit_scale` doubles the apparent dolly move. On a 24 mm lens a 5 cm error is huge; on a 135 mm it’s barely noticeable.
|
||||
|
||||
### How to measure accurately
|
||||
|
||||
1. **Grab real production data whenever possible.** Use the on-set focal length and documented dolly track distance. Enter those directly into the config.
|
||||
2. **Use Nuke’s reference tools.** In CameraTracker you can “Set Reference Distance” between two tracked points. If you tell it “this wall is exactly 4.2 m apart,” the entire camera solves into meters and `unit_scale` becomes 1.0.
|
||||
3. **Manual measurement workflow:**
|
||||
- Pick two frames (start/end) and read the raw ASCI translation delta (e.g., Tz changes by 0.11842).
|
||||
Formula:
|
||||
```
|
||||
unit_scale = real_world_distance_meters / abs(asci_delta)
|
||||
```
|
||||
Example: camera travels 6.30 m, ASCI delta is 0.1891 ⇒ `unit_scale = 6.30 / 0.1891 = 33.31`.
|
||||
- Measure the real-world distance for that move (tape measure on set, LiDAR scan, or deduced from perspective grids).
|
||||
- Compute the ratio using the formula above. Keep at least 3 significant decimals.
|
||||
- Verify by projecting a checkerboard onto set geometry: if it still drifts, adjust by the measured percent difference.
|
||||
4. **Wide lenses need tighter tolerances.** For anything 35 mm or wider, aim for centimeter-level accuracy. Every 1% error in scale equals ~1% sliding on screen.
|
||||
|
||||
Document the measurement in the config file (e.g. `"reference_distance_m": 6.53`). That way anyone regenerating the poses knows where the number came from and can double-check if the set changes.***
|
||||
@@ -0,0 +1,81 @@
|
||||
source:translation:configs/../inputs/nukeCam_unit-1-split_tr.asci rotation:configs/../inputs/nukeCam_unit-1-split_rot.asci fps:25.0 fx:0.9722222222222222 fy:1.4583333333333333 cx:0.5 cy:0.5 width:1920 height:1080 unit_scale:1
|
||||
0.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 1.000000000000 -0.000000000000 0.000000000000 -0.000000000000 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 0.000000000000 -0.000000000000 1.000000000000 -0.500000000000
|
||||
40000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999997620177 -0.000000000000 0.002181659834 -0.006063547170 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.002181659834 -0.000000000000 0.999997620177 -0.487487931338
|
||||
80000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999990480721 -0.000000000000 0.004363309285 -0.012072476516 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.004363309285 -0.000000000000 0.999990480721 -0.474951844950
|
||||
120000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999978581664 -0.000000000000 0.006544937967 -0.018026712232 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.006544937967 -0.000000000000 0.999978581664 -0.462391919452
|
||||
160000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999961923064 -0.000000000000 0.008726535498 -0.023926179030 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.008726535498 -0.000000000000 0.999961923064 -0.449808334072
|
||||
200000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999940505000 -0.000000000000 0.010908091494 -0.029770802145 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.010908091494 -0.000000000000 0.999940505000 -0.437201267856
|
||||
240000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999914327574 -0.000000000000 0.013089595571 -0.035560507433 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.013089595571 -0.000000000000 0.999914327574 -0.424570900458
|
||||
280000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999883390910 -0.000000000000 0.015271037347 -0.041295220972 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.015271037347 -0.000000000000 0.999883390910 -0.411917411453
|
||||
320000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999847695156 -0.000000000000 0.017452406437 -0.046974869762 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.017452406437 -0.000000000000 0.999847695156 -0.399240980716
|
||||
360000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999807240482 -0.000000000000 0.019633692461 -0.052599380927 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.019633692461 -0.000000000000 0.999807240482 -0.386541788340
|
||||
400000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999762027080 -0.000000000000 0.021814885035 -0.058168682516 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.021814885035 -0.000000000000 0.999762027080 -0.373820014719
|
||||
440000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999712055165 -0.000000000000 0.023995973777 -0.063682702697 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.023995973777 -0.000000000000 0.999712055165 -0.361075840159
|
||||
480000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999657324976 -0.000000000000 0.026176948308 -0.069141370573 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.026176948308 -0.000000000000 0.999657324976 -0.348309445564
|
||||
520000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999597836772 -0.000000000000 0.028357798245 -0.074544615459 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.028357798245 -0.000000000000 0.999597836772 -0.335521011648
|
||||
560000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999533590837 -0.000000000000 0.030538513210 -0.079892367309 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.030538513210 -0.000000000000 0.999533590837 -0.322710719722
|
||||
600000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999464587476 -0.000000000000 0.032719082822 -0.085184556497 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.032719082822 -0.000000000000 0.999464587476 -0.309878751005
|
||||
640000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999390827019 -0.000000000000 0.034899496703 -0.090421114224 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.034899496703 -0.000000000000 0.999390827019 -0.297025287002
|
||||
680000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999312309816 -0.000000000000 0.037079744474 -0.095601971919 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.037079744474 -0.000000000000 0.999312309816 -0.284150509426
|
||||
720000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999229036241 -0.000000000000 0.039259815759 -0.100727061641 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.039259815759 -0.000000000000 0.999229036241 -0.271254600184
|
||||
760000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999141006690 -0.000000000000 0.041439700181 -0.105796316076 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.041439700181 -0.000000000000 0.999141006690 -0.258337741373
|
||||
800000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.999048221582 -0.000000000000 0.043619387365 -0.110809668139 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.043619387365 -0.000000000000 0.999048221582 -0.245400115295
|
||||
840000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998950681359 -0.000000000000 0.045798866937 -0.115767051577 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.045798866937 -0.000000000000 0.998950681359 -0.232441904433
|
||||
880000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998848386485 -0.000000000000 0.047978128521 -0.120668400569 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.047978128521 -0.000000000000 0.998848386485 -0.219463291562
|
||||
920000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998741337447 -0.000000000000 0.050157161747 -0.125513649814 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.050157161747 -0.000000000000 0.998741337447 -0.206464459348
|
||||
960000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998629534755 -0.000000000000 0.052335956243 -0.130302734552 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.052335956243 -0.000000000000 0.998629534755 -0.193445590945
|
||||
1000000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998512978940 -0.000000000000 0.054514501638 -0.135035590660 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.054514501638 -0.000000000000 0.998512978940 -0.180406869686
|
||||
1040000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998391670557 -0.000000000000 0.056692787563 -0.139712154235 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.056692787563 -0.000000000000 0.998391670557 -0.167348478906
|
||||
1080000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998265610185 -0.000000000000 0.058870803651 -0.144332362305 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.058870803651 -0.000000000000 0.998265610185 -0.154270602099
|
||||
1120000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.998134798422 -0.000000000000 0.061048539535 -0.148896152049 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.061048539535 -0.000000000000 0.998134798422 -0.141173423267
|
||||
1160000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.997999235891 -0.000000000000 0.063225984849 -0.153403461465 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.063225984849 -0.000000000000 0.997999235891 -0.128057126272
|
||||
1200000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.997858923239 -0.000000000000 0.065403129230 -0.157854229082 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.065403129230 -0.000000000000 0.997858923239 -0.114921895155
|
||||
1240000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.997713861131 -0.000000000000 0.067579962315 -0.162248393683 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.067579962315 -0.000000000000 0.997713861131 -0.101767914454
|
||||
1280000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.997564050260 -0.000000000000 0.069756473744 -0.166585894968 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.069756473744 -0.000000000000 0.997564050260 -0.088595368557
|
||||
1320000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.997409491337 -0.000000000000 0.071932653157 -0.170866672780 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.071932653157 -0.000000000000 0.997409491337 -0.075404442151
|
||||
1360000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.997250185099 -0.000000000000 0.074108490195 -0.175090667888 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.074108490195 -0.000000000000 0.997250185099 -0.062195319973
|
||||
1400000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.997086132304 -0.000000000000 0.076283974504 -0.179257821223 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.076283974504 -0.000000000000 0.997086132304 -0.048968187255
|
||||
1440000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.996917333733 -0.000000000000 0.078459095728 -0.183368074628 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.078459095728 -0.000000000000 0.996917333733 -0.035723229073
|
||||
1480000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.996743790189 -0.000000000000 0.080633843514 -0.187421370093 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.080633843514 -0.000000000000 0.996743790189 -0.022460630799
|
||||
1520000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.996565502498 -0.000000000000 0.082808207512 -0.191417650546 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.082808207512 -0.000000000000 0.996565502498 -0.009180577944
|
||||
1560000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.996382471508 -0.000000000000 0.084982177372 -0.195356859053 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.084982177372 -0.000000000000 0.996382471508 0.004116743785
|
||||
1600000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.996194698092 -0.000000000000 0.087155742748 -0.199238939618 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.087155742748 -0.000000000000 0.996194698092 0.017431148550
|
||||
1640000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.996002183142 -0.000000000000 0.089328893292 -0.203063836486 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.089328893292 -0.000000000000 0.996002183142 0.030762450324
|
||||
1680000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.995804927575 -0.000000000000 0.091501618663 -0.206831494442 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.091501618663 -0.000000000000 0.995804927575 0.044110462919
|
||||
1720000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.995602932329 -0.000000000000 0.093673908519 -0.210541859109 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.093673908519 -0.000000000000 0.995602932329 0.057475000014
|
||||
1760000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.995396198367 -0.000000000000 0.095845752520 -0.214194876252 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.095845752520 -0.000000000000 0.995396198367 0.070855875094
|
||||
1800000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.995184726672 -0.000000000000 0.098017140330 -0.217790492578 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.098017140330 -0.000000000000 0.995184726672 0.084252901523
|
||||
1840000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.994968518251 -0.000000000000 0.100188061612 -0.221328654935 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.100188061612 -0.000000000000 0.994968518251 0.097665892473
|
||||
1880000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.994747574132 -0.000000000000 0.102358506035 -0.224809311102 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.102358506035 -0.000000000000 0.994747574132 0.111094661099
|
||||
1920000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.994521895368 -0.000000000000 0.104528463268 -0.228232409033 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.104528463268 -0.000000000000 0.994521895368 0.124539020067
|
||||
1960000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.994291483033 -0.000000000000 0.106697922982 -0.231597897590 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.106697922982 -0.000000000000 0.994291483033 0.137998782230
|
||||
2000000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.994056338222 -0.000000000000 0.108866874852 -0.234905725794 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.108866874852 -0.000000000000 0.994056338222 0.151473760151
|
||||
2040000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.993816462056 -0.000000000000 0.111035308554 -0.238155843594 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.111035308554 -0.000000000000 0.993816462056 0.164963766386
|
||||
2080000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.993571855677 -0.000000000000 0.113203213768 -0.241348201120 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.113203213768 -0.000000000000 0.993571855677 0.178468613005
|
||||
2120000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.993322520247 -0.000000000000 0.115370580174 -0.244482749310 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.115370580174 -0.000000000000 0.993322520247 0.191988112263
|
||||
2160000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.993068456955 -0.000000000000 0.117537397458 -0.247559439647 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.117537397458 -0.000000000000 0.993068456955 0.205522076271
|
||||
2200000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.992809667009 -0.000000000000 0.119703655305 -0.250578223896 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.119703655305 -0.000000000000 0.992809667009 0.219070316665
|
||||
2240000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.992546151641 -0.000000000000 0.121869343405 -0.253539054731 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.121869343405 -0.000000000000 0.992546151641 0.232632645289
|
||||
2280000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.992277912106 -0.000000000000 0.124034451450 -0.256441884972 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.124034451450 -0.000000000000 0.992277912106 0.246208873795
|
||||
2320000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.992004949680 -0.000000000000 0.126198969136 -0.259286668309 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.126198969136 -0.000000000000 0.992004949680 0.259798813539
|
||||
2360000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.991727265662 -0.000000000000 0.128362886159 -0.262073358881 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.128362886159 -0.000000000000 0.991727265662 0.273402275726
|
||||
2400000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.991444861374 -0.000000000000 0.130526192220 -0.264801911333 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.130526192220 -0.000000000000 0.991444861374 0.287019071726
|
||||
2440000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.991157738160 -0.000000000000 0.132688877023 -0.267472280998 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.132688877023 -0.000000000000 0.991157738160 0.300649012491
|
||||
2480000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.990865897387 -0.000000000000 0.134850930274 -0.270084423445 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.134850930274 -0.000000000000 0.990865897387 0.314291908900
|
||||
2520000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.990569340444 -0.000000000000 0.137012341682 -0.272638295089 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.137012341682 -0.000000000000 0.990569340444 0.327947571744
|
||||
2560000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.990268068742 -0.000000000000 0.139173100960 -0.275133852795 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.139173100960 -0.000000000000 0.990268068742 0.341615811669
|
||||
2600000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.989962083715 -0.000000000000 0.141333197824 -0.277571053976 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.141333197824 -0.000000000000 0.989962083715 0.355296439195
|
||||
2640000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.989651386820 -0.000000000000 0.143492621991 -0.279949856496 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.143492621991 -0.000000000000 0.989651386820 0.368989264703
|
||||
2680000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.989335979535 -0.000000000000 0.145651363185 -0.282270219065 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.145651363185 -0.000000000000 0.989335979535 0.382694098491
|
||||
2720000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.989015863362 -0.000000000000 0.147809411130 -0.284532100631 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.147809411130 -0.000000000000 0.989015863362 0.396410750793
|
||||
2760000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.988691039824 -0.000000000000 0.149966755554 -0.286735460837 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.149966755554 -0.000000000000 0.988691039824 0.410139031437
|
||||
2800000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.988361510468 -0.000000000000 0.152123386190 -0.288880259717 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.152123386190 -0.000000000000 0.988361510468 0.423878750512
|
||||
2840000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.988027276861 -0.000000000000 0.154279292772 -0.290966458213 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.154279292772 -0.000000000000 0.988027276861 0.437629717640
|
||||
2880000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.987688340595 -0.000000000000 0.156434465040 -0.292994017363 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.156434465040 -0.000000000000 0.987688340595 0.451391742660
|
||||
2920000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.987344703283 -0.000000000000 0.158588892735 -0.294962899196 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.158588892735 -0.000000000000 0.987344703283 0.465164635060
|
||||
2960000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.986996366560 -0.000000000000 0.160742565604 -0.296873065884 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.160742565604 -0.000000000000 0.986996366560 0.478948204254
|
||||
3000000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.986643332085 -0.000000000000 0.162895473395 -0.298724480545 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.162895473395 -0.000000000000 0.986643332085 0.492742259602
|
||||
3040000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.986285601537 -0.000000000000 0.165047605861 -0.300517106440 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.165047605861 -0.000000000000 0.986285601537 0.506546610393
|
||||
3080000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.985923176620 -0.000000000000 0.167198952759 -0.302250907727 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.167198952759 -0.000000000000 0.985923176620 0.520361065556
|
||||
3120000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.985556059058 -0.000000000000 0.169349503849 -0.303925848952 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.169349503849 -0.000000000000 0.985556059058 0.534185434292
|
||||
3160000.000000000000 0.972222222222 1.458333333333 0.500000000000 0.500000000000 1920.000000000000 1080.000000000000 0.985184250599 -0.000000000000 0.171499248896 -0.305541895283 -0.000000000000 1.000000000000 -0.000000000000 -0.500000000000 -0.171499248896 -0.000000000000 0.985184250599 0.548019525301
|
||||
@@ -1,111 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import math
|
||||
from pathlib import Path
|
||||
from typing import Sequence
|
||||
|
||||
ASCI_PATH = Path(__file__).resolve().parents[1] / "inputs" / "nukeCam_unit-1_v2.asci"
|
||||
POSE_PATH = Path(__file__).resolve().parents[1] / "outputs" / "nukeCam_unit-1_v4_converted.txt"
|
||||
|
||||
|
||||
def mat_mul(a: Sequence[Sequence[float]], b: Sequence[Sequence[float]]) -> list[list[float]]:
|
||||
return [
|
||||
[a[r][0] * b[0][c] + a[r][1] * b[1][c] + a[r][2] * b[2][c] for c in range(3)]
|
||||
for r in range(3)
|
||||
]
|
||||
|
||||
|
||||
def euler_zxy(rx: float, ry: float, rz: float) -> list[list[float]]:
|
||||
sx, cx = math.sin(rx), math.cos(rx)
|
||||
sy, cy = math.sin(ry), math.cos(ry)
|
||||
sz, cz = math.sin(rz), math.cos(rz)
|
||||
|
||||
Rx = (
|
||||
(1.0, 0.0, 0.0),
|
||||
(0.0, cx, -sx),
|
||||
(0.0, sx, cx),
|
||||
)
|
||||
Ry = (
|
||||
(cy, 0.0, sy),
|
||||
(0.0, 1.0, 0.0),
|
||||
(-sy, 0.0, cy),
|
||||
)
|
||||
Rz = (
|
||||
(cz, -sz, 0.0),
|
||||
(sz, cz, 0.0),
|
||||
(0.0, 0.0, 1.0),
|
||||
)
|
||||
|
||||
return mat_mul(Ry, mat_mul(Rx, Rz))
|
||||
|
||||
|
||||
def convert_yup_to_ydown(R: list[list[float]]) -> list[list[float]]:
|
||||
# T = diag(1, -1, 1)
|
||||
return [
|
||||
[R[0][0], -R[0][1], R[0][2]],
|
||||
[-R[1][0], R[1][1], -R[1][2]],
|
||||
[R[2][0], -R[2][1], R[2][2]],
|
||||
]
|
||||
|
||||
|
||||
def mat_vec_mul(R: Sequence[Sequence[float]], v: Sequence[float]) -> list[float]:
|
||||
return [
|
||||
R[0][0] * v[0] + R[0][1] * v[1] + R[0][2] * v[2],
|
||||
R[1][0] * v[0] + R[1][1] * v[1] + R[1][2] * v[2],
|
||||
R[2][0] * v[0] + R[2][1] * v[1] + R[2][2] * v[2],
|
||||
]
|
||||
|
||||
|
||||
def read_rows(path: Path) -> list[list[float]]:
|
||||
rows: list[list[float]] = []
|
||||
for line in path.read_text().splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
continue
|
||||
# Skip header/metadata lines (e.g. "source:…") that are not numeric.
|
||||
first = stripped[0]
|
||||
if first not in "0123456789-.":
|
||||
continue
|
||||
rows.append(list(map(float, stripped.split())))
|
||||
return rows
|
||||
|
||||
|
||||
def compare_rows(indices: list[int]) -> None:
|
||||
asci_rows = read_rows(ASCI_PATH)
|
||||
pose_rows = read_rows(POSE_PATH)
|
||||
|
||||
print(f"Frames in ASCI: {len(asci_rows)}")
|
||||
print(f"Frames in TXT: {len(pose_rows)}")
|
||||
|
||||
for idx in indices:
|
||||
if idx >= len(pose_rows):
|
||||
continue
|
||||
tx, rx, ty, ry, tz, rz = asci_rows[idx]
|
||||
R_nuke = euler_zxy(math.radians(rx), math.radians(ry), math.radians(rz))
|
||||
R_cv = convert_yup_to_ydown(R_nuke)
|
||||
t = [-val for val in mat_vec_mul(R_cv, (tx, ty, tz))]
|
||||
|
||||
pose_vals = pose_rows[idx]
|
||||
R_export = [
|
||||
pose_vals[7:11],
|
||||
pose_vals[11:15],
|
||||
pose_vals[15:19],
|
||||
]
|
||||
|
||||
rot_diff = max(abs(R_export[r][c] - R_cv[r][c]) for r in range(3) for c in range(3))
|
||||
|
||||
print(f"\nFrame {idx}")
|
||||
print("Nuke Tx Ty Tz :", (tx, ty, tz))
|
||||
print("Export t :", tuple(round(pose_vals[10 + i * 4], 6) for i in range(3)))
|
||||
print("Expected t :", tuple(round(val, 6) for val in t))
|
||||
print("Rotation diff :", rot_diff)
|
||||
print("R_nuke→OpenCV :")
|
||||
for row in R_cv:
|
||||
print(" ", " ".join(f"{val: .6f}" for val in row))
|
||||
print("R_export :")
|
||||
for row in R_export:
|
||||
print(" ", " ".join(f"{val: .6f}" for val in row))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
compare_rows([0, 79])
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debug tool to analyze camera rotations and verify coordinate system conversion.
|
||||
|
||||
This helps identify if a 90° Y-axis rotation (or any rotation) is being handled
|
||||
correctly by the converter.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
def euler_zxy_to_matrix(rx: float, ry: float, rz: float) -> np.ndarray:
|
||||
"""Convert ZXY Euler angles to rotation matrix (Nuke's system)."""
|
||||
sx, cx = math.sin(rx), math.cos(rx)
|
||||
sy, cy = math.sin(ry), math.cos(ry)
|
||||
sz, cz = math.sin(rz), math.cos(rz)
|
||||
|
||||
Rx = np.array([[1.0, 0.0, 0.0], [0.0, cx, -sx], [0.0, sx, cx]])
|
||||
Ry = np.array([[cy, 0.0, sy], [0.0, 1.0, 0.0], [-sy, 0.0, cy]])
|
||||
Rz = np.array([[cz, -sz, 0.0], [sz, cz, 0.0], [0.0, 0.0, 1.0]])
|
||||
|
||||
return Ry @ Rx @ Rz
|
||||
|
||||
|
||||
def convert_yup_to_ydown(rotation: np.ndarray) -> np.ndarray:
|
||||
"""Convert from Nuke Y-up to OpenCV Y-down coordinate system."""
|
||||
r = rotation
|
||||
return np.array([
|
||||
[r[0, 0], -r[0, 1], r[0, 2]],
|
||||
[-r[1, 0], r[1, 1], -r[1, 2]],
|
||||
[r[2, 0], -r[2, 1], r[2, 2]],
|
||||
])
|
||||
|
||||
|
||||
def rotation_to_euler_xyz(R: np.ndarray) -> Tuple[float, float, float]:
|
||||
"""Extract XYZ Euler angles from rotation matrix (for debugging)."""
|
||||
# Extract angles (this is approximate, works for most cases)
|
||||
sy = -R[2, 0]
|
||||
cy = math.sqrt(max(0, 1 - sy * sy))
|
||||
|
||||
if abs(cy) > 1e-6:
|
||||
sx = R[2, 1] / cy
|
||||
cx = R[2, 2] / cy
|
||||
sz = R[1, 0] / cy
|
||||
cz = R[0, 0] / cy
|
||||
else:
|
||||
# Gimbal lock case
|
||||
sx = R[1, 2]
|
||||
cx = R[1, 1]
|
||||
sz = 0.0
|
||||
cz = 1.0
|
||||
|
||||
return math.atan2(sx, cx), math.atan2(sy, cy), math.atan2(sz, cz)
|
||||
|
||||
|
||||
def analyze_asci_rotations(path: Path, num_frames: int = 5) -> None:
|
||||
"""Analyze rotations in ASCI file to identify patterns."""
|
||||
print(f"Analyzing rotations in: {path}")
|
||||
print("=" * 80)
|
||||
|
||||
lines = [l.strip() for l in path.read_text().splitlines() if l.strip()]
|
||||
num_frames = min(num_frames, len(lines))
|
||||
|
||||
print(f"\nFirst {num_frames} frames:")
|
||||
print("-" * 80)
|
||||
|
||||
for i in range(num_frames):
|
||||
values = lines[i].split()
|
||||
if len(values) != 6:
|
||||
continue
|
||||
|
||||
tx, rx_deg, ty, ry_deg, rz_deg, tz = [float(v) for v in values]
|
||||
|
||||
print(f"\nFrame {i+1}:")
|
||||
print(f" Translation: ({tx:.3f}, {ty:.3f}, {tz:.3f})")
|
||||
print(f" Rotation (degrees): X={rx_deg:.2f}°, Y={ry_deg:.2f}°, Z={rz_deg:.2f}°")
|
||||
|
||||
# Convert to rotation matrix
|
||||
rx, ry, rz = math.radians(rx_deg), math.radians(ry_deg), math.radians(rz_deg)
|
||||
R_nuke = euler_zxy_to_matrix(rx, ry, rz)
|
||||
|
||||
# Check if Y rotation is near 90°
|
||||
if abs(ry_deg - 90.0) < 5.0 or abs(ry_deg - (-90.0)) < 5.0:
|
||||
print(f" ⚠️ WARNING: Y rotation is near ±90° ({ry_deg:.2f}°)")
|
||||
print(f" This might indicate a coordinate system orientation issue")
|
||||
|
||||
# Convert to OpenCV coordinate system
|
||||
R_opencv = convert_yup_to_ydown(R_nuke)
|
||||
|
||||
# Verify rotation matrix properties
|
||||
det_nuke = np.linalg.det(R_nuke)
|
||||
det_opencv = np.linalg.det(R_opencv)
|
||||
|
||||
print(f" Rotation matrix determinant: Nuke={det_nuke:.6f}, OpenCV={det_opencv:.6f}")
|
||||
|
||||
# Check forward direction (Z axis in camera space)
|
||||
forward_nuke = R_nuke[:, 2] # Third column
|
||||
forward_opencv = R_opencv[:, 2]
|
||||
|
||||
print(f" Forward direction (Nuke): ({forward_nuke[0]:.3f}, {forward_nuke[1]:.3f}, {forward_nuke[2]:.3f})")
|
||||
print(f" Forward direction (OpenCV): ({forward_opencv[0]:.3f}, {forward_opencv[1]:.3f}, {forward_opencv[2]:.3f})")
|
||||
|
||||
# Check if coordinate conversion preserved the rotation correctly
|
||||
# The forward direction should point in the same physical direction
|
||||
# (just expressed in different coordinate systems)
|
||||
dot_product = np.dot(forward_nuke, forward_opencv)
|
||||
if abs(dot_product - 1.0) < 0.01:
|
||||
print(f" ✓ Coordinate conversion preserves forward direction")
|
||||
else:
|
||||
print(f" ⚠️ Forward directions differ (dot={dot_product:.6f})")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("\nInterpretation:")
|
||||
print("- If Y rotation is consistently near ±90°, this might indicate:")
|
||||
print(" 1. Camera solver oriented the scene differently (this is OK)")
|
||||
print(" 2. Coordinate system mismatch in the solver (check Nuke settings)")
|
||||
print("- The converter should preserve all rotations correctly")
|
||||
print("- If motion doesn't match, check unit_scale and lens parameters")
|
||||
|
||||
|
||||
def test_90_degree_rotation() -> None:
|
||||
"""Test if 90° Y rotation is handled correctly."""
|
||||
print("\n" + "=" * 80)
|
||||
print("Testing 90° Y-axis rotation conversion:")
|
||||
print("-" * 80)
|
||||
|
||||
# Test case: 90° rotation around Y axis
|
||||
ry_90 = math.radians(90.0)
|
||||
R_nuke = euler_zxy_to_matrix(0.0, ry_90, 0.0)
|
||||
|
||||
print(f"\nNuke rotation matrix (90° Y):")
|
||||
print(R_nuke)
|
||||
|
||||
R_opencv = convert_yup_to_ydown(R_nuke)
|
||||
|
||||
print(f"\nOpenCV rotation matrix (after conversion):")
|
||||
print(R_opencv)
|
||||
|
||||
# Verify properties
|
||||
det_nuke = np.linalg.det(R_nuke)
|
||||
det_opencv = np.linalg.det(R_opencv)
|
||||
|
||||
print(f"\nDeterminants: Nuke={det_nuke:.6f}, OpenCV={det_opencv:.6f}")
|
||||
|
||||
# Test forward direction
|
||||
forward_nuke = R_nuke[:, 2]
|
||||
forward_opencv = R_opencv[:, 2]
|
||||
|
||||
print(f"\nForward direction:")
|
||||
print(f" Nuke: ({forward_nuke[0]:.3f}, {forward_nuke[1]:.3f}, {forward_nuke[2]:.3f})")
|
||||
print(f" OpenCV: ({forward_opencv[0]:.3f}, {forward_opencv[1]:.3f}, {forward_opencv[2]:.3f})")
|
||||
|
||||
# In Nuke: +Z forward, so 90° Y rotation should point +X
|
||||
# After conversion, should still point in same physical direction
|
||||
print(f"\n✓ 90° Y rotation conversion test complete")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Debug camera rotations")
|
||||
parser.add_argument("asci", type=Path, help="ASCI file to analyze")
|
||||
parser.add_argument("--frames", type=int, default=5, help="Number of frames to analyze")
|
||||
parser.add_argument("--test-90deg", action="store_true", help="Run 90° rotation test")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.test_90deg:
|
||||
test_90_degree_rotation()
|
||||
|
||||
if args.asci.exists():
|
||||
analyze_asci_rotations(args.asci, args.frames)
|
||||
else:
|
||||
print(f"Error: {args.asci} not found")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Pose diagnostics CLI.
|
||||
|
||||
Replaces the previous individual helpers (check_mapping, validate_conversion,
|
||||
debug_rotation) with a single entry point that exposes three subcommands:
|
||||
|
||||
- validate: compare ASCI vs. converted TXT in detail.
|
||||
- check: probe specific frames for translation/rotation parity.
|
||||
- rotations:inspect raw ASCI rotations or run a 90° sanity test.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from pathlib import Path
|
||||
from typing import Iterable, List, Sequence, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Shared math helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def euler_zxy_to_matrix(rx: float, ry: float, rz: float) -> List[List[float]]:
|
||||
sx, cx = math.sin(rx), math.cos(rx)
|
||||
sy, cy = math.sin(ry), math.cos(ry)
|
||||
sz, cz = math.sin(rz), math.cos(rz)
|
||||
|
||||
Rx = [[1.0, 0.0, 0.0], [0.0, cx, -sx], [0.0, sx, cx]]
|
||||
Ry = [[cy, 0.0, sy], [0.0, 1.0, 0.0], [-sy, 0.0, cy]]
|
||||
Rz = [[cz, -sz, 0.0], [sz, cz, 0.0], [0.0, 0.0, 1.0]]
|
||||
|
||||
return (np.dot(np.dot(Ry, Rx), Rz)).tolist()
|
||||
|
||||
|
||||
def convert_yup_to_ydown(rotation: Sequence[Sequence[float]]) -> List[List[float]]:
|
||||
r = rotation
|
||||
return [
|
||||
[r[0][0], -r[0][1], r[0][2]],
|
||||
[-r[1][0], r[1][1], -r[1][2]],
|
||||
[r[2][0], -r[2][1], r[2][2]],
|
||||
]
|
||||
|
||||
|
||||
def mat_vec_mul(R: Sequence[Sequence[float]], v: Sequence[float]) -> List[float]:
|
||||
return [
|
||||
R[0][0] * v[0] + R[0][1] * v[1] + R[0][2] * v[2],
|
||||
R[1][0] * v[0] + R[1][1] * v[1] + R[1][2] * v[2],
|
||||
R[2][0] * v[0] + R[2][1] * v[1] + R[2][2] * v[2],
|
||||
]
|
||||
|
||||
|
||||
def read_numeric_rows(path: Path) -> List[List[float]]:
|
||||
rows: List[List[float]] = []
|
||||
for line in path.read_text().splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
continue
|
||||
if stripped[0] not in "0123456789-.":
|
||||
continue
|
||||
rows.append(list(map(float, stripped.split())))
|
||||
return rows
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# validate command
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_pose_line(line: str) -> Tuple[float, List[float], List[List[float]]]:
|
||||
values = line.strip().split()
|
||||
if len(values) != 19:
|
||||
raise ValueError(f"Expected 19 columns, got {len(values)}")
|
||||
timestamp = float(values[0])
|
||||
intrinsics = [float(v) for v in values[1:7]]
|
||||
matrix_values = [float(v) for v in values[7:19]]
|
||||
R = [
|
||||
matrix_values[0:4],
|
||||
matrix_values[4:8],
|
||||
matrix_values[8:12],
|
||||
]
|
||||
return timestamp, intrinsics, R
|
||||
|
||||
|
||||
def validate_rotation_matrix(R: Sequence[Sequence[float]], name: str) -> List[str]:
|
||||
issues: List[str] = []
|
||||
R_np = np.array(R)
|
||||
det = np.linalg.det(R_np)
|
||||
if abs(det - 1.0) > 0.01:
|
||||
issues.append(f"{name}: determinant {det:.6f} (expected 1.0)")
|
||||
should_be_identity = np.dot(R_np, R_np.T)
|
||||
identity = np.eye(3)
|
||||
max_diff = np.abs(should_be_identity - identity).max()
|
||||
if max_diff > 0.01:
|
||||
issues.append(f"{name}: not orthogonal (max diff {max_diff:.6f})")
|
||||
return issues
|
||||
|
||||
|
||||
def cmd_validate(args: argparse.Namespace) -> None:
|
||||
asci_path = args.asci
|
||||
pose_path = args.pose
|
||||
unit_scale = args.unit_scale
|
||||
|
||||
print(f"Reading ASCI: {asci_path}")
|
||||
print(f"Reading poses: {pose_path}")
|
||||
print(f"Using unit_scale: {unit_scale}")
|
||||
print()
|
||||
|
||||
asci_lines = [l for l in asci_path.read_text().splitlines() if l.strip()]
|
||||
pose_lines = pose_path.read_text().splitlines()
|
||||
header = pose_lines[0] if pose_lines else ""
|
||||
pose_data = [parse_pose_line(l) for l in pose_lines[1:] if l.strip()]
|
||||
|
||||
print(f"Found {len(asci_lines)} frames in ASCI")
|
||||
print(f"Found {len(pose_data)} frames in pose file")
|
||||
print(f"Header: {header}")
|
||||
if len(asci_lines) != len(pose_data):
|
||||
print(
|
||||
f"WARNING: Frame count mismatch! ASCI: {len(asci_lines)}, Poses: {len(pose_data)}"
|
||||
)
|
||||
print()
|
||||
|
||||
num_check = min(args.frames, len(asci_lines), len(pose_data))
|
||||
print(f"Detailed comparison of first {num_check} frames:")
|
||||
print("-" * 80)
|
||||
|
||||
all_issues: List[str] = []
|
||||
for i in range(num_check):
|
||||
tx, rx_deg, ty, ry_deg, tz, rz_deg = map(float, asci_lines[i].split())
|
||||
rx, ry, rz = math.radians(rx_deg), math.radians(ry_deg), math.radians(rz_deg)
|
||||
|
||||
print(f"\nFrame {i}:")
|
||||
print(f" ASCI: T=({tx:.3f}, {ty:.3f}, {tz:.3f}), R=({rx_deg:.2f}°, {ry_deg:.2f}°, {rz_deg:.2f}°)")
|
||||
|
||||
R_asci = euler_zxy_to_matrix(rx, ry, rz)
|
||||
R_pose = [row[:3] for row in pose_data[i][2]]
|
||||
t_pose = [row[3] for row in pose_data[i][2]]
|
||||
print(f" Pose t: ({t_pose[0]:.6f}, {t_pose[1]:.6f}, {t_pose[2]:.6f})")
|
||||
|
||||
all_issues.extend(validate_rotation_matrix(R_asci, f"ASCI_R_{i}"))
|
||||
all_issues.extend(validate_rotation_matrix(R_pose, f"Pose_R_{i}"))
|
||||
|
||||
# Translation check
|
||||
camera_center_scaled = np.array([tx * unit_scale, ty * unit_scale, tz * unit_scale])
|
||||
expected_t = -np.array(R_pose) @ camera_center_scaled
|
||||
t_diff = np.abs(np.array(t_pose) - expected_t)
|
||||
max_t_diff = t_diff.max()
|
||||
if max_t_diff > 0.01:
|
||||
print(f" WARNING: translation mismatch (max diff {max_t_diff:.6f})")
|
||||
all_issues.append(f"Frame {i}: translation mismatch ({max_t_diff:.6f})")
|
||||
else:
|
||||
print(f" ✓ translation matches (max diff {max_t_diff:.6f})")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
if all_issues:
|
||||
print("SUMMARY: issues detected:")
|
||||
for issue in all_issues:
|
||||
print(" -", issue)
|
||||
else:
|
||||
print("SUMMARY: All checks passed ✓")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# check command (specific frame parity)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_check(args: argparse.Namespace) -> None:
|
||||
asci_rows = read_numeric_rows(args.asci)
|
||||
pose_rows = read_numeric_rows(args.pose)[1:] # skip header already filtered
|
||||
|
||||
print(f"Frames in ASCI: {len(asci_rows)}")
|
||||
print(f"Frames in TXT : {len(pose_rows)}")
|
||||
|
||||
indices = args.frames or [0, len(pose_rows) - 1]
|
||||
|
||||
for idx in indices:
|
||||
actual_idx = idx if idx >= 0 else len(pose_rows) + idx
|
||||
if actual_idx < 0 or actual_idx >= len(pose_rows):
|
||||
continue
|
||||
tx, rx, ty, ry, tz, rz = asci_rows[actual_idx]
|
||||
R_nuke = euler_zxy_to_matrix(math.radians(rx), math.radians(ry), math.radians(rz))
|
||||
R_cv = convert_yup_to_ydown(R_nuke)
|
||||
t_expected = [-val for val in mat_vec_mul(R_cv, (tx, ty, tz))]
|
||||
|
||||
pose_vals = pose_rows[actual_idx]
|
||||
R_export = [
|
||||
pose_vals[7:11],
|
||||
pose_vals[11:15],
|
||||
pose_vals[15:19],
|
||||
]
|
||||
|
||||
rot_diff = max(abs(R_export[r][c] - R_cv[r][c]) for r in range(3) for c in range(3))
|
||||
|
||||
print(f"\nFrame {actual_idx}")
|
||||
print("Nuke Tx Ty Tz :", (tx, ty, tz))
|
||||
print("Export t :", tuple(round(pose_vals[10 + i * 4], 6) for i in range(3)))
|
||||
print("Expected t :", tuple(round(val, 6) for val in t_expected))
|
||||
print("Rotation diff :", rot_diff)
|
||||
print("R_nuke→OpenCV :")
|
||||
for row in R_cv:
|
||||
print(" ", " ".join(f"{val: .6f}" for val in row))
|
||||
print("R_export :")
|
||||
for row in R_export:
|
||||
print(" ", " ".join(f"{val: .6f}" for val in row))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# rotations command (ASCI analysis)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def analyze_asci_rotations(path: Path, num_frames: int = 5) -> None:
|
||||
print(f"Analyzing rotations in: {path}")
|
||||
print("=" * 80)
|
||||
|
||||
lines = [l.strip() for l in path.read_text().splitlines() if l.strip()]
|
||||
num_frames = min(num_frames, len(lines))
|
||||
|
||||
for i in range(num_frames):
|
||||
values = list(map(float, lines[i].split()))
|
||||
tx, rx_deg, ty, ry_deg, tz, rz_deg = values
|
||||
|
||||
print(f"\nFrame {i}: T=({tx:.3f}, {ty:.3f}, {tz:.3f}) R=({rx_deg:.2f}°, {ry_deg:.2f}°, {rz_deg:.2f}°)")
|
||||
R_nuke = np.array(euler_zxy_to_matrix(math.radians(rx_deg), math.radians(ry_deg), math.radians(rz_deg)))
|
||||
R_cv = np.array(convert_yup_to_ydown(R_nuke))
|
||||
print(f" det(Nuke)={np.linalg.det(R_nuke):.6f} det(OpenCV)={np.linalg.det(R_cv):.6f}")
|
||||
|
||||
|
||||
def test_90deg_rotation() -> None:
|
||||
print("=" * 80)
|
||||
print("Testing 90° Y-axis rotation conversion")
|
||||
ry = math.radians(90.0)
|
||||
R_nuke = np.array(euler_zxy_to_matrix(0.0, ry, 0.0))
|
||||
R_cv = np.array(convert_yup_to_ydown(R_nuke))
|
||||
print("Nuke rotation:\n", R_nuke)
|
||||
print("OpenCV rotation:\n", R_cv)
|
||||
print(f"det Nuke={np.linalg.det(R_nuke):.6f}, det OpenCV={np.linalg.det(R_cv):.6f}")
|
||||
|
||||
|
||||
def cmd_rotations(args: argparse.Namespace) -> None:
|
||||
if args.test_90deg:
|
||||
test_90deg_rotation()
|
||||
analyze_asci_rotations(args.asci, args.frames)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="Pose diagnostics helper")
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
validate_parser = subparsers.add_parser(
|
||||
"validate", help="Cross-check ASCI vs converted TXT (detailed)"
|
||||
)
|
||||
validate_parser.add_argument("asci", type=Path)
|
||||
validate_parser.add_argument("pose", type=Path)
|
||||
validate_parser.add_argument("--unit-scale", type=float, default=1.0)
|
||||
validate_parser.add_argument(
|
||||
"--frames", type=int, default=5, help="Number of initial frames to inspect"
|
||||
)
|
||||
validate_parser.set_defaults(func=cmd_validate)
|
||||
|
||||
check_parser = subparsers.add_parser(
|
||||
"check", help="Inspect specific frame indices for rotation/translation parity"
|
||||
)
|
||||
check_parser.add_argument("asci", type=Path)
|
||||
check_parser.add_argument("pose", type=Path)
|
||||
check_parser.add_argument(
|
||||
"--frames",
|
||||
type=int,
|
||||
nargs="*",
|
||||
default=None,
|
||||
help="Frame indices to inspect (use negative indices for reverse order)",
|
||||
)
|
||||
check_parser.set_defaults(func=cmd_check)
|
||||
|
||||
rot_parser = subparsers.add_parser(
|
||||
"rotations", help="Inspect rotations inside an ASCI export"
|
||||
)
|
||||
rot_parser.add_argument("asci", type=Path)
|
||||
rot_parser.add_argument("--frames", type=int, default=5)
|
||||
rot_parser.add_argument("--test-90deg", action="store_true")
|
||||
rot_parser.set_defaults(func=cmd_rotations)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = build_parser()
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Validation script to check camera pose conversion accuracy.
|
||||
|
||||
This script:
|
||||
1. Reads the original ASCI file
|
||||
2. Reads the converted pose file
|
||||
3. Validates the transformations
|
||||
4. Reports any issues with coordinate systems, scales, etc.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
import numpy as np
|
||||
|
||||
|
||||
def parse_asci_line(line: str) -> Tuple[float, float, float, float, float, float]:
|
||||
"""Parse one line of ASCI file: [Tx, Rx, Ty, Ry, Tz, Rz]"""
|
||||
values = line.strip().split()
|
||||
if len(values) != 6:
|
||||
raise ValueError(f"Expected 6 columns, got {len(values)}")
|
||||
tx, rx, ty, ry, tz, rz = (float(v) for v in values)
|
||||
return tx, rx, ty, ry, tz, rz
|
||||
|
||||
|
||||
def parse_pose_line(line: str) -> Tuple[float, List[float], List[float]]:
|
||||
"""Parse one line of pose file: timestamp, intrinsics, [R|t] matrix"""
|
||||
values = line.strip().split()
|
||||
if len(values) != 19:
|
||||
raise ValueError(f"Expected 19 columns, got {len(values)}")
|
||||
|
||||
timestamp = float(values[0])
|
||||
intrinsics = [float(v) for v in values[1:7]] # fx, fy, cx, cy, width, height
|
||||
|
||||
# Extract 3x4 [R|t] matrix (row-major)
|
||||
matrix_values = [float(v) for v in values[7:19]]
|
||||
R = [
|
||||
matrix_values[0:4], # Row 0: r00, r01, r02, t0
|
||||
matrix_values[4:8], # Row 1: r10, r11, r12, t1
|
||||
matrix_values[8:12], # Row 2: r20, r21, r22, t2
|
||||
]
|
||||
|
||||
return timestamp, intrinsics, R
|
||||
|
||||
|
||||
def euler_zxy_to_matrix(rx: float, ry: float, rz: float) -> List[List[float]]:
|
||||
"""Convert ZXY Euler angles to rotation matrix."""
|
||||
sx, cx = math.sin(rx), math.cos(rx)
|
||||
sy, cy = math.sin(ry), math.cos(ry)
|
||||
sz, cz = math.sin(rz), math.cos(rz)
|
||||
|
||||
Rx = [[1.0, 0.0, 0.0], [0.0, cx, -sx], [0.0, sx, cx]]
|
||||
Ry = [[cy, 0.0, sy], [0.0, 1.0, 0.0], [-sy, 0.0, cy]]
|
||||
Rz = [[cz, -sz, 0.0], [sz, cz, 0.0], [0.0, 0.0, 1.0]]
|
||||
|
||||
# R = Ry * Rx * Rz
|
||||
R_temp = np.dot(Ry, Rx)
|
||||
R = np.dot(R_temp, Rz)
|
||||
return R.tolist()
|
||||
|
||||
|
||||
def validate_rotation_matrix(R: List[List[float]], name: str = "R") -> List[str]:
|
||||
"""Check if rotation matrix is valid (orthogonal, determinant = 1)."""
|
||||
issues = []
|
||||
R_np = np.array(R)
|
||||
|
||||
# Check determinant (should be ~1.0 for rotation matrix)
|
||||
det = np.linalg.det(R_np)
|
||||
if abs(det - 1.0) > 0.01:
|
||||
issues.append(f"{name}: determinant = {det:.6f} (expected ~1.0)")
|
||||
|
||||
# Check orthogonality (R * R^T should be identity)
|
||||
should_be_identity = np.dot(R_np, R_np.T)
|
||||
identity = np.eye(3)
|
||||
max_diff = np.abs(should_be_identity - identity).max()
|
||||
if max_diff > 0.01:
|
||||
issues.append(f"{name}: not orthogonal (max diff: {max_diff:.6f})")
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def compare_poses(asci_path: Path, pose_path: Path, unit_scale: float = 1.0) -> None:
|
||||
"""Compare ASCI input with converted pose output."""
|
||||
print(f"Reading ASCI: {asci_path}")
|
||||
print(f"Reading poses: {pose_path}")
|
||||
print(f"Using unit_scale: {unit_scale}")
|
||||
print()
|
||||
|
||||
# Read ASCI
|
||||
asci_lines = [l for l in asci_path.read_text().splitlines() if l.strip()]
|
||||
print(f"Found {len(asci_lines)} frames in ASCI")
|
||||
|
||||
# Read pose header
|
||||
pose_lines = pose_path.read_text().splitlines()
|
||||
header = pose_lines[0]
|
||||
pose_data = [parse_pose_line(l) for l in pose_lines[1:] if l.strip()]
|
||||
print(f"Found {len(pose_data)} frames in pose file")
|
||||
print(f"Header: {header}")
|
||||
print()
|
||||
|
||||
if len(asci_lines) != len(pose_data):
|
||||
print(f"WARNING: Frame count mismatch! ASCI: {len(asci_lines)}, Poses: {len(pose_data)}")
|
||||
print()
|
||||
|
||||
# Compare first few frames in detail
|
||||
num_check = min(5, len(asci_lines), len(pose_data))
|
||||
print(f"Detailed comparison of first {num_check} frames:")
|
||||
print("-" * 80)
|
||||
|
||||
all_issues = []
|
||||
|
||||
for i in range(num_check):
|
||||
print(f"\nFrame {i}:")
|
||||
|
||||
# Parse ASCI
|
||||
tx, rx_deg, ty, ry_deg, tz, rz_deg = parse_asci_line(asci_lines[i])
|
||||
rx, ry, rz = math.radians(rx_deg), math.radians(ry_deg), math.radians(rz_deg)
|
||||
|
||||
print(f" ASCI: T=({tx:.3f}, {ty:.3f}, {tz:.3f}), R=({rx_deg:.1f}°, {ry_deg:.1f}°, {rz_deg:.1f}°)")
|
||||
|
||||
# Reconstruct rotation matrix from ASCI
|
||||
R_asci = euler_zxy_to_matrix(rx, ry, rz)
|
||||
R_asci_np = np.array(R_asci)
|
||||
|
||||
# Get pose data (already parsed on line 97)
|
||||
timestamp, intrinsics, R_pose_row = pose_data[i]
|
||||
R_pose = [row[:3] for row in R_pose_row] # Extract rotation part
|
||||
t_pose = [row[3] for row in R_pose_row] # Extract translation part
|
||||
R_pose_np = np.array(R_pose)
|
||||
|
||||
print(f" Pose: timestamp={timestamp:.0f}µs, fx={intrinsics[0]:.6f}, fy={intrinsics[1]:.6f}")
|
||||
print(f" Pose R[0]: [{R_pose[0][0]:.6f}, {R_pose[0][1]:.6f}, {R_pose[0][2]:.6f}]")
|
||||
print(f" Pose R[1]: [{R_pose[1][0]:.6f}, {R_pose[1][1]:.6f}, {R_pose[1][2]:.6f}]")
|
||||
print(f" Pose t: ({t_pose[0]:.6f}, {t_pose[1]:.6f}, {t_pose[2]:.6f})")
|
||||
|
||||
# Validate rotation matrices
|
||||
issues_asci = validate_rotation_matrix(R_asci, f"ASCI_R_frame{i}")
|
||||
issues_pose = validate_rotation_matrix(R_pose, f"Pose_R_frame{i}")
|
||||
|
||||
if issues_asci:
|
||||
print(f" ISSUES with ASCI rotation matrix:")
|
||||
for issue in issues_asci:
|
||||
print(f" - {issue}")
|
||||
all_issues.extend(issues_asci)
|
||||
|
||||
if issues_pose:
|
||||
print(f" ISSUES with Pose rotation matrix:")
|
||||
for issue in issues_pose:
|
||||
print(f" - {issue}")
|
||||
all_issues.extend(issues_pose)
|
||||
|
||||
# Check coordinate system conversion
|
||||
# In Y-up to Y-down conversion, Y row and Y column should be negated
|
||||
# Check if this pattern exists
|
||||
y_row_flipped = (
|
||||
abs(R_pose[1][0] + R_asci[1][0]) < 0.01 and
|
||||
abs(R_pose[1][1] - R_asci[1][1]) < 0.01 and
|
||||
abs(R_pose[1][2] + R_asci[1][2]) < 0.01
|
||||
)
|
||||
y_col_flipped = (
|
||||
abs(R_pose[0][1] + R_asci[0][1]) < 0.01 and
|
||||
abs(R_pose[2][1] + R_asci[2][1]) < 0.01
|
||||
)
|
||||
|
||||
if not y_row_flipped or not y_col_flipped:
|
||||
print(f" WARNING: Coordinate system conversion may be incorrect!")
|
||||
print(f" Y-row flipped: {y_row_flipped}, Y-col flipped: {y_col_flipped}")
|
||||
all_issues.append(f"Frame {i}: Coordinate system conversion issue")
|
||||
|
||||
# Check translation conversion
|
||||
# Expected: t = -R_opencv * C_scaled
|
||||
camera_center_scaled = np.array([tx * unit_scale, ty * unit_scale, tz * unit_scale])
|
||||
expected_t = -R_pose_np @ camera_center_scaled
|
||||
|
||||
t_diff = np.abs(np.array(t_pose) - expected_t)
|
||||
max_t_diff = t_diff.max()
|
||||
if max_t_diff > 0.01:
|
||||
print(f" WARNING: Translation mismatch!")
|
||||
print(f" Expected t: ({expected_t[0]:.6f}, {expected_t[1]:.6f}, {expected_t[2]:.6f})")
|
||||
print(f" Actual t: ({t_pose[0]:.6f}, {t_pose[1]:.6f}, {t_pose[2]:.6f})")
|
||||
print(f" Max diff: {max_t_diff:.6f}")
|
||||
all_issues.append(f"Frame {i}: Translation conversion issue (max diff: {max_t_diff:.6f})")
|
||||
else:
|
||||
print(f" ✓ Translation conversion looks correct (max diff: {max_t_diff:.6f})")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
if all_issues:
|
||||
print(f"SUMMARY: Found {len(all_issues)} issues:")
|
||||
for issue in all_issues:
|
||||
print(f" - {issue}")
|
||||
else:
|
||||
print("SUMMARY: All checks passed! ✓")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Validate camera pose conversion")
|
||||
parser.add_argument("asci", type=Path, help="Input ASCI file")
|
||||
parser.add_argument("pose", type=Path, help="Converted pose file")
|
||||
parser.add_argument("--unit-scale", type=float, default=1.0, help="Unit scale used in conversion")
|
||||
args = parser.parse_args()
|
||||
|
||||
compare_poses(args.asci, args.pose, args.unit_scale)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user