Meshroom/meshroom/multiview.py
2024-09-12 10:50:13 +02:00

239 lines
7.8 KiB
Python

import os
# Supported image extensions
imageExtensions = (
# bmp:
'.bmp',
# cineon:
'.cin',
# dds
'.dds',
# dpx:
'.dpx',
# gif:
'.gif',
# hdr:
'.hdr', '.rgbe',
# heif
'.heic', '.heif', '.avif',
# ico:
'.ico',
# iff:
'.iff', '.z',
# jpeg:
'.jpg', '.jpe', '.jpeg', '.jif', '.jfif', '.jfi',
# jpeg2000:
'.jp2', '.j2k', '.j2c',
# openexr:
'.exr', '.sxr', '.mxr',
# png:
'.png',
# pnm:
'.ppm', '.pgm', '.pbm', '.pnm', '.pfm',
# psd:
'.psd', '.pdd', '.psb',
# ptex:
'.ptex', '.ptx',
# raw:
'.bay', '.bmq', '.cr2', '.cr3', '.crw', '.cs1', '.dc2', '.dcr', '.dng', '.erf', '.fff', '.k25', '.kdc', '.mdc',
'.mos', '.mrw', '.nef', '.orf', '.pef', '.pxn', '.raf', '.raw', '.rdc', '.sr2', '.srf', '.x3f', '.arw', '.3fr',
'.cine', '.ia', '.kc2', '.mef', '.nrw', '.qtk', '.rw2', '.sti', '.rwl', '.srw', '.drf', '.dsc', '.cap', '.iiq',
'.rwz',
# rla:
'.rla',
# sgi:
'.sgi', '.rgb', '.rgba', '.bw', '.int', '.inta',
# socket:
'.socket',
# softimage:
'.pic',
# tiff:
'.tiff', '.tif', '.tx', '.env', '.sm', '.vsm',
# targa:
'.tga', '.tpic',
# webp:
'webp',
# zfile:
'.zfile',
# osl:
'.osl', '.oso', '.oslgroup', '.oslbody',
)
videoExtensions = (
'.avi', '.mov', '.qt',
'.mkv', '.webm',
'.mp4', '.mpg', '.mpeg', '.m2v', '.m4v',
'.wmv',
'.ogv', '.ogg',
'.mxf',
)
panoramaInfoExtensions = ('.xml')
meshroomSceneExtensions = ('.mg')
def hasExtension(filepath, extensions):
""" Return whether filepath is one of the following extensions. """
return os.path.splitext(filepath)[1].lower() in extensions
class FilesByType:
def __init__(self):
self.images = []
self.videos = []
self.panoramaInfo = []
self.meshroomScenes = []
self.other = []
def __bool__(self):
return self.images or self.videos or self.panoramaInfo or self.meshroomScenes
def extend(self, other):
self.images.extend(other.images)
self.videos.extend(other.videos)
self.panoramaInfo.extend(other.panoramaInfo)
self.meshroomScenes.extend(other.meshroomScenes)
self.other.extend(other.other)
def addFile(self, file):
if hasExtension(file, imageExtensions):
self.images.append(file)
elif hasExtension(file, videoExtensions):
self.videos.append(file)
elif hasExtension(file, panoramaInfoExtensions):
self.panoramaInfo.append(file)
elif hasExtension(file, meshroomSceneExtensions):
self.meshroomScenes.append(file)
else:
self.other.append(file)
def addFiles(self, files):
for file in files:
self.addFile(file)
def findFilesByTypeInFolder(folder, recursive=False):
"""
Return all files that are images in 'folder' based on their extensions.
Args:
folder (str): folder to look into or list of folder/files
Returns:
list: the list of image files with a supported extension.
"""
inputFolders = []
if isinstance(folder, (list, tuple)):
inputFolders = folder
else:
inputFolders.append(folder)
output = FilesByType()
for currentFolder in inputFolders:
currentFolder = os.path.abspath(currentFolder)
if os.path.isfile(currentFolder):
output.addFile(currentFolder)
continue
elif os.path.isdir(currentFolder):
if recursive:
for root, directories, files in os.walk(currentFolder):
for filename in files:
output.addFile(os.path.join(root, filename))
else:
output.addFiles([os.path.join(currentFolder, filename) for filename in os.listdir(currentFolder)])
else:
# if not a directory or a file, it may be an expression
import glob
paths = glob.glob(currentFolder)
filesByType = findFilesByTypeInFolder(paths, recursive=recursive)
output.extend(filesByType)
return output
def mvsPipeline(graph, sfm=None):
"""
Instantiate a MVS pipeline inside 'graph'.
Args:
graph (Graph/UIGraph): the graph in which nodes should be instantiated
sfm (Node, optional): if specified, connect the MVS pipeline to this StructureFromMotion node
Returns:
list of Node: the created nodes
"""
if sfm and not sfm.nodeType == "StructureFromMotion":
raise ValueError("Invalid node type. Expected StructureFromMotion, got {}.".format(sfm.nodeType))
prepareDenseScene = graph.addNewNode('PrepareDenseScene',
input=sfm.output if sfm else "")
depthMap = graph.addNewNode('DepthMap',
input=prepareDenseScene.input,
imagesFolder=prepareDenseScene.output)
depthMapFilter = graph.addNewNode('DepthMapFilter',
input=depthMap.input,
depthMapsFolder=depthMap.output)
meshing = graph.addNewNode('Meshing',
input=depthMapFilter.input,
depthMapsFolder=depthMapFilter.output)
meshFiltering = graph.addNewNode('MeshFiltering',
inputMesh=meshing.outputMesh)
texturing = graph.addNewNode('Texturing',
input=meshing.output,
imagesFolder=depthMap.imagesFolder,
inputMesh=meshFiltering.outputMesh)
return [
prepareDenseScene,
depthMap,
depthMapFilter,
meshing,
meshFiltering,
texturing
]
def sfmAugmentation(graph, sourceSfm, withMVS=False):
"""
Create a SfM augmentation inside 'graph'.
Args:
graph (Graph/UIGraph): the graph in which nodes should be instantiated
sourceSfm (Node, optional): if specified, connect the MVS pipeline to this StructureFromMotion node
withMVS (bool): whether to create a MVS pipeline after the augmented SfM branch
Returns:
tuple: the created nodes (sfmNodes, mvsNodes)
"""
cameraInit = graph.addNewNode('CameraInit')
featureExtraction = graph.addNewNode('FeatureExtraction',
input=cameraInit.output)
imageMatchingMulti = graph.addNewNode('ImageMatchingMultiSfM',
input=featureExtraction.input,
featuresFolders=[featureExtraction.output]
)
featureMatching = graph.addNewNode('FeatureMatching',
input=imageMatchingMulti.outputCombinedSfM,
featuresFolders=imageMatchingMulti.featuresFolders,
imagePairsList=imageMatchingMulti.output,
describerTypes=featureExtraction.describerTypes)
structureFromMotion = graph.addNewNode('StructureFromMotion',
input=featureMatching.input,
featuresFolders=featureMatching.featuresFolders,
matchesFolders=[featureMatching.output],
describerTypes=featureMatching.describerTypes)
graph.addEdge(sourceSfm.output, imageMatchingMulti.inputB)
sfmNodes = [
cameraInit,
featureExtraction,
imageMatchingMulti,
featureMatching,
structureFromMotion
]
mvsNodes = []
if withMVS:
mvsNodes = mvsPipeline(graph, structureFromMotion)
return sfmNodes, mvsNodes