new node generateVideo from sfmData#3
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new Meshroom “Video Utils” node intended to generate a video from an SfMData by ordering views via frameId.
Changes:
- Introduces a new
GenerateVideocommand-line node that reads an SfMData and creates a sequential image set via symlinks. - Builds an
ffmpegcommand to encode those frames into an MP4 in the node cache. - Adds a root
.gitignoreentry for__pycache__.
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
meshroom/videoUtils/generateVideo.py |
New node that loads SfMData views, creates sequential symlinks, and runs ffmpeg to produce a video. |
.gitignore |
Ignores Python __pycache__ artifacts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import logging | ||
| logging.getLogger().setLevel(chunk.node.verboseLevel.value.upper()) | ||
|
|
There was a problem hiding this comment.
Setting the root logger level inside a node (logging.getLogger().setLevel(...)) can have side effects on unrelated logging in the same process. Prefer configuring a module-specific logger (e.g. logging.getLogger(__name__)) or using Meshroom’s standard logging facilities for nodes if available.
| desc.FloatParam( | ||
| name="frameRate", | ||
| label="Camera Frame Rate", | ||
| description="Define the camera's Frames per second.", | ||
| value=24.0, | ||
| range=(1.0, 60.0, 1.0), | ||
| ), |
There was a problem hiding this comment.
Parameter naming is inconsistent with the existing nodes in meshroom/videoUtils (framerate is used elsewhere). Consider renaming frameRate to framerate for consistency and to make scripting/graph reuse more predictable.
meshroom/videoUtils/generateVideo.py
Outdated
| link.symlink_to(target) | ||
| pos = pos + 1 | ||
|
|
||
| self.commandLine = f"ffmpeg -framerate 24 -start_number 0 -i %012d{suffix} -vf \"scale=trunc(iw/2)*2:trunc(ih/2)*2\" -c:v libx264 -pix_fmt yuv420p {chunk.node.outputVideo.value}" |
There was a problem hiding this comment.
The frameRate input is not used: the ffmpeg command hardcodes -framerate 24. Use the node parameter value for -framerate so the UI control actually affects the generated video. Also consider adding -y (as in other videoUtils nodes) to avoid failing on reruns when the output file already exists.
| self.commandLine = f"ffmpeg -framerate 24 -start_number 0 -i %012d{suffix} -vf \"scale=trunc(iw/2)*2:trunc(ih/2)*2\" -c:v libx264 -pix_fmt yuv420p {chunk.node.outputVideo.value}" | |
| self.commandLine = f"ffmpeg -y -framerate {chunk.node.frameRate.value} -start_number 0 -i %012d{suffix} -vf \"scale=trunc(iw/2)*2:trunc(ih/2)*2\" -c:v libx264 -pix_fmt yuv420p {chunk.node.outputVideo.value}" |
| # Create a link using a sequential order | ||
| link = intermediate_directory / f"{pos:012d}{suffix}" | ||
|
|
||
| # Force symlink creation | ||
| if link.exists() or link.is_symlink(): | ||
| link.unlink() | ||
|
|
||
| # Create symbolic link | ||
| link.symlink_to(target) | ||
| pos = pos + 1 |
There was a problem hiding this comment.
This loop only overwrites links from 0..N-1; if the node cache is reused and a later run has fewer frames than a previous run, stale symlinks beyond pos will remain and ffmpeg will silently include old frames. Clear previously generated links (or generate them in a dedicated subdirectory that you recreate/clean each run) before creating the new sequence.
|
|
||
|
|
There was a problem hiding this comment.
If the SfMData contains zero views, suffix stays empty and no links are created, but the node still builds an ffmpeg command that cannot work. Add an explicit check after loading/views extraction to fail fast with a clear error when no views/images are found.
| # fail fast if no views/images were found | |
| if not viewsPerFrameId: | |
| logging.error("No views/images found in the input SfMData.") | |
| raise RuntimeError() |
| logging.error("Cannot open input") | ||
| raise RuntimeError() |
There was a problem hiding this comment.
The error handling here drops important context: it logs a generic message and raises RuntimeError() without any message/path, which makes failures hard to debug. Include the input filepath in the log/exception and raise with a descriptive message (or rethrow a more specific exception) so the UI/logs show actionable info.
| logging.error("Cannot open input") | |
| raise RuntimeError() | |
| logging.error("Cannot open input sfmData file: %s", chunk.node.input.value) | |
| raise RuntimeError(f"Cannot open input sfmData file: {chunk.node.input.value}") |
09abbbe to
9b7b6f3
Compare
Generate a new node to which will generate a video given a sfmData.