mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-06-27 23:17:20 +02:00
[bin] meshroom: option to choose HDRI pipeline
This commit is contained in:
parent
a85730aa45
commit
07f564d6e0
4 changed files with 108 additions and 28 deletions
|
@ -6,13 +6,27 @@ import os
|
||||||
from meshroom.core.graph import Graph, GraphModification
|
from meshroom.core.graph import Graph, GraphModification
|
||||||
|
|
||||||
# Supported image extensions
|
# Supported image extensions
|
||||||
imageExtensions = ('.jpg', '.jpeg', '.tif', '.tiff', '.png', '.exr', '.rw2', '.cr2', '.nef', '.arw', '.dng')
|
imageExtensions = ('.jpg', '.jpeg', '.tif', '.tiff', '.png', '.exr', '.rw2', '.cr2', '.nef', '.arw')
|
||||||
|
videoExtensions = ('.avi', '.mov', '.qt',
|
||||||
|
'.mkv', '.webm',
|
||||||
|
'.mp4', '.mpg', '.mpeg', '.m2v', '.m4v',
|
||||||
|
'.wmv',
|
||||||
|
'.ogv', '.ogg',
|
||||||
|
'.mxf')
|
||||||
|
panoramaExtensions = ('.xml')
|
||||||
|
|
||||||
def isImageFile(filepath):
|
def isImageFile(filepath):
|
||||||
""" Return whether filepath is a path to an image file supported by Meshroom. """
|
""" Return whether filepath is a path to an image file supported by Meshroom. """
|
||||||
return os.path.splitext(filepath)[1].lower() in imageExtensions
|
return os.path.splitext(filepath)[1].lower() in imageExtensions
|
||||||
|
|
||||||
|
def isVideoFile(filepath):
|
||||||
|
""" Return whether filepath is a path to a video file supported by Meshroom. """
|
||||||
|
return os.path.splitext(filepath)[1].lower() in videoExtensions
|
||||||
|
|
||||||
|
def isPanoramaFile(filepath):
|
||||||
|
""" Return whether filepath is a path to a panorama info file supported by Meshroom. """
|
||||||
|
return os.path.splitext(filepath)[1].lower() in panoramaExtensions
|
||||||
|
|
||||||
|
|
||||||
def findImageFiles(folder, recursive=False):
|
def findImageFiles(folder, recursive=False):
|
||||||
"""
|
"""
|
||||||
|
@ -46,6 +60,80 @@ def findImageFiles(folder, recursive=False):
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def hdri(inputImages=list(), inputViewpoints=list(), inputIntrinsics=list(), output=''):
|
||||||
|
"""
|
||||||
|
Create a new Graph with a complete HDRI pipeline.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
inputImages (list of str, optional): list of image file paths
|
||||||
|
inputViewpoints (list of Viewpoint, optional): list of Viewpoints
|
||||||
|
output (str, optional): the path to export reconstructed model to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Graph: the created graph
|
||||||
|
"""
|
||||||
|
graph = Graph('HDRI')
|
||||||
|
with GraphModification(graph):
|
||||||
|
nodes = hdriPipeline(graph)
|
||||||
|
cameraInit = nodes[0]
|
||||||
|
cameraInit.viewpoints.extend([{'path': image} for image in inputImages])
|
||||||
|
cameraInit.viewpoints.extend(inputViewpoints)
|
||||||
|
cameraInit.intrinsics.extend(inputIntrinsics)
|
||||||
|
|
||||||
|
if output:
|
||||||
|
stitching = nodes[-1]
|
||||||
|
graph.addNewNode('Publish', output=output, inputFiles=[stitching.output])
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
def hdriPipeline(graph):
|
||||||
|
"""
|
||||||
|
Instantiate an HDRI pipeline inside 'graph'.
|
||||||
|
Args:
|
||||||
|
graph (Graph/UIGraph): the graph in which nodes should be instantiated
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list of Node: the created nodes
|
||||||
|
"""
|
||||||
|
cameraInit = graph.addNewNode('CameraInit')
|
||||||
|
|
||||||
|
ldr2hdr = graph.addNewNode('LDRToHDR',
|
||||||
|
input=cameraInit.output)
|
||||||
|
|
||||||
|
featureExtraction = graph.addNewNode('FeatureExtraction',
|
||||||
|
input=ldr2hdr.outSfMDataFilename)
|
||||||
|
imageMatching = graph.addNewNode('ImageMatching',
|
||||||
|
input=featureExtraction.input,
|
||||||
|
featuresFolders=[featureExtraction.output])
|
||||||
|
featureMatching = graph.addNewNode('FeatureMatching',
|
||||||
|
input=imageMatching.input,
|
||||||
|
featuresFolders=imageMatching.featuresFolders,
|
||||||
|
imagePairsList=imageMatching.output)
|
||||||
|
|
||||||
|
panoramaExternalInfo = graph.addNewNode('PanoramaExternalInfo',
|
||||||
|
input=ldr2hdr.input)
|
||||||
|
|
||||||
|
panoramaEstimation = graph.addNewNode('PanoramaEstimation',
|
||||||
|
input=panoramaExternalInfo.outSfMDataFilename,
|
||||||
|
featuresFolders=featureMatching.featuresFolders,
|
||||||
|
matchesFolders=[featureMatching.output])
|
||||||
|
|
||||||
|
panoramaStitching = graph.addNewNode('PanoramaStitching',
|
||||||
|
input=panoramaEstimation.outSfMDataFilename)
|
||||||
|
|
||||||
|
return [
|
||||||
|
cameraInit,
|
||||||
|
featureExtraction,
|
||||||
|
imageMatching,
|
||||||
|
featureMatching,
|
||||||
|
panoramaExternalInfo,
|
||||||
|
panoramaEstimation,
|
||||||
|
panoramaStitching,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def photogrammetry(inputImages=list(), inputViewpoints=list(), inputIntrinsics=list(), output=''):
|
def photogrammetry(inputImages=list(), inputViewpoints=list(), inputIntrinsics=list(), output=''):
|
||||||
"""
|
"""
|
||||||
Create a new Graph with a complete photogrammetry pipeline.
|
Create a new Graph with a complete photogrammetry pipeline.
|
||||||
|
|
|
@ -65,7 +65,7 @@ class MeshroomApp(QApplication):
|
||||||
help='Import images or folder with images to reconstruct.')
|
help='Import images or folder with images to reconstruct.')
|
||||||
parser.add_argument('-I', '--importRecursive', metavar='FOLDERS', type=str, nargs='*',
|
parser.add_argument('-I', '--importRecursive', metavar='FOLDERS', type=str, nargs='*',
|
||||||
help='Import images to reconstruct from specified folder and sub-folders.')
|
help='Import images to reconstruct from specified folder and sub-folders.')
|
||||||
parser.add_argument('-p', '--pipeline', metavar='MESHROOM_FILE', type=str, required=False,
|
parser.add_argument('-p', '--pipeline', metavar='MESHROOM_FILE/photogrammetry/hdri', type=str, default=os.environ.get("MESHROOM_DEFAULT_PIPELINE", "photogrammetry"),
|
||||||
help='Override the default Meshroom pipeline with this external graph.')
|
help='Override the default Meshroom pipeline with this external graph.')
|
||||||
|
|
||||||
args = parser.parse_args(args[1:])
|
args = parser.parse_args(args[1:])
|
||||||
|
@ -101,7 +101,7 @@ class MeshroomApp(QApplication):
|
||||||
self.engine.rootContext().setContextProperty("_nodeTypes", sorted(nodesDesc.keys()))
|
self.engine.rootContext().setContextProperty("_nodeTypes", sorted(nodesDesc.keys()))
|
||||||
|
|
||||||
# instantiate Reconstruction object
|
# instantiate Reconstruction object
|
||||||
r = Reconstruction(parent=self)
|
r = Reconstruction(defaultPipeline=args.pipeline, parent=self)
|
||||||
self.engine.rootContext().setContextProperty("_reconstruction", r)
|
self.engine.rootContext().setContextProperty("_reconstruction", r)
|
||||||
|
|
||||||
# those helpers should be available from QML Utils module as singletons, but:
|
# those helpers should be available from QML Utils module as singletons, but:
|
||||||
|
@ -119,15 +119,6 @@ class MeshroomApp(QApplication):
|
||||||
# request any potential computation to stop on exit
|
# request any potential computation to stop on exit
|
||||||
self.aboutToQuit.connect(r.stopChildThreads)
|
self.aboutToQuit.connect(r.stopChildThreads)
|
||||||
|
|
||||||
if args.pipeline:
|
|
||||||
# the pipeline from the command line has the priority
|
|
||||||
r.setDefaultPipeline(args.pipeline)
|
|
||||||
else:
|
|
||||||
# consider the environment variable
|
|
||||||
defaultPipeline = os.environ.get("MESHROOM_DEFAULT_PIPELINE", "")
|
|
||||||
if defaultPipeline:
|
|
||||||
r.setDefaultPipeline(args.pipeline)
|
|
||||||
|
|
||||||
if args.project and not os.path.isfile(args.project):
|
if args.project and not os.path.isfile(args.project):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Meshroom Command Line Error: 'PROJECT' argument should be a Meshroom project file (.mg).\n"
|
"Meshroom Command Line Error: 'PROJECT' argument should be a Meshroom project file (.mg).\n"
|
||||||
|
@ -135,6 +126,8 @@ class MeshroomApp(QApplication):
|
||||||
|
|
||||||
if args.project:
|
if args.project:
|
||||||
r.load(args.project)
|
r.load(args.project)
|
||||||
|
else:
|
||||||
|
r.new()
|
||||||
|
|
||||||
# import is a python keyword, so we have to access the attribute by a string
|
# import is a python keyword, so we have to access the attribute by a string
|
||||||
if getattr(args, "import", None):
|
if getattr(args, "import", None):
|
||||||
|
|
|
@ -9,6 +9,7 @@ from multiprocessing.pool import ThreadPool
|
||||||
|
|
||||||
from PySide2.QtCore import Slot, QJsonValue, QObject, QUrl, Property, Signal, QPoint
|
from PySide2.QtCore import Slot, QJsonValue, QObject, QUrl, Property, Signal, QPoint
|
||||||
|
|
||||||
|
from meshroom import multiview
|
||||||
from meshroom.common.qt import QObjectListModel
|
from meshroom.common.qt import QObjectListModel
|
||||||
from meshroom.core.attribute import Attribute, ListAttribute
|
from meshroom.core.attribute import Attribute, ListAttribute
|
||||||
from meshroom.core.graph import Graph, Edge, submitGraph, executeGraph
|
from meshroom.core.graph import Graph, Edge, submitGraph, executeGraph
|
||||||
|
@ -242,7 +243,7 @@ class UIGraph(QObject):
|
||||||
UIGraph exposes undoable methods on its graph and computation in a separate thread.
|
UIGraph exposes undoable methods on its graph and computation in a separate thread.
|
||||||
It also provides a monitoring of all its computation units (NodeChunks).
|
It also provides a monitoring of all its computation units (NodeChunks).
|
||||||
"""
|
"""
|
||||||
def __init__(self, filepath='', parent=None):
|
def __init__(self, parent=None):
|
||||||
super(UIGraph, self).__init__(parent)
|
super(UIGraph, self).__init__(parent)
|
||||||
self._undoStack = commands.UndoStack(self)
|
self._undoStack = commands.UndoStack(self)
|
||||||
self._graph = Graph('', self)
|
self._graph = Graph('', self)
|
||||||
|
@ -254,9 +255,6 @@ class UIGraph(QObject):
|
||||||
self._layout = GraphLayout(self)
|
self._layout = GraphLayout(self)
|
||||||
self._selectedNode = None
|
self._selectedNode = None
|
||||||
self._hoveredNode = None
|
self._hoveredNode = None
|
||||||
self._defaultPipelineFilepath = None
|
|
||||||
if filepath:
|
|
||||||
self.load(filepath)
|
|
||||||
|
|
||||||
def setGraph(self, g):
|
def setGraph(self, g):
|
||||||
""" Set the internal graph. """
|
""" Set the internal graph. """
|
||||||
|
@ -311,10 +309,6 @@ class UIGraph(QObject):
|
||||||
self.stopExecution()
|
self.stopExecution()
|
||||||
self._chunksMonitor.stop()
|
self._chunksMonitor.stop()
|
||||||
|
|
||||||
def setDefaultPipeline(self, pipelineFilepath):
|
|
||||||
self._defaultPipelineFilepath = pipelineFilepath
|
|
||||||
self._graph.load(pipelineFilepath, setupProjectFile=False)
|
|
||||||
|
|
||||||
def load(self, filepath, setupProjectFile=True):
|
def load(self, filepath, setupProjectFile=True):
|
||||||
g = Graph('')
|
g = Graph('')
|
||||||
g.load(filepath, setupProjectFile)
|
g.load(filepath, setupProjectFile)
|
||||||
|
|
|
@ -401,12 +401,15 @@ class Reconstruction(UIGraph):
|
||||||
@Slot()
|
@Slot()
|
||||||
def new(self):
|
def new(self):
|
||||||
""" Create a new photogrammetry pipeline. """
|
""" Create a new photogrammetry pipeline. """
|
||||||
if self._defaultPipelineFilepath:
|
if self._defaultPipeline.lower() == "photogrammetry":
|
||||||
# use the user-provided default photogrammetry project file
|
|
||||||
self.load(self._defaultPipelineFilepath, setupProjectFile=False)
|
|
||||||
else:
|
|
||||||
# default photogrammetry pipeline
|
# default photogrammetry pipeline
|
||||||
self.setGraph(multiview.photogrammetry())
|
self.setGraph(multiview.photogrammetry())
|
||||||
|
elif self._defaultPipeline.lower() == "hdri":
|
||||||
|
# default hdri pipeline
|
||||||
|
self.setGraph(multiview.hdri())
|
||||||
|
else:
|
||||||
|
# use the user-provided default photogrammetry project file
|
||||||
|
self.load(self._defaultPipeline, setupProjectFile=False)
|
||||||
|
|
||||||
def load(self, filepath, setupProjectFile=True):
|
def load(self, filepath, setupProjectFile=True):
|
||||||
try:
|
try:
|
||||||
|
@ -590,9 +593,11 @@ class Reconstruction(UIGraph):
|
||||||
images.extend(multiview.findImageFiles(localFile))
|
images.extend(multiview.findImageFiles(localFile))
|
||||||
elif multiview.isImageFile(localFile):
|
elif multiview.isImageFile(localFile):
|
||||||
images.append(localFile)
|
images.append(localFile)
|
||||||
else:
|
elif multiview.isVideoFile(localFile):
|
||||||
otherFiles.append(localFile)
|
images.append(localFile)
|
||||||
return images, otherFiles
|
elif multiview.isPanoramaFile(localFile):
|
||||||
|
pass
|
||||||
|
return images
|
||||||
|
|
||||||
def importImagesFromFolder(self, path, recursive=False):
|
def importImagesFromFolder(self, path, recursive=False):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue