[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
# 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):
""" Return whether filepath is a path to an image file supported by Meshroom. """
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):
"""
@ -46,6 +60,80 @@ def findImageFiles(folder, recursive=False):
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=''):
"""
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.')
parser.add_argument('-I', '--importRecursive', metavar='FOLDERS', type=str, nargs='*',
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.')
args = parser.parse_args(args[1:])
@ -101,7 +101,7 @@ class MeshroomApp(QApplication):
self.engine.rootContext().setContextProperty("_nodeTypes", sorted(nodesDesc.keys()))
# instantiate Reconstruction object
r = Reconstruction(parent=self)
r = Reconstruction(defaultPipeline=args.pipeline, parent=self)
self.engine.rootContext().setContextProperty("_reconstruction", r)
# 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
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):
raise RuntimeError(
"Meshroom Command Line Error: 'PROJECT' argument should be a Meshroom project file (.mg).\n"
@ -135,6 +126,8 @@ class MeshroomApp(QApplication):
if args.project:
r.load(args.project)
else:
r.new()
# import is a python keyword, so we have to access the attribute by a string
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 meshroom import multiview
from meshroom.common.qt import QObjectListModel
from meshroom.core.attribute import Attribute, ListAttribute
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.
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)
self._undoStack = commands.UndoStack(self)
self._graph = Graph('', self)
@ -254,9 +255,6 @@ class UIGraph(QObject):
self._layout = GraphLayout(self)
self._selectedNode = None
self._hoveredNode = None
self._defaultPipelineFilepath = None
if filepath:
self.load(filepath)
def setGraph(self, g):
""" Set the internal graph. """
@ -311,10 +309,6 @@ class UIGraph(QObject):
self.stopExecution()
self._chunksMonitor.stop()
def setDefaultPipeline(self, pipelineFilepath):
self._defaultPipelineFilepath = pipelineFilepath
self._graph.load(pipelineFilepath, setupProjectFile=False)
def load(self, filepath, setupProjectFile=True):
g = Graph('')
g.load(filepath, setupProjectFile)

View file

@ -401,12 +401,15 @@ class Reconstruction(UIGraph):
@Slot()
def new(self):
""" Create a new photogrammetry pipeline. """
if self._defaultPipelineFilepath:
# use the user-provided default photogrammetry project file
self.load(self._defaultPipelineFilepath, setupProjectFile=False)
else:
if self._defaultPipeline.lower() == "photogrammetry":
# default photogrammetry pipeline
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):
try:
@ -590,9 +593,11 @@ class Reconstruction(UIGraph):
images.extend(multiview.findImageFiles(localFile))
elif multiview.isImageFile(localFile):
images.append(localFile)
else:
otherFiles.append(localFile)
return images, otherFiles
elif multiview.isVideoFile(localFile):
images.append(localFile)
elif multiview.isPanoramaFile(localFile):
pass
return images
def importImagesFromFolder(self, path, recursive=False):
"""