diff --git a/README.md b/README.md index bb5ec25b..c1de1271 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ If you use this project for a publication, please cite the [paper](https://hal.a ``` @inproceedings{alicevision2021, title={{A}liceVision {M}eshroom: An open-source {3D} reconstruction pipeline}, - authors={Carsten Griwodz and Simone Gasparini and Lilian Calvet and Pierre Gurdjos and Fabien Castan and Benoit Maujean and Gregoire De Lillo and Yann Lanthony}, + author={Carsten Griwodz and Simone Gasparini and Lilian Calvet and Pierre Gurdjos and Fabien Castan and Benoit Maujean and Gregoire De Lillo and Yann Lanthony}, booktitle={Proceedings of the 12th ACM Multimedia Systems Conference - {MMSys '21}}, doi = {10.1145/3458305.3478443}, publisher = {ACM Press}, diff --git a/bin/meshroom_batch b/bin/meshroom_batch index 5e141495..7a917039 100755 --- a/bin/meshroom_batch +++ b/bin/meshroom_batch @@ -177,7 +177,7 @@ with multiview.GraphModification(graph): if args.paramOverrides: print("\n") import re - reExtract = re.compile('(\w+)([:.])(\w+)=(.*)') + reExtract = re.compile('(\w+)([:.])(\w[\w.]*)=(.*)') for p in args.paramOverrides: result = reExtract.match(p) if not result: diff --git a/bin/meshroom_newNodeType b/bin/meshroom_newNodeType index 2ee23f27..7ba37b4e 100755 --- a/bin/meshroom_newNodeType +++ b/bin/meshroom_newNodeType @@ -5,6 +5,7 @@ import argparse import os import re import sys +import shlex from pprint import pprint def trim(s): @@ -60,7 +61,7 @@ parser.add_argument('node', metavar='NODE_NAME', type=str, help='New node name') parser.add_argument('bin', metavar='CMDLINE', type=str, default=None, - help='Output plugin folder') + help='Input executable') parser.add_argument('--output', metavar='DIR', type=str, default=os.path.dirname(__file__), help='Output plugin folder') @@ -77,7 +78,7 @@ soft = "{nodeType}" if args.bin: soft = args.bin import subprocess - proc = subprocess.Popen(args=[args.bin, '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen(args=shlex.split(args.bin) + ['--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() inputCmdLineDoc = stdout if stdout else stderr elif sys.stdin.isatty(): @@ -137,7 +138,7 @@ choiceValues1_re = re.compile('\* (?P\w+):') choiceValues2_re = re.compile('\((?P.+?)\)') choiceValues3_re = re.compile('\{(?P.+?)\}') -cmdLineArgs = args_re.findall(inputCmdLineDoc) +cmdLineArgs = args_re.findall(inputCmdLineDoc.decode('utf-8')) print('='*80) pprint(cmdLineArgs) diff --git a/meshroom/core/__init__.py b/meshroom/core/__init__.py index 7f9760d3..834ace0a 100644 --- a/meshroom/core/__init__.py +++ b/meshroom/core/__init__.py @@ -272,9 +272,17 @@ def loadSubmitters(folder, packageName): meshroomFolder = os.path.dirname(os.path.dirname(__file__)) +additionalNodesPath = os.environ.get("MESHROOM_NODES_PATH", "").split(os.pathsep) +# filter empty strings +additionalNodesPath = [i for i in additionalNodesPath if i] + # Load plugins: # - Nodes -loadAllNodes(folder=os.path.join(meshroomFolder, 'nodes')) +nodesFolders = [os.path.join(meshroomFolder, 'nodes')] + additionalNodesPath + +for f in nodesFolders: + loadAllNodes(folder=f) + # - Submitters subs = loadSubmitters(os.environ.get("MESHROOM_SUBMITTERS_PATH", meshroomFolder), 'submitters') diff --git a/meshroom/core/desc.py b/meshroom/core/desc.py old mode 100644 new mode 100755 index f74b7a03..66b3d80c --- a/meshroom/core/desc.py +++ b/meshroom/core/desc.py @@ -5,6 +5,8 @@ import math import os import psutil import ast +import distutils.util + class Attribute(BaseObject): """ @@ -191,7 +193,10 @@ class BoolParam(Param): def validateValue(self, value): try: - return bool(int(value)) # int cast is useful to handle string values ('0', '1') + if isinstance(value, pyCompatibility.basestring): + # use distutils.util.strtobool to handle (1/0, true/false, on/off, y/n) + return bool(distutils.util.strtobool(value)) + return bool(value) except: raise ValueError('BoolParam only supports bool value (param:{}, value:{}, type:{})'.format(self.name, value, type(value))) diff --git a/meshroom/core/stats.py b/meshroom/core/stats.py index 4a3b9d11..046b1c5c 100644 --- a/meshroom/core/stats.py +++ b/meshroom/core/stats.py @@ -95,23 +95,34 @@ class ComputerStatistics: return try: p = subprocess.Popen([self.nvidia_smi, "-q", "-x"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - xmlGpu, stdError = p.communicate(timeout=10) # 10 seconds + if sys.version_info[0] == 2: + # no timeout in python-2 + xmlGpu, stdError = p.communicate() + else: + xmlGpu, stdError = p.communicate(timeout=10) # 10 seconds smiTree = ET.fromstring(xmlGpu) gpuTree = smiTree.find('gpu') try: - self._addKV('gpuMemoryUsed', gpuTree.find('fb_memory_usage').find('used').text.split(" ")[0]) + gpuMemoryUsed = gpuTree.find('fb_memory_usage').find('used').text.split(" ")[0] + self._addKV('gpuMemoryUsed', gpuMemoryUsed) except Exception as e: logging.debug('Failed to get gpuMemoryUsed: "{}".'.format(str(e))) pass try: - self._addKV('gpuUsed', gpuTree.find('utilization').find('gpu_util').text.split(" ")[0]) + self.gpuMemoryTotal = gpuTree.find('fb_memory_usage').find('total').text.split(" ")[0] + except Exception as e: + pass + try: + gpuUsed = gpuTree.find('utilization').find('gpu_util').text.split(" ")[0] + self._addKV('gpuUsed', gpuUsed) except Exception as e: logging.debug('Failed to get gpuUsed: "{}".'.format(str(e))) pass try: - self._addKV('gpuTemperature', gpuTree.find('temperature').find('gpu_temp').text.split(" ")[0]) + gpuTemperature = gpuTree.find('temperature').find('gpu_temp').text.split(" ")[0] + self._addKV('gpuTemperature', gpuTemperature) except Exception as e: logging.debug('Failed to get gpuTemperature: "{}".'.format(str(e))) pass @@ -191,7 +202,7 @@ class ProcStatistics: data = proc.as_dict(self.dynamicKeys) for k, v in data.items(): self._addKV(k, v) - + ## Note: Do not collect stats about open files for now, # as there is bug in psutil-5.7.2 on Windows which crashes the application. # https://github.com/giampaolo/psutil/issues/1763 diff --git a/meshroom/multiview.py b/meshroom/multiview.py index c85dc79f..b8ad1821 100644 --- a/meshroom/multiview.py +++ b/meshroom/multiview.py @@ -136,7 +136,7 @@ def findFilesByTypeInFolder(folder, recursive=False): else: output.addFiles([os.path.join(currentFolder, filename) for filename in os.listdir(currentFolder)]) else: - # if not a diretory or a file, it may be an expression + # if not a directory or a file, it may be an expression import glob paths = glob.glob(currentFolder) filesByType = findFilesByTypeInFolder(paths, recursive=recursive) @@ -186,7 +186,7 @@ def panoramaFisheyeHdr(inputImages=None, inputViewpoints=None, inputIntrinsics=N # when using fisheye images, 'sift' performs better than 'dspsift' featureExtraction.attribute("describerTypes").value = ['sift'] # when using fisheye images, the overlap between images can be small - # and thus requires many features to get enough correspondances for cameras estimation + # and thus requires many features to get enough correspondences for cameras estimation featureExtraction.attribute("describerPreset").value = 'high' return graph diff --git a/meshroom/nodes/aliceVision/CameraInit.py b/meshroom/nodes/aliceVision/CameraInit.py index a5fcb8fc..91cf5fef 100644 --- a/meshroom/nodes/aliceVision/CameraInit.py +++ b/meshroom/nodes/aliceVision/CameraInit.py @@ -1,4 +1,4 @@ -__version__ = "6.0" +__version__ = "7.0" import os import json @@ -28,16 +28,17 @@ Viewpoint = [ Intrinsic = [ desc.IntParam(name="intrinsicId", label="Id", description="Intrinsic UID", value=-1, uid=[0], range=None), - desc.FloatParam(name="pxInitialFocalLength", label="Initial Focal Length", + desc.FloatParam(name="initialFocalLength", label="Initial Focal Length", description="Initial Guess on the Focal Length (in pixels). \n" "When we have an initial value from EXIF, this value is not accurate but cannot be wrong. \n" "So this value is used to limit the range of possible values in the optimization. \n" "If you put -1, this value will not be used and the focal length will not be bounded.", value=-1.0, uid=[0], range=None), - desc.GroupAttribute(name="pxFocalLength", label="Focal Length", description="Known/Calibrated Focal Length (in pixels)", groupDesc=[ - desc.FloatParam(name="x", label="x", description="", value=-1, uid=[], range=(0, 10000, 1)), - desc.FloatParam(name="y", label="y", description="", value=-1, uid=[], range=(0, 10000, 1)), - ]), + desc.FloatParam(name="focalLength", label="Focal Length", description="Known/Calibrated Focal Length (in mm)", value=1000, uid=[], range=(0, 10000, 1)), + desc.FloatParam(name="pixelRatio", label="pixel Ratio", description="ratio between pixel width and pixel height", value=1, uid=[], range=(0, 10, 0.1)), + desc.BoolParam(name='pixelRatioLocked', label='Pixel ratio Locked', + description='the pixelRatio value is locked for estimation', + value=True, uid=[0]), desc.ChoiceParam(name="type", label="Camera Type", description="Mathematical Model used to represent a camera:\n" " * pinhole: Simplest projective camera model without optical distortion (focal and optical center).\n" @@ -107,11 +108,6 @@ def readSfMData(sfmFile): intrinsic['principalPoint']['x'] = pp[0] intrinsic['principalPoint']['y'] = pp[1] - f = intrinsic['pxFocalLength'] - intrinsic['pxFocalLength'] = {} - intrinsic['pxFocalLength']['x'] = f[0] - intrinsic['pxFocalLength']['y'] = f[1] - # convert empty string distortionParams (i.e: Pinhole model) to empty list if intrinsic['distortionParams'] == '': intrinsic['distortionParams'] = list() @@ -255,12 +251,6 @@ The metadata needed are: ] def upgradeAttributeValues(self, attrValues, fromVersion): - # Starting with version 5, the focal length is now split on x and y - if fromVersion < Version(5, 0): - for intrinsic in attrValues['intrinsics']: - pxFocalLength = intrinsic['pxFocalLength'] - if not isinstance(pxFocalLength, dict): - intrinsic['pxFocalLength'] = {"x": pxFocalLength, "y": pxFocalLength} # Starting with version 6, the principal point is now relative to the image center if fromVersion < Version(6, 0): @@ -271,6 +261,20 @@ The metadata needed are: "y": int(principalPoint["y"] - 0.5 * intrinsic['height']) } + # Starting with version 7, the focal length is now in mm + if fromVersion < Version(7, 0): + for intrinsic in attrValues['intrinsics']: + pxInitialFocalLength = intrinsic['pxInitialFocalLength'] + pxFocalLength = intrinsic['pxFocalLength'] + sensorWidth = intrinsic['sensorWidth'] + width = intrinsic['width'] + focalLength = (pxFocalLength / width) * sensorWidth + initialFocalLength = (pxInitialFocalLength / width) * sensorWidth + intrinsic['initialFocalLength'] = initialFocalLength + intrinsic['focalLength'] = focalLength + intrinsic['pixelRatio'] = 1.0 + intrinsic['pixelRatioLocked'] = False + return attrValues def readSfMData(self, sfmFile): @@ -329,7 +333,6 @@ The metadata needed are: intrinsics = node.intrinsics.getPrimitiveValue(exportDefault=True) for intrinsic in intrinsics: intrinsic['principalPoint'] = [intrinsic['principalPoint']['x'], intrinsic['principalPoint']['y']] - intrinsic['pxFocalLength'] = [intrinsic['pxFocalLength']['x'], intrinsic['pxFocalLength']['y']] views = node.viewpoints.getPrimitiveValue(exportDefault=False) # convert the metadata string into a map @@ -338,7 +341,7 @@ The metadata needed are: view['metadata'] = json.loads(view['metadata']) sfmData = { - "version": [1, 2, 1], + "version": [1, 2, 2], "views": views + newViews, "intrinsics": intrinsics, "featureFolder": "", diff --git a/meshroom/nodes/aliceVision/ColorCheckerDetection.py b/meshroom/nodes/aliceVision/ColorCheckerDetection.py index f33c653a..f3109cfe 100644 --- a/meshroom/nodes/aliceVision/ColorCheckerDetection.py +++ b/meshroom/nodes/aliceVision/ColorCheckerDetection.py @@ -17,7 +17,7 @@ Performs Macbeth color checker chart detection. Outputs: - the detected color charts position and colors -- the associated tranform matrix from "theoric" to "measured" +- the associated transform matrix from "theoric" to "measured" assuming that the "theoric" Macbeth chart corners coordinates are: (0, 0), (1675, 0), (1675, 1125), (0, 1125) diff --git a/meshroom/nodes/aliceVision/DepthMap.py b/meshroom/nodes/aliceVision/DepthMap.py index bb6ca66e..dbccfe66 100644 --- a/meshroom/nodes/aliceVision/DepthMap.py +++ b/meshroom/nodes/aliceVision/DepthMap.py @@ -63,6 +63,38 @@ Use a downscale factor of one (full-resolution) only if the quality of the input uid=[0], advanced=True, ), + desc.IntParam( + name='sgmScale', + label='SGM: Downscale factor', + description='Semi Global Matching: Downscale factor used to compute the similarity volume.', + value=-1, + range=(-1, 10, 1), + uid=[0], + ), + desc.IntParam( + name='sgmStepXY', + label='SGM: Step XY', + description='Semi Global Matching: Step used to compute the similarity volume on X and Y axis.', + value=-1, + range=(-1, 10, 1), + uid=[0], + ), + desc.IntParam( + name='sgmStepZ', + label='SGM: Step Z', + description='Semi Global Matching: Step used to compute the similarity volume on Z axis.', + value=-1, + range=(-1, 10, 1), + uid=[0], + ), + desc.IntParam( + name='sgmMaxSideXY', + label='SGM: Max Side', + description='Semi Global Matching: Max side in pixels used to automatically decide for sgmScale/sgmStep if not defined.', + value=700, + range=(-1, 1000, 1), + uid=[0], + ), desc.IntParam( name='sgmMaxTCams', label='SGM: Nb Neighbour Cameras', @@ -98,6 +130,59 @@ Use a downscale factor of one (full-resolution) only if the quality of the input uid=[0], advanced=True, ), + + desc.FloatParam( + name='sgmP1', + label='SGM: P1', + description='Semi Global Matching: P1.', + value=10.0, + range=(0.0, 255.0, 0.5), + uid=[0], + advanced=True, + ), + desc.FloatParam( + name='sgmP2', + label='SGM: P2', + description='Semi Global Matching: P2 weight.', + value=100.0, + range=(-255.0, 255.0, 0.5), + uid=[0], + advanced=True, + ), + desc.IntParam( + name='sgmMaxDepths', + label='SGM: Max Depths', + description='Semi Global Matching: Max number of depths in the overall similarity volume.', + value=3000, + range=(1, 5000, 1), + uid=[0], + advanced=True, + ), + desc.IntParam( + name='sgmMaxDepthsPerTc', + label='SGM: Max Depths Per Camera Pairs', + description='Semi Global Matching: Max number of depths to sweep in the similarity volume per Rc/Tc cameras.', + value=1500, + range=(1, 5000, 1), + uid=[0], + advanced=True, + ), + desc.BoolParam( + name='sgmUseSfmSeeds', + label='SGM: Use SfM Landmarks', + description='Semi Global Matching: Use landmarks from SfM to define the ranges for the plane sweeping.', + value=True, + uid=[0], + advanced=True, + ), + desc.StringParam( + name='sgmFilteringAxes', + label='SGM: Filtering Axes', + description="Semi Global Matching: Define axes for the filtering of the similarity volume.", + value='YX', + uid=[0], + advanced=True, + ), desc.IntParam( name='refineMaxTCams', label='Refine: Nb Neighbour Cameras', diff --git a/meshroom/nodes/aliceVision/DepthMapFilter.py b/meshroom/nodes/aliceVision/DepthMapFilter.py index d2d9c313..f69de340 100644 --- a/meshroom/nodes/aliceVision/DepthMapFilter.py +++ b/meshroom/nodes/aliceVision/DepthMapFilter.py @@ -74,6 +74,15 @@ This allows to filter unstable points before starting the fusion of all depth ma range=(0, 10, 1), uid=[0], ), + desc.FloatParam( + name='pixToleranceFactor', + label='Tolerance Size', + description='Filtering tolerance size factor (in px).', + value=2.0, + range=(0.001, 10.0, 0.1), + uid=[0], + advanced=True, + ), desc.IntParam( name="pixSizeBall", label="Filtering Size in Pixels", diff --git a/meshroom/nodes/aliceVision/FeatureMatching.py b/meshroom/nodes/aliceVision/FeatureMatching.py index fdd4f634..831dda2d 100644 --- a/meshroom/nodes/aliceVision/FeatureMatching.py +++ b/meshroom/nodes/aliceVision/FeatureMatching.py @@ -170,7 +170,7 @@ then it checks the number of features that validates this model and iterate thro desc.BoolParam( name='crossMatching', label='Cross Matching', - description='Make sure that the matching process is symmetric (same matches for I->J than fo J->I)', + description='Make sure that the matching process is symmetric (same matches for I->J than for J->I)', value=False, uid=[0], ), diff --git a/meshroom/nodes/aliceVision/PanoramaSeams.py b/meshroom/nodes/aliceVision/PanoramaSeams.py index 326d7959..c36beb92 100644 --- a/meshroom/nodes/aliceVision/PanoramaSeams.py +++ b/meshroom/nodes/aliceVision/PanoramaSeams.py @@ -43,7 +43,7 @@ Estimate the seams lines between the inputs to provide an optimal compositing in desc.BoolParam( name='useGraphCut', label='Use Smart Seams', - description='Use a graphcut algorithm to optmize seams for better transitions between images.', + description='Use a graphcut algorithm to optimize seams for better transitions between images.', value=True, uid=[0], ), diff --git a/meshroom/nodes/aliceVision/SketchfabUpload.py b/meshroom/nodes/aliceVision/SketchfabUpload.py index 2d980e4d..69d75aa2 100644 --- a/meshroom/nodes/aliceVision/SketchfabUpload.py +++ b/meshroom/nodes/aliceVision/SketchfabUpload.py @@ -200,7 +200,7 @@ Upload a textured mesh on Sketchfab. modelEndpoint, **{'data': body, 'headers': headers}) chunk.logManager.completeProgressBar() except requests.exceptions.RequestException as e: - chunk.logger.error(u'An error occured: {}'.format(e)) + chunk.logger.error(u'An error occurred: {}'.format(e)) raise RuntimeError() if r.status_code != requests.codes.created: chunk.logger.error(u'Upload failed with error: {}'.format(r.json())) diff --git a/meshroom/nodes/aliceVision/Split360Images.py b/meshroom/nodes/aliceVision/Split360Images.py index 2b351b78..daee4598 100644 --- a/meshroom/nodes/aliceVision/Split360Images.py +++ b/meshroom/nodes/aliceVision/Split360Images.py @@ -6,7 +6,7 @@ class Split360Images(desc.CommandLineNode): commandLine = 'aliceVision_utils_split360Images {allParams}' category = 'Utils' - documentation = '''This node is used to extract multiple images from equirectangular or dualfisheye images or image folder''' + documentation = "This node is used to extract multiple images from equirectangular or dualfisheye images." inputs = [ desc.File( diff --git a/meshroom/nodes/aliceVision/StructureFromMotion.py b/meshroom/nodes/aliceVision/StructureFromMotion.py index d029656d..03b35ad1 100644 --- a/meshroom/nodes/aliceVision/StructureFromMotion.py +++ b/meshroom/nodes/aliceVision/StructureFromMotion.py @@ -116,7 +116,7 @@ It iterates like that, adding cameras and triangulating new 2D features into 3D desc.ChoiceParam( name='observationConstraint', label='Observation Constraint', - description='Observation contraint mode used in the optimization:\n' + description='Observation constraint mode used in the optimization:\n' ' * Basic: Use standard reprojection error in pixel coordinates\n' ' * Scale: Use reprojection error in pixel coordinates but relative to the feature scale', value='Scale', @@ -308,6 +308,13 @@ It iterates like that, adding cameras and triangulating new 2D features into 3D value=False, uid=[0], ), + desc.BoolParam( + name='computeStructureColor', + label='Compute Structure Color', + description='Enable/Disable color computation of each 3D point.', + value=True, + uid=[0], + ), desc.File( name='initialPairA', label='Initial Pair A', diff --git a/meshroom/nodes/aliceVision/Texturing.py b/meshroom/nodes/aliceVision/Texturing.py index 894e6a2b..34ed45d4 100644 --- a/meshroom/nodes/aliceVision/Texturing.py +++ b/meshroom/nodes/aliceVision/Texturing.py @@ -273,7 +273,7 @@ Many cameras are contributing to the low frequencies and only the best ones cont desc.BoolParam( name='forceVisibleByAllVertices', label='Force Visible By All Vertices', - description='''Triangle visibility is based on the union of vertices visiblity.''', + description='''Triangle visibility is based on the union of vertices visibility.''', value=False, uid=[0], advanced=True, diff --git a/meshroom/nodes/blender/scripts/renderAnimatedCameraInBlender.py b/meshroom/nodes/blender/scripts/renderAnimatedCameraInBlender.py index 7549c990..e580aac3 100644 --- a/meshroom/nodes/blender/scripts/renderAnimatedCameraInBlender.py +++ b/meshroom/nodes/blender/scripts/renderAnimatedCameraInBlender.py @@ -31,7 +31,7 @@ def main(): parser.add_argument( "--useBackground", type=strtobool, required=True, - help="Diplay the background image or not.", + help="Display the background image or not.", ) parser.add_argument( @@ -262,8 +262,8 @@ def main(): # For showing an outline of the object, we need to add two materials to the mesh: # Center and Edge, we are using a method that consists in having a "bold" effect on the Edge Material so we can see it - # around the Center material. We use a Solidify Modifier on which we flip normals and reduce Thickness to bellow zero. - # The more the thickness get bellow zero, the more the egde will be largely revealed. + # around the Center material. We use a Solidify Modifier on which we flip normals and reduce Thickness to below zero. + # The more the thickness get below zero, the more the edge will be largely revealed. elif args.model.lower().endswith('.obj'): print("Import OBJ") diff --git a/meshroom/submitters/simpleFarmConfig.json b/meshroom/submitters/simpleFarmConfig.json index 67fd4036..c5c2d5fe 100644 --- a/meshroom/submitters/simpleFarmConfig.json +++ b/meshroom/submitters/simpleFarmConfig.json @@ -12,7 +12,7 @@ }, "GPU": { "NONE": [], - "NORMAL": ["!\"*rnd*\""], - "INTENSIVE": ["!\"*rnd*\"", "@.nCPUs>=12"] + "NORMAL": ["cuda8G"], + "INTENSIVE": ["cuda16G"] } } diff --git a/meshroom/submitters/simpleFarmSubmitter.py b/meshroom/submitters/simpleFarmSubmitter.py index fc46bf8c..b04a4990 100644 --- a/meshroom/submitters/simpleFarmSubmitter.py +++ b/meshroom/submitters/simpleFarmSubmitter.py @@ -3,6 +3,7 @@ import os import json +import logging import simpleFarm from meshroom.core.desc import Level @@ -12,14 +13,12 @@ currentDir = os.path.dirname(os.path.realpath(__file__)) binDir = os.path.dirname(os.path.dirname(os.path.dirname(currentDir))) class SimpleFarmSubmitter(BaseSubmitter): - if 'REZ_MESHROOM_VERSION' in os.environ: - MESHROOM_PACKAGE = "meshroom-{}".format(os.environ.get('REZ_MESHROOM_VERSION', '')) - else: - MESHROOM_PACKAGE = None filepath = os.environ.get('SIMPLEFARMCONFIG', os.path.join(currentDir, 'simpleFarmConfig.json')) config = json.load(open(filepath)) + reqPackages = [] + environment = {} ENGINE = '' DEFAULT_TAGS = {'prod': ''} @@ -28,6 +27,32 @@ class SimpleFarmSubmitter(BaseSubmitter): self.engine = os.environ.get('MESHROOM_SIMPLEFARM_ENGINE', 'tractor') self.share = os.environ.get('MESHROOM_SIMPLEFARM_SHARE', 'vfx') self.prod = os.environ.get('PROD', 'mvg') + if 'REZ_REQUEST' in os.environ: + packages = os.environ.get('REZ_REQUEST', '').split() + resolvedPackages = os.environ.get('REZ_RESOLVE', '').split() + resolvedVersions = {} + for r in resolvedPackages: + # remove implict packages + if r.startswith('~'): + continue + # logging.info('REZ: {}'.format(str(r))) + v = r.split('-') + # logging.info(' v: {}'.format(str(v))) + if len(v) == 2: + resolvedVersions[v[0]] = v[1] + for p in packages: + if p.startswith('~'): + continue + v = p.split('-') + self.reqPackages.append('-'.join([v[0], resolvedVersions[v[0]]])) + logging.debug('REZ Packages: {}'.format(str(self.reqPackages))) + elif 'REZ_MESHROOM_VERSION' in os.environ: + self.reqPackages = ["meshroom-{}".format(os.environ.get('REZ_MESHROOM_VERSION', ''))] + else: + self.reqPackages = None + + if 'REZ_DEV_PACKAGES_ROOT' in os.environ: + self.environment['REZ_DEV_PACKAGES_ROOT'] = os.environ['REZ_DEV_PACKAGES_ROOT'] def createTask(self, meshroomFile, node): tags = self.DEFAULT_TAGS.copy() # copy to not modify default tags @@ -50,10 +75,10 @@ class SimpleFarmSubmitter(BaseSubmitter): task = simpleFarm.Task( name=node.nodeType, command='{exe} --node {nodeName} "{meshroomFile}" {parallelArgs} --extern'.format( - exe='meshroom_compute' if self.MESHROOM_PACKAGE else os.path.join(binDir, 'meshroom_compute'), + exe='meshroom_compute' if self.reqPackages else os.path.join(binDir, 'meshroom_compute'), nodeName=node.name, meshroomFile=meshroomFile, parallelArgs=parallelArgs), tags=tags, - rezPackages=[self.MESHROOM_PACKAGE] if self.MESHROOM_PACKAGE else None, + rezPackages=self.reqPackages, requirements={'service': str(','.join(allRequirements))}, **arguments) return task @@ -74,6 +99,7 @@ class SimpleFarmSubmitter(BaseSubmitter): job = simpleFarm.Job(name, tags=mainTags, requirements={'service': str(','.join(allRequirements))}, + environment=self.environment, ) nodeNameToTask = {} diff --git a/meshroom/ui/graph.py b/meshroom/ui/graph.py index 3134e7a6..250abcd2 100644 --- a/meshroom/ui/graph.py +++ b/meshroom/ui/graph.py @@ -515,7 +515,7 @@ class UIGraph(QObject): @Slot(Node, QPoint, QObject) def moveNode(self, node, position, nodes=None): """ - Move 'node' to the given 'position' and also update the positions of 'nodes' if neccessary. + Move 'node' to the given 'position' and also update the positions of 'nodes' if necessary. Args: node (Node): the node to move diff --git a/meshroom/ui/qml/AboutDialog.qml b/meshroom/ui/qml/AboutDialog.qml index 08d57f6f..483a4dce 100644 --- a/meshroom/ui/qml/AboutDialog.qml +++ b/meshroom/ui/qml/AboutDialog.qml @@ -118,7 +118,7 @@ Dialog { font.pointSize: 10 } Label { - text: "2010-2021 AliceVision contributors" + text: "2010-2022 AliceVision contributors" } } diff --git a/meshroom/ui/qml/Controls/MessageDialog.qml b/meshroom/ui/qml/Controls/MessageDialog.qml index cfb20df3..079bc202 100644 --- a/meshroom/ui/qml/Controls/MessageDialog.qml +++ b/meshroom/ui/qml/Controls/MessageDialog.qml @@ -20,7 +20,7 @@ Dialog { /// Return the text content of this dialog as a simple string. /// Used when copying the message in the system clipboard. - /// Can be overriden in components extending MessageDialog + /// Can be overridden in components extending MessageDialog function getAsString() { return asString } diff --git a/meshroom/ui/qml/DialogsFactory.qml b/meshroom/ui/qml/DialogsFactory.qml index 4a11f82b..f0ccaab8 100644 --- a/meshroom/ui/qml/DialogsFactory.qml +++ b/meshroom/ui/qml/DialogsFactory.qml @@ -7,7 +7,7 @@ import Controls 1.0 */ QtObject { - readonly property string defaultErrorText: "An unexpected error has occured" + readonly property string defaultErrorText: "An unexpected error has occurred" property Component infoDialog: Component { MessageDialog { diff --git a/meshroom/ui/qml/GraphEditor/GraphEditor.qml b/meshroom/ui/qml/GraphEditor/GraphEditor.qml index 7b7c14be..2d3591ab 100755 --- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml @@ -63,7 +63,7 @@ Item { } } - /// Duplicate a node and optionnally all the following ones + /// Duplicate a node and optionally all the following ones function duplicateNode(duplicateFollowingNodes) { if (duplicateFollowingNodes) { var nodes = uigraph.duplicateNodesFrom(uigraph.selectedNodes) diff --git a/meshroom/ui/qml/ImageGallery/ImageGallery.qml b/meshroom/ui/qml/ImageGallery/ImageGallery.qml index 5041f32c..6e0256e2 100644 --- a/meshroom/ui/qml/ImageGallery/ImageGallery.qml +++ b/meshroom/ui/qml/ImageGallery/ImageGallery.qml @@ -48,6 +48,10 @@ Panel { parseIntr() } + function changeCurrentIndex(newIndex) { + _reconstruction.cameraInitIndex = newIndex + } + function populate_model() { intrinsicModel.clear() @@ -474,9 +478,8 @@ Panel { property var columnWidths: [105, 75, 75, 75, 125, 60, 60, 45, 45, 200, 60, 60] property var columnNames: [ "intrinsicId", - "pxInitialFocalLength", - "pxFocalLength.x", - "pxFocalLength.y", + "initialFocalLength", + "focalLength", "type", "width", "height", @@ -499,7 +502,6 @@ Panel { TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[9]]} } TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[10]]} } TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[11]]} } - TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[12]]} } //https://doc.qt.io/qt-5/qml-qt-labs-qmlmodels-tablemodel.html#appendRow-method } @@ -541,7 +543,7 @@ Panel { font.family: MaterialIcons.fontFamily ToolTip.text: "Next Group (Alt+Right)" ToolTip.visible: hovered - enabled: root.cameraInitIndex < root.cameraInits.count - 1 + enabled: nodesCB.currentIndex < root.cameraInits.count - 1 onClicked: nodesCB.incrementCurrentIndex() } } diff --git a/meshroom/ui/qml/Utils/SortFilterDelegateModel.qml b/meshroom/ui/qml/Utils/SortFilterDelegateModel.qml index 44fbfda1..29135b07 100644 --- a/meshroom/ui/qml/Utils/SortFilterDelegateModel.qml +++ b/meshroom/ui/qml/Utils/SortFilterDelegateModel.qml @@ -5,12 +5,12 @@ import QtQuick.Controls 2.3 /** * SortFilderDelegateModel adds sorting and filtering capabilities on a source model. * - * The way model data is accessed can be overriden by redefining the modelData function. + * The way model data is accessed can be overridden by redefining the modelData function. * This is useful if the value is not directly accessible from the model and needs * some extra logic. * * Regarding filtering, any type of value can be used as 'filterValue' (variant). - * Filtering behavior can also be overriden by redefining the respectFilter function. + * Filtering behavior can also be overridden by redefining the respectFilter function. * * Based on http://doc.qt.io/qt-5/qtquick-tutorials-dynamicview-dynamicview4-example.html */ diff --git a/meshroom/ui/qml/Utils/request.js b/meshroom/ui/qml/Utils/request.js index fba1a599..ffb5f2d6 100644 --- a/meshroom/ui/qml/Utils/request.js +++ b/meshroom/ui/qml/Utils/request.js @@ -1,7 +1,7 @@ .pragma library /** - * Perform 'GET' request on url, and bind 'callback' to onreadystatechange (with XHR objet as parameter). + * Perform 'GET' request on url, and bind 'callback' to onreadystatechange (with XHR object as parameter). */ function get(url, callback) { var xhr = new XMLHttpRequest(); diff --git a/meshroom/ui/qml/Viewer/ColorCheckerEntity.qml b/meshroom/ui/qml/Viewer/ColorCheckerEntity.qml index 67e52768..385bb25c 100644 --- a/meshroom/ui/qml/Viewer/ColorCheckerEntity.qml +++ b/meshroom/ui/qml/Viewer/ColorCheckerEntity.qml @@ -4,8 +4,8 @@ Item { id: root // required for perspective transform - property real sizeX: 1675.0 // might be overrided in ColorCheckerViewer - property real sizeY: 1125.0 // might be overrided in ColorCheckerViewer + property real sizeX: 1675.0 // might be overridden in ColorCheckerViewer + property real sizeY: 1125.0 // might be overridden in ColorCheckerViewer property var colors: null property var window: null diff --git a/meshroom/ui/qml/Viewer/FeaturesInfoOverlay.qml b/meshroom/ui/qml/Viewer/FeaturesInfoOverlay.qml index 6f5e8038..d971c573 100644 --- a/meshroom/ui/qml/Viewer/FeaturesInfoOverlay.qml +++ b/meshroom/ui/qml/Viewer/FeaturesInfoOverlay.qml @@ -200,7 +200,7 @@ FloatingPane { font.pointSize: 10 opacity: featureType.viewer.visible ? 1.0 : 0.6 } - // Tracks visibility toogle + // Tracks visibility toggle MaterialToolButton { id: tracksVisibilityButton checkable: true @@ -213,7 +213,7 @@ FloatingPane { } font.pointSize: 10 } - // Matches visibility toogle + // Matches visibility toggle MaterialToolButton { id: matchesVisibilityButton checkable: true @@ -225,7 +225,7 @@ FloatingPane { } font.pointSize: 10 } - // Landmarks visibility toogle + // Landmarks visibility toggle MaterialToolButton { id: landmarksVisibilityButton checkable: true diff --git a/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe.geom b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe.geom index 6eb0ecc7..86d0423c 100644 --- a/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe.geom +++ b/meshroom/ui/qml/Viewer3D/Materials/shaders/robustwireframe.geom @@ -94,7 +94,7 @@ void main() else { // Viewport projection breaks down for one or two vertices. - // Caclulate what we can here and defer rest to fragment shader. + // Calculate what we can here and defer rest to fragment shader. // Since this is coherent for the entire primitive the conditional // in the fragment shader is still cheap as all concurrent // fragment shader invocations will take the same code path. diff --git a/meshroom/ui/qml/Viewer3D/MediaLibrary.qml b/meshroom/ui/qml/Viewer3D/MediaLibrary.qml index 624a579a..f3fa124f 100644 --- a/meshroom/ui/qml/Viewer3D/MediaLibrary.qml +++ b/meshroom/ui/qml/Viewer3D/MediaLibrary.qml @@ -21,7 +21,7 @@ Entity { property Layer frontLayerComponent property var window - /// Camera to consider for positionning + /// Camera to consider for positioning property Camera camera: null /// True while at least one media is being loaded @@ -190,7 +190,7 @@ Entity { // whether MediaLoader has been fully instantiated by the NodeInstantiator property bool fullyInstantiated: false - // explicitely store some attached model properties for outside use and ease binding + // explicitly store some attached model properties for outside use and ease binding readonly property var attribute: model.attribute readonly property int idx: index readonly property var modelSource: attribute || model.source @@ -216,7 +216,7 @@ Entity { property string finalSource: model.requested ? currentSource : "" // To use only if we want to draw the input source and not the current node output (Warning: to use with caution) - // There is maybe a better way to do this to avoid overwritting bindings which should be readonly properties + // There is maybe a better way to do this to avoid overwriting bindings which should be readonly properties function drawInputSource() { rawSource = Qt.binding(() => instantiatedEntity.currentNode ? instantiatedEntity.currentNode.attribute("input").value: "") currentSource = Qt.binding(() => rawSource) diff --git a/meshroom/ui/qml/WorkspaceView.qml b/meshroom/ui/qml/WorkspaceView.qml index d6291517..ced16fe2 100644 --- a/meshroom/ui/qml/WorkspaceView.qml +++ b/meshroom/ui/qml/WorkspaceView.qml @@ -120,7 +120,7 @@ Item { } Action { id: displayLensDistortionToolBarAction - text: "Display Lens Distorsion Toolbar" + text: "Display Lens Distortion Toolbar" checkable: true checked: true enabled: viewer2D.useLensDistortionViewer diff --git a/meshroom/ui/qml/main.qml b/meshroom/ui/qml/main.qml index e28a42b5..2b06ea18 100755 --- a/meshroom/ui/qml/main.qml +++ b/meshroom/ui/qml/main.qml @@ -613,7 +613,7 @@ ApplicationWindow { Action { text: "About Meshroom" onTriggered: aboutDialog.open() - // shoud be StandardKey.HelpContents, but for some reason it's not stable + // should be StandardKey.HelpContents, but for some reason it's not stable // (may cause crash, requires pressing F1 twice after closing the popup) shortcut: "F1" } diff --git a/meshroom/ui/reconstruction.py b/meshroom/ui/reconstruction.py index 60b6c280..3915c42a 100755 --- a/meshroom/ui/reconstruction.py +++ b/meshroom/ui/reconstruction.py @@ -358,8 +358,9 @@ class ViewpointWrapper(QObject): """ Get camera vertical field of view in degrees. """ if not self.solvedIntrinsics: return None - pxFocalLength = self.solvedIntrinsics["pxFocalLength"] - return 2.0 * math.atan(self.orientedImageSize.height() / (2.0 * float(pxFocalLength[0]))) * 180 / math.pi + focalLength = self.solvedIntrinsics["focalLength"] + sensorHeight = self.solvedIntrinsics["sensorHeight"] + return 2.0 * math.atan(float(sensorHeight) / (2.0 * float(focalLength))) * 180.0 / math.pi @Property(type=QUrl, notify=denseSceneParamsChanged) def undistortedImageSource(self): diff --git a/meshroom/ui/utils.py b/meshroom/ui/utils.py index cfdb3a55..700bb1b4 100755 --- a/meshroom/ui/utils.py +++ b/meshroom/ui/utils.py @@ -87,7 +87,7 @@ class QmlInstantEngine(QQmlApplicationEngine): self._extensions = extensions def setVerbose(self, verboseValue): - """ Activate (True) or desactivate (False) the verbose. """ + """ Activate (True) or deactivate (False) the verbose. """ self._verbose = verboseValue def addFile(self, filename):