diff --git a/meshroom/core/node.py b/meshroom/core/node.py index 17e7ffcfb6..3c7b439505 100644 --- a/meshroom/core/node.py +++ b/meshroom/core/node.py @@ -10,6 +10,7 @@ import re import shutil import time +import types import uuid from collections import defaultdict, namedtuple from enum import Enum @@ -561,15 +562,15 @@ def _computeUids(self): def _buildCmdVars(self): def _buildAttributeCmdVars(cmdVars, name, attr): if attr.enabled: - if attr.attributeDesc.group is not None: + group = attr.attributeDesc.group(attr.node) if isinstance(attr.attributeDesc.group, types.FunctionType) else attr.attributeDesc.group + if group is not None: # if there is a valid command line "group" v = attr.getValueStr() cmdVars[name] = '--{name} {value}'.format(name=name, value=v) cmdVars[name + 'Value'] = str(v) if v: - cmdVars[attr.attributeDesc.group] = cmdVars.get(attr.attributeDesc.group, '') + \ - ' ' + cmdVars[name] + cmdVars[group] = cmdVars.get(group, '') + ' ' + cmdVars[name] elif isinstance(attr, GroupAttribute): assert isinstance(attr.value, DictModel) # if the GroupAttribute is not set in a single command line argument, diff --git a/meshroom/multiview.py b/meshroom/multiview.py index b6c6f02885..616e4d044d 100644 --- a/meshroom/multiview.py +++ b/meshroom/multiview.py @@ -180,6 +180,10 @@ def panoramaFisheyeHdr(inputImages=None, inputViewpoints=None, inputIntrinsics=N panoramaHdr(inputImages, inputViewpoints, inputIntrinsics, output, graph) for panoramaInit in graph.nodesByType("PanoramaInit"): panoramaInit.attribute("useFisheye").value = True + # when using fisheye images, the overlap between images can be small + # and thus requires many features to get enough correspondances for cameras estimation + for featureExtraction in graph.nodesByType("FeatureExtraction"): + featureExtraction.attribute("describerPreset").value = 'high' return graph def panoramaHdrPipeline(graph): @@ -214,7 +218,7 @@ def panoramaHdrPipeline(graph): featureExtraction = graph.addNewNode('FeatureExtraction', input=ldr2hdrMerge.outSfMData, - describerPreset='high') + describerQuality='high') panoramaInit = graph.addNewNode('PanoramaInit', input=featureExtraction.input, diff --git a/meshroom/nodes/aliceVision/CameraLocalization.py b/meshroom/nodes/aliceVision/CameraLocalization.py index 0900282747..08a14e412c 100644 --- a/meshroom/nodes/aliceVision/CameraLocalization.py +++ b/meshroom/nodes/aliceVision/CameraLocalization.py @@ -41,7 +41,7 @@ class CameraLocalization(desc.CommandLineNode): label='Match Desc Types', description='''Describer types to use for the matching.''', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], joinChar=',', diff --git a/meshroom/nodes/aliceVision/CameraRigCalibration.py b/meshroom/nodes/aliceVision/CameraRigCalibration.py index 67ea8730f5..fe189d082f 100644 --- a/meshroom/nodes/aliceVision/CameraRigCalibration.py +++ b/meshroom/nodes/aliceVision/CameraRigCalibration.py @@ -48,7 +48,7 @@ class CameraRigCalibration(desc.CommandLineNode): label='Match Describer Types', description='''The describer types to use for the matching''', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], joinChar=',', diff --git a/meshroom/nodes/aliceVision/ConvertSfMFormat.py b/meshroom/nodes/aliceVision/ConvertSfMFormat.py index 2ffc80225b..687ad5c496 100644 --- a/meshroom/nodes/aliceVision/ConvertSfMFormat.py +++ b/meshroom/nodes/aliceVision/ConvertSfMFormat.py @@ -35,7 +35,7 @@ class ConvertSfMFormat(desc.CommandLineNode): label='Describer Types', description='Describer types to keep.', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv', 'unknown'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv', 'unknown'], exclusive=False, uid=[0], joinChar=',', diff --git a/meshroom/nodes/aliceVision/ExportMatches.py b/meshroom/nodes/aliceVision/ExportMatches.py index 8df3b39181..53525165df 100644 --- a/meshroom/nodes/aliceVision/ExportMatches.py +++ b/meshroom/nodes/aliceVision/ExportMatches.py @@ -20,7 +20,7 @@ class ExportMatches(desc.CommandLineNode): label='Describer Types', description='Describer types used to describe an image.', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], joinChar=',', diff --git a/meshroom/nodes/aliceVision/FeatureExtraction.py b/meshroom/nodes/aliceVision/FeatureExtraction.py index a18537787a..923ed7ee9f 100644 --- a/meshroom/nodes/aliceVision/FeatureExtraction.py +++ b/meshroom/nodes/aliceVision/FeatureExtraction.py @@ -42,20 +42,76 @@ class FeatureExtraction(desc.CommandLineNode): label='Describer Types', description='Describer types used to describe an image.', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], joinChar=',', ), desc.ChoiceParam( name='describerPreset', - label='Describer Preset', - description='Control the ImageDescriber configuration (low, medium, normal, high, ultra). Configuration "ultra" can take long time !', + label='Describer Density', + description='Control the ImageDescriber density (low, medium, normal, high, ultra).\n' + 'Warning: Use ULTRA only on small datasets.', + value='normal', + values=['low', 'medium', 'normal', 'high', 'ultra', 'custom'], + exclusive=True, + uid=[0], + group=lambda node: 'allParams' if node.describerPreset.value != 'custom' else None, + ), + desc.IntParam( + name='maxNbFeatures', + label='Max Nb Features', + description='Max number of features extracted (0 means default value based on Describer Density).', + value=0, + range=(0, 100000, 1000), + uid=[0], + advanced=True, + enabled=lambda node: (node.describerPreset.value == 'custom'), + ), + desc.ChoiceParam( + name='describerQuality', + label='Describer Quality', + description='Control the ImageDescriber quality (low, medium, normal, high, ultra).', value='normal', values=['low', 'medium', 'normal', 'high', 'ultra'], exclusive=True, uid=[0], ), + desc.ChoiceParam( + name='contrastFiltering', + label='Contrast Filtering', + description="Contrast filtering method to ignore features with too low contrast that can be considered as noise:\n" + "* Static: Fixed threshold.\n" + "* AdaptiveToMedianVariance: Based on image content analysis.\n" + "* NoFiltering: Disable contrast filtering.\n" + "* GridSortOctaves: Grid Sort but per octaves (and only per scale at the end).\n" + "* GridSort: Grid sort per octaves and at the end (scale * peakValue).\n" + "* GridSortScaleSteps: Grid sort per octaves and at the end (scale and then peakValue).\n" + "* NonExtremaFiltering: Filter non-extrema peakValues.\n", + value='GridSort', + values=['Static', 'AdaptiveToMedianVariance', 'NoFiltering', 'GridSortOctaves', 'GridSort', 'GridSortScaleSteps', 'GridSortOctaveSteps', 'NonExtremaFiltering'], + exclusive=True, + advanced=True, + uid=[0], + ), + desc.FloatParam( + name='relativePeakThreshold', + label='Relative Peak Threshold', + description='Peak Threshold relative to median of gradiants.', + value=0.01, + range=(0.01, 1.0, 0.001), + advanced=True, + uid=[0], + enabled=lambda node: (node.contrastFiltering.value == 'AdaptiveToMedianVariance'), + ), + desc.BoolParam( + name='gridFiltering', + label='Grid Filtering', + description='Enable grid filtering. Highly recommended to ensure usable number of features.', + value=True, + advanced=True, + uid=[0], + ), desc.BoolParam( name='forceCpuExtraction', label='Force CPU Extraction', diff --git a/meshroom/nodes/aliceVision/FeatureMatching.py b/meshroom/nodes/aliceVision/FeatureMatching.py index 9ffcca0045..addc6c3204 100644 --- a/meshroom/nodes/aliceVision/FeatureMatching.py +++ b/meshroom/nodes/aliceVision/FeatureMatching.py @@ -63,7 +63,7 @@ class FeatureMatching(desc.CommandLineNode): label='Describer Types', description='Describer types used to describe an image.', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], joinChar=',', diff --git a/meshroom/nodes/aliceVision/FeatureRepeatability.py b/meshroom/nodes/aliceVision/FeatureRepeatability.py new file mode 100644 index 0000000000..746ba551a0 --- /dev/null +++ b/meshroom/nodes/aliceVision/FeatureRepeatability.py @@ -0,0 +1,131 @@ +__version__ = "1.1" + +from meshroom.core import desc + + +class FeatureRepeatability(desc.CommandLineNode): + commandLine = 'aliceVision_samples_repeatabilityDataset {allParams}' + size = desc.DynamicNodeSize('input') + # parallelization = desc.Parallelization(blockSize=40) + # commandLineRange = '--rangeStart {rangeStart} --rangeSize {rangeBlockSize}' + + documentation = ''' +''' + + inputs = [ + desc.File( + name='input', + label='Input Folder', + description='Input Folder with evaluation datasets.', + value='', + uid=[0], + ), + desc.ChoiceParam( + name='describerTypes', + label='Describer Types', + description='Describer types used to describe an image.', + value=['sift'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + exclusive=False, + uid=[0], + joinChar=',', + ), + desc.ChoiceParam( + name='describerPreset', + label='Describer Density', + description='Control the ImageDescriber density (low, medium, normal, high, ultra).\n' + 'Warning: Use ULTRA only on small datasets.', + value='normal', + values=['low', 'medium', 'normal', 'high', 'ultra'], + exclusive=True, + uid=[0], + ), + desc.ChoiceParam( + name='describerQuality', + label='Describer Quality', + description='Control the ImageDescriber quality (low, medium, normal, high, ultra).', + value='normal', + values=['low', 'medium', 'normal', 'high', 'ultra'], + exclusive=True, + uid=[0], + ), + desc.ChoiceParam( + name='contrastFiltering', + label='Contrast Filtering', + description="Contrast filtering method to ignore features with too low contrast that can be considered as noise:\n" + "* Static: Fixed threshold.\n" + "* AdaptiveToMedianVariance: Based on image content analysis.\n" + "* NoFiltering: Disable contrast filtering.\n" + "* GridSortOctaves: Grid Sort but per octaves (and only per scale at the end).\n" + "* GridSort: Grid sort per octaves and at the end (scale * peakValue).\n" + "* GridSortScaleSteps: Grid sort per octaves and at the end (scale and then peakValue).\n" + "* NonExtremaFiltering: Filter non-extrema peakValues.\n", + value='Static', + values=['Static', 'AdaptiveToMedianVariance', 'NoFiltering', 'GridSortOctaves', 'GridSort', 'GridSortScaleSteps', 'GridSortOctaveSteps', 'NonExtremaFiltering'], + exclusive=True, + advanced=True, + uid=[0], + ), + desc.FloatParam( + name='relativePeakThreshold', + label='Relative Peak Threshold', + description='Peak Threshold relative to median of gradiants.', + value=0.01, + range=(0.01, 1.0, 0.001), + advanced=True, + uid=[0], + enabled=lambda node: (node.contrastFiltering.value == 'AdaptiveToMedianVariance'), + ), + desc.BoolParam( + name='gridFiltering', + label='Grid Filtering', + description='Enable grid filtering. Highly recommended to ensure usable number of features.', + value=True, + advanced=True, + uid=[0], + ), + desc.BoolParam( + name='forceCpuExtraction', + label='Force CPU Extraction', + description='Use only CPU feature extraction.', + value=True, + uid=[], + advanced=True, + ), + desc.IntParam( + name='invalidate', + label='Invalidate', + description='Invalidate.', + value=0, + range=(0, 10000, 1), + group="", + uid=[0], + ), + desc.StringParam( + name="comments", + label="Comments", + description="Comments", + value="", + group="", + uid=[], + ), + desc.ChoiceParam( + name='verboseLevel', + label='Verbose Level', + description='verbosity level (fatal, error, warning, info, debug, trace).', + value='info', + values=['fatal', 'error', 'warning', 'info', 'debug', 'trace'], + exclusive=True, + uid=[], + ) + ] + + outputs = [ + desc.File( + name='output', + label='Output Folder', + description='Output path for the features and descriptors files (*.feat, *.desc).', + value=desc.Node.internalFolder, + uid=[], + ), + ] diff --git a/meshroom/nodes/aliceVision/GlobalSfM.py b/meshroom/nodes/aliceVision/GlobalSfM.py index fb06535161..49410fea08 100644 --- a/meshroom/nodes/aliceVision/GlobalSfM.py +++ b/meshroom/nodes/aliceVision/GlobalSfM.py @@ -52,7 +52,7 @@ class GlobalSfM(desc.CommandLineNode): label='Describer Types', description='Describer types used to describe an image.', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], diff --git a/meshroom/nodes/aliceVision/PanoramaEstimation.py b/meshroom/nodes/aliceVision/PanoramaEstimation.py index 23dac100cc..b89ae370da 100644 --- a/meshroom/nodes/aliceVision/PanoramaEstimation.py +++ b/meshroom/nodes/aliceVision/PanoramaEstimation.py @@ -51,7 +51,7 @@ class PanoramaEstimation(desc.CommandLineNode): label='Describer Types', description='Describer types used to describe an image.', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], diff --git a/meshroom/nodes/aliceVision/PanoramaInit.py b/meshroom/nodes/aliceVision/PanoramaInit.py index a24e112a44..740f791a11 100644 --- a/meshroom/nodes/aliceVision/PanoramaInit.py +++ b/meshroom/nodes/aliceVision/PanoramaInit.py @@ -93,7 +93,7 @@ class PanoramaInit(desc.CommandLineNode): desc.ChoiceParam( name='inputAngle', label='input Angle offset', - description='Add a rotation to the input XML given poses.', + description='Add a rotation to the input XML given poses (CCW).', value='None', values=['None', 'rotate90', 'rotate180', 'rotate270'], exclusive=True, diff --git a/meshroom/nodes/aliceVision/SfMTransform.py b/meshroom/nodes/aliceVision/SfMTransform.py index 18b0324e66..3bd2f006d0 100644 --- a/meshroom/nodes/aliceVision/SfMTransform.py +++ b/meshroom/nodes/aliceVision/SfMTransform.py @@ -112,8 +112,8 @@ class SfMTransform(desc.CommandLineNode): joinChar="," ), desc.FloatParam( - name="manualScale", - label="Scale", + name="manualScale", + label="Scale", description="Uniform Scale.", value=1.0, uid=[0], @@ -127,8 +127,8 @@ class SfMTransform(desc.CommandLineNode): name='landmarksDescriberTypes', label='Landmarks Describer Types', description='Image describer types used to compute the mean of the point cloud. (only for "landmarks" method).', - value=['sift', 'akaze'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + value=['sift', 'dspsift', 'akaze'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv', 'unknown'], exclusive=False, uid=[0], joinChar=',', diff --git a/meshroom/nodes/aliceVision/StructureFromMotion.py b/meshroom/nodes/aliceVision/StructureFromMotion.py index 7fa6c194a1..878db32b89 100644 --- a/meshroom/nodes/aliceVision/StructureFromMotion.py +++ b/meshroom/nodes/aliceVision/StructureFromMotion.py @@ -97,7 +97,7 @@ class StructureFromMotion(desc.CommandLineNode): label='Describer Types', description='Describer types used to describe an image.', value=['sift'], - values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + values=['sift', 'sift_float', 'sift_upright', 'dspsift', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], joinChar=',', diff --git a/meshroom/ui/qml/main.qml b/meshroom/ui/qml/main.qml index 2cf65039e2..5902d667a9 100755 --- a/meshroom/ui/qml/main.qml +++ b/meshroom/ui/qml/main.qml @@ -352,7 +352,12 @@ ApplicationWindow { id: openActionItem text: "Open" shortcut: "Ctrl+O" - onTriggered: ensureSaved(function() { openFileDialog.open() }) + onTriggered: ensureSaved(function() { + if(_reconstruction.graph && _reconstruction.graph.filepath) { + openFileDialog.folder = Filepath.stringToUrl(Filepath.dirname(_reconstruction.graph.filepath)) + } + openFileDialog.open() + }) } Menu { id: openRecentMenu @@ -405,7 +410,15 @@ ApplicationWindow { text: "Save" shortcut: "Ctrl+S" enabled: (_reconstruction.graph && !_reconstruction.graph.filepath) || !_reconstruction.undoStack.clean - onTriggered: _reconstruction.graph.filepath ? _reconstruction.save() : saveFileDialog.open() + onTriggered: { + if(_reconstruction.graph.filepath) { + _reconstruction.save() + } + else + { + saveFileDialog.open() + } + } } Action { id: saveAsAction