Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions Plugins/nosTrack/CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Record Track (COLMAP) Node

## Summary

A new node "Record Track (COLMAP)" added to the `nosTrack` plugin. It records incoming camera tracking data per frame and exports it in COLMAP's text format (`cameras.txt` + `images.txt`).

## Files Changed

### New files
- `Source/RecordTrackCOLMAP.cpp` — Node implementation
- `Config/RecordTrackCOLMAP.nosdef` — Node definition (pins, functions, metadata)

### Modified files
- `Source/TrackMain.cpp` — Added `RecordTrackCOLMAP` to the `TrackNode` enum and `ExportNodeFunctions` switch
- `Track.noscfg` — Added `Config/RecordTrackCOLMAP.nosdef` to `node_definitions`

## Node Design

### Pins
| Pin | Type | Direction | Description |
|-----|------|-----------|-------------|
| Track | `nos.track.Track` | Input | Incoming tracking data |
| Track Out | `nos.track.Track` | Output (only) | Pass-through of input |
| Output Directory | `string` | Property | Folder picker for output |
| Image Resolution | `nos.fb.vec2u` | Property | Width/height (default 1920x1080) |
| Record | `bool` | Property | Mirrors Record/Stop functions |
| Frame Count | `uint` | Output (only) | Frames in buffer |

### Functions
| Function | Behavior |
|----------|----------|
| Record | Validates folder is empty, clears buffer, starts recording. Orphaned while recording. |
| Stop | Stops recording (does NOT save). Orphaned while idle. |
| Save | Writes `cameras.txt` + `images.txt` to disk. Does not clear buffer. |
| Clear | Clears frame buffer and resets count. |
| Open Folder | Opens output directory in explorer (Windows) or xdg-open (Linux). |

### State Management
- Record pin and functions are kept in sync bidirectionally. A `SyncingRecordPin` bool guard prevents re-entrant loops between pin changes and function calls.
- Function orphan states: Record/Stop toggle via `SetNodeOrphanState` using a `Name -> UUID` map built in constructor.
- Status messages show recording state + frame count, and persist error messages (e.g., "Target folder is not empty") via `LastError` until user changes the output directory.
- Non-empty folder check: Recording fails with a FAILURE status if the target folder already has files.

### COLMAP Output Format
- `cameras.txt`: One OPENCV camera per frame — `fx, fy, cx, cy, k1, k2, p1, p2` derived from Track FOV, sensor size, pixel aspect ratio, lens distortion.
- `images.txt`: Per-frame pose — Euler angles converted to quaternion (world-to-camera), translation as `t = -R * C`.

## Known Review Points
- Euler-to-quaternion convention: The Track's rotation fields (roll/tilt/pan) are passed through `glm::quat(eulerRadians)` then inverted for COLMAP's world-to-camera convention. May need validation against actual tracker output.
- One camera per frame: Each frame gets its own camera entry. This handles zoom/FOV changes but may be unusual for COLMAP workflows with constant intrinsics.
- No `points3D.txt`: COLMAP expects this file too (can be empty). Not currently written.
- `std::system()` for Open Folder: Works but is a simple shell call. Could be replaced with platform APIs if needed.

## Build
```
./nodos dev build -p Project13 --target nosTrack
```
81 changes: 81 additions & 0 deletions Plugins/nosTrack/Config/PlaybackTrackCOLMAP.nosdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"nodes": [
{
"class_name": "PlaybackTrackCOLMAP",
"menu_info": {
"category": "nosTrack",
"display_name": "Playback Track (COLMAP)",
"name_aliases": [ "colmap", "import camera", "playback camera" ]
},
"node": {
"class_name": "PlaybackTrackCOLMAP",
"display_name": "Playback Track (COLMAP)",
"contents_type": "Job",
"always_execute": true,
"description": "Loads camera tracking data from COLMAP text format (cameras.txt + images.txt) and outputs Track data at a given frame index.",
"pins": [
{
"name": "InputDirectory",
"display_name": "Input Directory",
"type_name": "string",
"show_as": "PROPERTY",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"visualizer": { "type": "FOLDER_PICKER" },
"description": "Directory containing cameras.txt and images.txt in COLMAP text format."
},
{
"name": "CoordinateSystem",
"display_name": "Coordinate System",
"type_name": "nos.sys.track.CoordinateSystem",
"show_as": "PROPERTY",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"data": "ZYX",
"description": "Euler angle rotation order for converting COLMAP quaternion to Track rotation. Default ZYX matches the FreeD node convention."
},
{
"name": "InFrameIndex",
"display_name": "Frame Index",
"type_name": "uint",
"show_as": "INPUT_PIN",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"data": 0,
"description": "Frame index to output."
},
{
"name": "Track",
"type_name": "nos.sys.track.Track",
"show_as": "OUTPUT_PIN",
"can_show_as": "OUTPUT_PIN_ONLY",
"description": "Track data for the current frame."
},
{
"name": "OutFrameIndex",
"display_name": "Frame Index",
"type_name": "uint",
"show_as": "OUTPUT_PIN",
"can_show_as": "OUTPUT_PIN_ONLY",
"data": 0,
"description": "Current playback frame index."
},
{
"name": "FrameCount",
"display_name": "Frame Count",
"type_name": "uint",
"show_as": "OUTPUT_PIN",
"can_show_as": "OUTPUT_PIN_ONLY",
"data": 0,
"description": "Total number of frames loaded."
}
],
"functions": [
{
"class_name": "PlaybackTrackCOLMAP_OpenFolder",
"display_name": "Open Folder",
"contents_type": "Job",
"pins": []
}
]
}
}
]
}
125 changes: 125 additions & 0 deletions Plugins/nosTrack/Config/RecordTrackCOLMAP.nosdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"nodes": [
{
"class_name": "RecordTrackCOLMAP",
"menu_info": {
"category": "nosTrack",
"display_name": "Record Track (COLMAP)",
"name_aliases": [ "colmap", "export camera", "record camera" ]
},
"node": {
"class_name": "RecordTrackCOLMAP",
"display_name": "Record Track (COLMAP)",
"contents_type": "Job",
"always_execute": true,
"description": "Records camera tracking data each frame while recording is enabled, then exports cameras.txt and images.txt in COLMAP format. Intrinsics (focal length, distortion) are derived from the Track's FOV, sensor size, and lens distortion. Extrinsics (rotation, translation) are stored per frame in world-to-camera convention.",
"pins": [
{
"name": "InTrack",
"display_name": "Track",
"type_name": "nos.sys.track.Track",
"show_as": "INPUT_PIN",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"description": "Incoming camera tracking data to record. Position, rotation, FOV, sensor size, and lens distortion are captured each frame."
},
{
"name": "OutTrack",
"display_name": "Track",
"type_name": "nos.sys.track.Track",
"show_as": "OUTPUT_PIN",
"can_show_as": "OUTPUT_PIN_ONLY",
"description": "Pass-through of the incoming Track data."
},
{
"name": "OutputDirectory",
"display_name": "Output Directory",
"type_name": "string",
"show_as": "PROPERTY",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"visualizer": { "type": "FOLDER_PICKER" },
"description": "Directory where cameras.txt and images.txt will be written when recording stops. Must be empty to start recording."
},
{
"name": "ImageResolution",
"display_name": "Image Resolution",
"type_name": "nos.fb.vec2u",
"show_as": "PROPERTY",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"data": {
"x": 1920,
"y": 1080
},
"description": "Image resolution in pixels (width, height). Used to compute focal length and principal point for COLMAP camera model."
},
{
"name": "CoordinateSystem",
"display_name": "Coordinate System",
"type_name": "nos.sys.track.CoordinateSystem",
"show_as": "PROPERTY",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"data": "ZYX",
"description": "Euler angle rotation order used when converting Track rotation to COLMAP extrinsics. Default ZYX matches the FreeD node convention."
},
{
"name": "Record",
"type_name": "bool",
"show_as": "PROPERTY",
"can_show_as": "INPUT_PIN_OR_PROPERTY",
"data": false,
"description": "Toggle recording. Mirrors Record/Stop functions. Enabling clears previous frames and starts capturing. Will fail if the output directory is not empty."
},
{
"name": "RecordingFrame",
"display_name": "Recording Frame",
"type_name": "uint",
"show_as": "OUTPUT_PIN",
"can_show_as": "OUTPUT_PIN_ONLY",
"data": 0,
"description": "Current recording frame index. Outputs 0 when not recording."
},
{
"name": "FrameCount",
"display_name": "Frame Count",
"type_name": "uint",
"show_as": "OUTPUT_PIN",
"can_show_as": "OUTPUT_PIN_ONLY",
"data": 0,
"description": "Number of frames in the buffer."
}
],
"functions": [
{
"class_name": "RecordTrackCOLMAP_Record",
"display_name": "Record",
"contents_type": "Job",
"pins": []
},
{
"class_name": "RecordTrackCOLMAP_Stop",
"display_name": "Stop",
"contents_type": "Job",
"pins": []
},
{
"class_name": "RecordTrackCOLMAP_Save",
"display_name": "Save",
"contents_type": "Job",
"pins": []
},
{
"class_name": "RecordTrackCOLMAP_Clear",
"display_name": "Clear",
"contents_type": "Job",
"pins": []
},
{
"class_name": "RecordTrackCOLMAP_OpenFolder",
"display_name": "Open Folder",
"contents_type": "Job",
"pins": []
}
]
}
}
]
}
Loading