Skip to content

Commit 09abbbe

Browse files
author
Fabien Servant
committed
new node generateVideo from sfmData
1 parent 6a9fc0e commit 09abbbe

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
__version__ = "1.0"
2+
3+
from meshroom.core import desc
4+
from meshroom.core.utils import VERBOSE_LEVEL
5+
6+
class GenerateVideo(desc.CommandLineNode):
7+
category = "Video Utils"
8+
documentation = """ Generate a video from a sfmData ordered by frameId """
9+
10+
inputs = [
11+
desc.File(
12+
name="input",
13+
label="Input",
14+
description="SfmData used to get the list of images.",
15+
value="",
16+
),
17+
desc.FloatParam(
18+
name="frameRate",
19+
label="Camera Frame Rate",
20+
description="Define the camera's Frames per second.",
21+
value=24.0,
22+
range=(1.0, 60.0, 1.0),
23+
),
24+
desc.ChoiceParam(
25+
name="verboseLevel",
26+
label="Verbose Level",
27+
description="Verbosity level (fatal, error, warning, info, debug, trace).",
28+
values=VERBOSE_LEVEL,
29+
value="info",
30+
)
31+
]
32+
33+
outputs = [
34+
desc.File(
35+
name='directory',
36+
label='Intermediate directory',
37+
description="Intermediate directory",
38+
value="{nodeCacheFolder}",
39+
),
40+
desc.File(
41+
name='outputVideo',
42+
label='Output Video',
43+
description="Generated video.",
44+
value="{nodeCacheFolder}/output.mp4",
45+
)
46+
]
47+
48+
def processChunk(self, chunk):
49+
50+
from pathlib import Path
51+
52+
import logging
53+
logging.getLogger().setLevel(chunk.node.verboseLevel.value.upper())
54+
55+
from pyalicevision import sfmDataIO as avsfmdataio
56+
from pyalicevision import sfmData as avsfmdata
57+
58+
logging.info("Open input file")
59+
data = avsfmdata.SfMData()
60+
ret = avsfmdataio.load(data, chunk.node.input.value, avsfmdataio.VIEWS)
61+
if not ret:
62+
logging.error("Cannot open input")
63+
raise RuntimeError()
64+
65+
# store all image path in sfmData indexed by frame id
66+
views = data.getViews()
67+
viewsPerFrameId = {}
68+
for viewId in views:
69+
view = views[viewId]
70+
frameId = view.getFrameId()
71+
viewsPerFrameId[frameId] = view.getImage().getImagePath()
72+
73+
74+
# sort path per frameId
75+
viewsPerFrameId = dict(sorted(viewsPerFrameId.items()))
76+
# Check intermediate directory
77+
intermediate_directory = Path(chunk.node.directory.value)
78+
if not (intermediate_directory.exists() and intermediate_directory.is_dir()):
79+
logging.error("Intermediate directory is not valid")
80+
raise RuntimeError()
81+
82+
pos = 0
83+
suffix = ""
84+
for frameId, path in viewsPerFrameId.items():
85+
target = Path(path)
86+
if suffix == "":
87+
suffix = target.suffix
88+
if target.suffix.lower() != suffix.lower():
89+
logging.error("Multiple image types found.")
90+
raise RuntimeError()
91+
92+
# Create a link using a sequential order
93+
link = intermediate_directory / f"{pos:012d}{suffix}"
94+
95+
# Force symlink creation
96+
if link.exists() or link.is_symlink():
97+
link.unlink()
98+
99+
# Create symbolic link
100+
link.symlink_to(target)
101+
pos = pos + 1
102+
103+
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}"
104+
desc.CommandLineNode.processChunk(self, chunk)

0 commit comments

Comments
 (0)