[bin] meshroom: option to choose HDRI pipeline

This commit is contained in:
Fabien Castan 2019-11-04 00:30:22 +01:00
parent a85730aa45
commit 07f564d6e0
4 changed files with 108 additions and 28 deletions

View file

@ -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.

View file

@ -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):

View file

@ -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)

View file

@ -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):
""" """