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
|
||||
|
||||
# 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.
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue