From 01adad9e214cf9ff32c02b0448f2e6433625a7d9 Mon Sep 17 00:00:00 2001 From: Guillaume Buisson Date: Mon, 21 Oct 2019 15:58:36 +0200 Subject: [PATCH 01/20] [nodes] new ImageMasking node --- meshroom/nodes/aliceVision/ImageMasking.py | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 meshroom/nodes/aliceVision/ImageMasking.py diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py new file mode 100644 index 00000000..bdd2891c --- /dev/null +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -0,0 +1,73 @@ +__version__ = "3.0" + +from meshroom.core import desc + + +class ImageMasking(desc.CommandLineNode): + commandLine = 'aliceVision_imageMasking {allParams}' + size = desc.DynamicNodeSize('input') + parallelization = desc.Parallelization(blockSize=40) + commandLineRange = '--rangeStart {rangeStart} --rangeSize {rangeBlockSize}' + + inputs = [ + desc.File( + name='input', + label='Input', + description='''SfMData file.''', + value='', + uid=[0], + ), + #desc.GroupAttribute( + # name="colour", + # label="Keyed Colour", + # description="", + # groupDesc=[ + # desc.FloatParam(name="r", label="r", description="", value=0, uid=[0], range=(0, 1, 0.01)), + # desc.FloatParam(name="g", label="g", description="", value=0, uid=[0], range=(0, 1, 0.01)), + # desc.FloatParam(name="b", label="b", description="", value=0, uid=[0], range=(0, 1, 0.01)), + # ]), + desc.ChoiceParam( + name='algorithm', + label='Algorithm', + description='', + value='hsv', + values=['hsv'], + exclusive=True, + uid=[0], + ), + desc.FloatParam( + name='hue', + label='Hue', + description='Hue value to isolate in [0,1] range. 0 = red, 0.33 = green, 0.66 = blue, 1 = red.', + value=0.33, + range=(0, 1, 0.01), + uid=[0] + ), + desc.FloatParam( + name='hueRange', + label='Tolerance', + description='Tolerance around the hue value to isolate.', + value=0.1, + range=(0, 1, 0.01), + uid=[0] + ), + 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', + description='''Output folder.''', + value=desc.Node.internalFolder, + uid=[], + ), + ] From a0f12743a5e24a6dd54a8b9e7bbf4b730d2b5fc0 Mon Sep 17 00:00:00 2001 From: Guillaume Buisson Date: Fri, 15 Nov 2019 16:37:54 +0100 Subject: [PATCH 02/20] [nodes] update imageMasking featureExtraction and prepareDenseScene --- .../nodes/aliceVision/FeatureExtraction.py | 7 ++ meshroom/nodes/aliceVision/ImageMasking.py | 96 +++++++++++++++---- .../nodes/aliceVision/PrepareDenseScene.py | 12 +++ 3 files changed, 94 insertions(+), 21 deletions(-) diff --git a/meshroom/nodes/aliceVision/FeatureExtraction.py b/meshroom/nodes/aliceVision/FeatureExtraction.py index 6dd48783..5bb38e06 100644 --- a/meshroom/nodes/aliceVision/FeatureExtraction.py +++ b/meshroom/nodes/aliceVision/FeatureExtraction.py @@ -38,6 +38,13 @@ It is robust to motion-blur, depth-of-field, occlusion. Be careful to have enoug value='', uid=[0], ), + desc.File( + name='masksFolder', + label='Masks Folder', + description='Use masks to filter features. Filename should be the same or the image uid.', + value='', + uid=[0], + ), desc.ChoiceParam( name='describerTypes', label='Describer Types', diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index bdd2891c..457016e9 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -17,15 +17,6 @@ class ImageMasking(desc.CommandLineNode): value='', uid=[0], ), - #desc.GroupAttribute( - # name="colour", - # label="Keyed Colour", - # description="", - # groupDesc=[ - # desc.FloatParam(name="r", label="r", description="", value=0, uid=[0], range=(0, 1, 0.01)), - # desc.FloatParam(name="g", label="g", description="", value=0, uid=[0], range=(0, 1, 0.01)), - # desc.FloatParam(name="b", label="b", description="", value=0, uid=[0], range=(0, 1, 0.01)), - # ]), desc.ChoiceParam( name='algorithm', label='Algorithm', @@ -35,20 +26,83 @@ class ImageMasking(desc.CommandLineNode): exclusive=True, uid=[0], ), - desc.FloatParam( - name='hue', - label='Hue', - description='Hue value to isolate in [0,1] range. 0 = red, 0.33 = green, 0.66 = blue, 1 = red.', - value=0.33, - range=(0, 1, 0.01), + desc.GroupAttribute( + name="hsv", + label="HSV Parameters", + description="", + formatter=desc.GroupAttribute.prefixFormatter, + joinChar='-', + groupDesc=[ + desc.FloatParam( + name='hue', + label='Hue', + description='Hue value to isolate in [0,1] range. 0 = red, 0.33 = green, 0.66 = blue, 1 = red.', + value=0.33, + range=(0, 1, 0.01), + uid=[0] + ), + desc.FloatParam( + name='hueRange', + label='Tolerance', + description='Tolerance around the hue value to isolate.', + value=0.1, + range=(0, 1, 0.01), + uid=[0] + ), + desc.FloatParam( + name='minSaturation', + label='Min Saturation', + description='Hue is meaningless if saturation is low. Do not mask pixels below this threshold.', + value=0.3, + range=(0, 1, 0.01), + uid=[0] + ), + desc.FloatParam( + name='maxSaturation', + label='Max Saturation', + description='Do not mask pixels above this threshold. It might be useful to mask white/black pixels.', + value=1, + range=(0, 1, 0.01), + uid=[0] + ), + desc.FloatParam( + name='minValue', + label='Min Value', + description='Hue is meaningless if value is low. Do not mask pixels below this threshold.', + value=0.3, + range=(0, 1, 0.01), + uid=[0] + ), + desc.FloatParam( + name='maxValue', + label='Max Value', + description='Do not mask pixels above this threshold. It might be useful to mask white/black pixels.', + value=1, + range=(0, 1, 0.01), + uid=[0] + ), + ]), + desc.BoolParam( + name='invert', + label='Invert', + description='Invert the selected area.', + value=False, uid=[0] ), - desc.FloatParam( - name='hueRange', - label='Tolerance', - description='Tolerance around the hue value to isolate.', - value=0.1, - range=(0, 1, 0.01), + desc.IntParam( + name='growRadius', + label='Grow Radius', + description='Grow the selected area. It might be used to fill the holes: then use shrinkRadius to restore the initial coutours.', + value=0, + range=(0, 50, 1), + uid=[0] + ), + desc.IntParam( + name='shrinkRadius', + label='Shrink Radius', + description='Shrink the selected area.', + value=0, + range=(0, 50, 1), uid=[0] ), desc.ChoiceParam( diff --git a/meshroom/nodes/aliceVision/PrepareDenseScene.py b/meshroom/nodes/aliceVision/PrepareDenseScene.py index ac2fd0ec..31d0ecf1 100644 --- a/meshroom/nodes/aliceVision/PrepareDenseScene.py +++ b/meshroom/nodes/aliceVision/PrepareDenseScene.py @@ -34,6 +34,18 @@ This node export undistorted images so the depth map and texturing can be comput label="Images Folders", description='Use images from specific folder(s). Filename should be the same or the image uid.', ), + desc.ListAttribute( + elementDesc=desc.File( + name="masksFolder", + label="Masks Folder", + description="", + value="", + uid=[0], + ), + name="masksFolders", + label="Masks Folders", + description='Use masks from specific folder(s). Filename should be the same or the image uid.', + ), desc.ChoiceParam( name='outputFileType', label='Output File Type', From dc3f4dd2bebb73308eff1c6aaeef343740941d7e Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Mon, 19 Jul 2021 12:03:00 +0200 Subject: [PATCH 03/20] [nodes] add semantic to customize attibute UI --- meshroom/core/desc.py | 40 ++++++++++--------- meshroom/nodes/aliceVision/ImageMasking.py | 1 + .../qml/GraphEditor/AttributeItemDelegate.qml | 39 +++++++++++++++++- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/meshroom/core/desc.py b/meshroom/core/desc.py index 87ec1d84..5312dd89 100644 --- a/meshroom/core/desc.py +++ b/meshroom/core/desc.py @@ -10,7 +10,7 @@ class Attribute(BaseObject): """ """ - def __init__(self, name, label, description, value, advanced, uid, group, enabled): + def __init__(self, name, label, description, value, advanced, semantic, uid, group, enabled): super(Attribute, self).__init__() self._name = name self._label = label @@ -20,6 +20,7 @@ class Attribute(BaseObject): self._group = group self._advanced = advanced self._enabled = enabled + self._semantic = semantic name = Property(str, lambda self: self._name, constant=True) label = Property(str, lambda self: self._label, constant=True) @@ -29,6 +30,7 @@ class Attribute(BaseObject): group = Property(str, lambda self: self._group, constant=True) advanced = Property(bool, lambda self: self._advanced, constant=True) enabled = Property(Variant, lambda self: self._enabled, constant=True) + semantic = Property(str, lambda self: self._semantic, constant=True) type = Property(str, lambda self: self.__class__.__name__, constant=True) def validateValue(self, value): @@ -55,13 +57,13 @@ class Attribute(BaseObject): class ListAttribute(Attribute): """ A list of Attributes """ - def __init__(self, elementDesc, name, label, description, group='allParams', advanced=False, enabled=True, joinChar=' '): + def __init__(self, elementDesc, name, label, description, group='allParams', advanced=False, semantic='', enabled=True, joinChar=' '): """ :param elementDesc: the Attribute description of elements to store in that list """ self._elementDesc = elementDesc self._joinChar = joinChar - super(ListAttribute, self).__init__(name=name, label=label, description=description, value=[], uid=(), group=group, advanced=advanced, enabled=enabled) + super(ListAttribute, self).__init__(name=name, label=label, description=description, value=[], uid=(), group=group, advanced=advanced, semantic=semantic, enabled=enabled) elementDesc = Property(Attribute, lambda self: self._elementDesc, constant=True) uid = Property(Variant, lambda self: self.elementDesc.uid, constant=True) @@ -92,13 +94,13 @@ class ListAttribute(Attribute): class GroupAttribute(Attribute): """ A macro Attribute composed of several Attributes """ - def __init__(self, groupDesc, name, label, description, group='allParams', advanced=False, enabled=True, joinChar=' '): + def __init__(self, groupDesc, name, label, description, group='allParams', advanced=False, semantic='', enabled=True, joinChar=' '): """ :param groupDesc: the description of the Attributes composing this group """ self._groupDesc = groupDesc self._joinChar = joinChar - super(GroupAttribute, self).__init__(name=name, label=label, description=description, value={}, uid=(), group=group, advanced=advanced, enabled=enabled) + super(GroupAttribute, self).__init__(name=name, label=label, description=description, value={}, uid=(), group=group, advanced=advanced, semantic=semantic, enabled=enabled) groupDesc = Property(Variant, lambda self: self._groupDesc, constant=True) @@ -166,15 +168,15 @@ class GroupAttribute(Attribute): class Param(Attribute): """ """ - def __init__(self, name, label, description, value, uid, group, advanced, enabled): - super(Param, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled) + def __init__(self, name, label, description, value, uid, group, advanced, semantic, enabled): + super(Param, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled) class File(Attribute): """ """ - def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, enabled=True): - super(File, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled) + def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, semantic='', enabled=True): + super(File, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled) def validateValue(self, value): if not isinstance(value, pyCompatibility.basestring): @@ -185,8 +187,8 @@ class File(Attribute): class BoolParam(Param): """ """ - def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, enabled=True): - super(BoolParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled) + def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, semantic='', enabled=True): + super(BoolParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled) def validateValue(self, value): try: @@ -198,9 +200,9 @@ class BoolParam(Param): class IntParam(Param): """ """ - def __init__(self, name, label, description, value, range, uid, group='allParams', advanced=False, enabled=True): + def __init__(self, name, label, description, value, range, uid, group='allParams', advanced=False, semantic='', enabled=True): self._range = range - super(IntParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled) + super(IntParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled) def validateValue(self, value): # handle unsigned int values that are translated to int by shiboken and may overflow @@ -217,9 +219,9 @@ class IntParam(Param): class FloatParam(Param): """ """ - def __init__(self, name, label, description, value, range, uid, group='allParams', advanced=False, enabled=True): + def __init__(self, name, label, description, value, range, uid, group='allParams', advanced=False, semantic='', enabled=True): self._range = range - super(FloatParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled) + super(FloatParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled) def validateValue(self, value): try: @@ -233,13 +235,13 @@ class FloatParam(Param): class ChoiceParam(Param): """ """ - def __init__(self, name, label, description, value, values, exclusive, uid, group='allParams', joinChar=' ', advanced=False, enabled=True): + def __init__(self, name, label, description, value, values, exclusive, uid, group='allParams', joinChar=' ', advanced=False, semantic='', enabled=True): assert values self._values = values self._exclusive = exclusive self._joinChar = joinChar self._valueType = type(self._values[0]) # cast to value type - super(ChoiceParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled) + super(ChoiceParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled) def conformValue(self, val): """ Conform 'val' to the correct type and check for its validity """ @@ -264,8 +266,8 @@ class ChoiceParam(Param): class StringParam(Param): """ """ - def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, enabled=True): - super(StringParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled) + def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, semantic='', enabled=True): + super(StringParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled) def validateValue(self, value): if not isinstance(value, pyCompatibility.basestring): diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index 457016e9..4d441e92 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -37,6 +37,7 @@ class ImageMasking(desc.CommandLineNode): name='hue', label='Hue', description='Hue value to isolate in [0,1] range. 0 = red, 0.33 = green, 0.66 = blue, 1 = red.', + semantic='color/hue', value=0.33, range=(0, 1, 0.01), uid=[0] diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 7c918196..16ec8141 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -145,7 +145,10 @@ RowLayout { { case "ChoiceParam": return attribute.desc.exclusive ? comboBox_component : multiChoice_component case "IntParam": return slider_component - case "FloatParam": return slider_component + case "FloatParam": + if(attribute.desc.semantic === 'color/hue') + return color_hue_component + return slider_component case "BoolParam": return checkbox_component case "ListAttribute": return listAttribute_component case "GroupAttribute": return groupAttribute_component @@ -379,5 +382,39 @@ RowLayout { } } } + + Component { + id: color_hue_component + Slider { + readonly property int stepDecimalCount: 2 + readonly property real formattedValue: value.toFixed(stepDecimalCount) + enabled: root.editable + value: attribute.value + from: 0 + to: 1 + stepSize: 0.01 + snapMode: Slider.SnapAlways + onPressedChanged: { + if(!pressed) + _reconstruction.setAttribute(attribute, formattedValue) + } + + background: ShaderEffect { + width: control.availableWidth + height: control.availableHeight + blending: false + fragmentShader: " + varying mediump vec2 qt_TexCoord0; + vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + } + void main() { + gl_FragColor = vec4(hsv2rgb(vec3(qt_TexCoord0.x, 1.0, 1.0)), 1.0); + }" + } + } + } } } From 67c4cf3c654c61f5c221674c3c255d0771cce551 Mon Sep 17 00:00:00 2001 From: Guillaume Buisson Date: Wed, 20 Nov 2019 11:13:55 +0100 Subject: [PATCH 04/20] [attribute] hue semantic editor: add text editor and colored display --- .../qml/GraphEditor/AttributeItemDelegate.qml | 79 +++++++++++++------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index 16ec8141..83ab8ca3 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -385,34 +385,61 @@ RowLayout { Component { id: color_hue_component - Slider { - readonly property int stepDecimalCount: 2 - readonly property real formattedValue: value.toFixed(stepDecimalCount) - enabled: root.editable - value: attribute.value - from: 0 - to: 1 - stepSize: 0.01 - snapMode: Slider.SnapAlways - onPressedChanged: { - if(!pressed) - _reconstruction.setAttribute(attribute, formattedValue) + RowLayout { + TextField { + implicitWidth: 100 + enabled: root.editable + // cast value to string to avoid intrusive scientific notations on numbers + property string displayValue: String(slider.pressed ? slider.formattedValue : attribute.value) + text: displayValue + selectByMouse: true + validator: DoubleValidator { + locale: 'C' // use '.' decimal separator disregarding the system locale + } + onEditingFinished: setTextFieldAttribute(text) + onAccepted: setTextFieldAttribute(text) + Component.onDestruction: { + if(activeFocus) + setTextFieldAttribute(text) + } } + Rectangle { + height: slider.height + width: height + color: Qt.hsla(slider.pressed ? slider.formattedValue : attribute.value, 1, 0.5, 1) + } + Slider { + Layout.fillWidth: true - background: ShaderEffect { - width: control.availableWidth - height: control.availableHeight - blending: false - fragmentShader: " - varying mediump vec2 qt_TexCoord0; - vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); - } - void main() { - gl_FragColor = vec4(hsv2rgb(vec3(qt_TexCoord0.x, 1.0, 1.0)), 1.0); - }" + id: slider + readonly property int stepDecimalCount: 2 + readonly property real formattedValue: value.toFixed(stepDecimalCount) + enabled: root.editable + value: attribute.value + from: 0 + to: 1 + stepSize: 0.01 + snapMode: Slider.SnapAlways + onPressedChanged: { + if(!pressed) + _reconstruction.setAttribute(attribute, formattedValue) + } + + background: ShaderEffect { + width: control.availableWidth + height: control.availableHeight + blending: false + fragmentShader: " + varying mediump vec2 qt_TexCoord0; + vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + } + void main() { + gl_FragColor = vec4(hsv2rgb(vec3(qt_TexCoord0.x, 1.0, 1.0)), 1.0); + }" + } } } } From afc95219412bd37c05d1df9353f4017d2d777cc6 Mon Sep 17 00:00:00 2001 From: Guillaume Buisson Date: Thu, 21 Nov 2019 08:56:37 +0100 Subject: [PATCH 05/20] [nodes] add image masking description --- meshroom/nodes/aliceVision/ImageMasking.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index 4d441e92..05c8ddea 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -29,7 +29,11 @@ class ImageMasking(desc.CommandLineNode): desc.GroupAttribute( name="hsv", label="HSV Parameters", - description="", + description="""Values to select: + - Green: default values + - White: Tolerance = 1, minSaturation = 0, maxSaturation = 0.1, minValue = 0.8, maxValue = 1 + - Black: Tolerance = 1, minSaturation = 0, maxSaturation = 0.1, minValue = 0, maxValue = 0.2 + """, formatter=desc.GroupAttribute.prefixFormatter, joinChar='-', groupDesc=[ @@ -86,8 +90,9 @@ class ImageMasking(desc.CommandLineNode): desc.BoolParam( name='invert', label='Invert', - description='Invert the selected area.', - value=False, + description='''If ticked, the selected area is ignored. + If not, only the selected area is considered.''', + value=True, uid=[0] ), desc.IntParam( From 603dfc43b13101037a96801835839a1d1d8c48ed Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Mon, 19 Jul 2021 12:04:47 +0200 Subject: [PATCH 06/20] fix imagemasking node --- meshroom/nodes/aliceVision/ImageMasking.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index 05c8ddea..1dadab4f 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -34,11 +34,10 @@ class ImageMasking(desc.CommandLineNode): - White: Tolerance = 1, minSaturation = 0, maxSaturation = 0.1, minValue = 0.8, maxValue = 1 - Black: Tolerance = 1, minSaturation = 0, maxSaturation = 0.1, minValue = 0, maxValue = 0.2 """, - formatter=desc.GroupAttribute.prefixFormatter, - joinChar='-', + group='', groupDesc=[ desc.FloatParam( - name='hue', + name='hsv-hue', label='Hue', description='Hue value to isolate in [0,1] range. 0 = red, 0.33 = green, 0.66 = blue, 1 = red.', semantic='color/hue', @@ -47,7 +46,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='hueRange', + name='hsv-hueRange', label='Tolerance', description='Tolerance around the hue value to isolate.', value=0.1, @@ -55,7 +54,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='minSaturation', + name='hsv-minSaturation', label='Min Saturation', description='Hue is meaningless if saturation is low. Do not mask pixels below this threshold.', value=0.3, @@ -63,7 +62,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='maxSaturation', + name='hsv-maxSaturation', label='Max Saturation', description='Do not mask pixels above this threshold. It might be useful to mask white/black pixels.', value=1, @@ -71,7 +70,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='minValue', + name='hsv-minValue', label='Min Value', description='Hue is meaningless if value is low. Do not mask pixels below this threshold.', value=0.3, @@ -79,7 +78,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='maxValue', + name='hsv-maxValue', label='Max Value', description='Do not mask pixels above this threshold. It might be useful to mask white/black pixels.', value=1, From d90fbc5fc32ef3054d1469b7ef4e1952578cd18c Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Tue, 20 Jul 2021 16:20:11 +0200 Subject: [PATCH 07/20] [nodes] ImageMasking: update params names --- meshroom/nodes/aliceVision/ImageMasking.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index 1dadab4f..a3a9348b 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -34,10 +34,10 @@ class ImageMasking(desc.CommandLineNode): - White: Tolerance = 1, minSaturation = 0, maxSaturation = 0.1, minValue = 0.8, maxValue = 1 - Black: Tolerance = 1, minSaturation = 0, maxSaturation = 0.1, minValue = 0, maxValue = 0.2 """, - group='', + group=None, groupDesc=[ desc.FloatParam( - name='hsv-hue', + name='hsvHue', label='Hue', description='Hue value to isolate in [0,1] range. 0 = red, 0.33 = green, 0.66 = blue, 1 = red.', semantic='color/hue', @@ -46,7 +46,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='hsv-hueRange', + name='hsvHueRange', label='Tolerance', description='Tolerance around the hue value to isolate.', value=0.1, @@ -54,7 +54,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='hsv-minSaturation', + name='hsvMinSaturation', label='Min Saturation', description='Hue is meaningless if saturation is low. Do not mask pixels below this threshold.', value=0.3, @@ -62,7 +62,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='hsv-maxSaturation', + name='hsvMaxSaturation', label='Max Saturation', description='Do not mask pixels above this threshold. It might be useful to mask white/black pixels.', value=1, @@ -70,7 +70,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='hsv-minValue', + name='hsvMinValue', label='Min Value', description='Hue is meaningless if value is low. Do not mask pixels below this threshold.', value=0.3, @@ -78,7 +78,7 @@ class ImageMasking(desc.CommandLineNode): uid=[0] ), desc.FloatParam( - name='hsv-maxValue', + name='hsvMaxValue', label='Max Value', description='Do not mask pixels above this threshold. It might be useful to mask white/black pixels.', value=1, From ac1416099282d14566944adbaf5bce820193b562 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Tue, 5 Nov 2019 11:58:18 +0100 Subject: [PATCH 08/20] [nodes] add new node SfMDistances --- meshroom/nodes/aliceVision/SfMDistances.py | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 meshroom/nodes/aliceVision/SfMDistances.py diff --git a/meshroom/nodes/aliceVision/SfMDistances.py b/meshroom/nodes/aliceVision/SfMDistances.py new file mode 100644 index 00000000..24ab2354 --- /dev/null +++ b/meshroom/nodes/aliceVision/SfMDistances.py @@ -0,0 +1,67 @@ +__version__ = "3.0" + +from meshroom.core import desc + + +class SfMDistances(desc.CommandLineNode): + commandLine = 'aliceVision_utils_sfmDistances {allParams}' + size = desc.DynamicNodeSize('input') + + inputs = [ + desc.File( + name='input', + label='Input', + description='''SfMData file.''', + value='', + uid=[0], + ), + desc.ChoiceParam( + name='objectType', + label='Type', + description='', + value='landmarks', + values=['landmarks', 'cameras'], + exclusive=True, + uid=[0], + ), + desc.ChoiceParam( + name='landmarksDescriberTypes', + label='Describer Types', + description='Describer types used to describe an image (only used when using "landmarks").', + value=['cctag3'], + values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], + exclusive=False, + uid=[0], + joinChar=',', + ), + desc.StringParam( + name='A', + label='A IDs', + description='It will display the distances between A and B elements.\n' + 'This value should be an ID or a list of IDs of landmarks IDs or cameras (UID or filename without extension).\n' + 'It will list all elements if empty.', + value='', + uid=[0], + ), + desc.StringParam( + name='B', + label='B IDs', + description='It will display the distances between A and B elements.\n' + 'This value should be an ID or a list of IDs of landmarks IDs or cameras (UID or filename without extension).\n' + 'It will list all elements if empty.', + value='', + uid=[0], + ), + 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 = [ + ] From 8f9e5376dab7c45894fe8645fefa3bf90a1990cb Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Tue, 5 Nov 2019 11:58:59 +0100 Subject: [PATCH 09/20] [nodes] SfMTransform: update param tooltip --- meshroom/nodes/aliceVision/SfMTransform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshroom/nodes/aliceVision/SfMTransform.py b/meshroom/nodes/aliceVision/SfMTransform.py index 6f68e30b..3e4ee7df 100644 --- a/meshroom/nodes/aliceVision/SfMTransform.py +++ b/meshroom/nodes/aliceVision/SfMTransform.py @@ -51,7 +51,7 @@ The transformation can be based on: label='Transformation', description="Required only for 'transformation' and 'from_single_camera' methods:\n" " * transformation: Align [X,Y,Z] to +Y-axis, rotate around Y by R deg, scale by S; syntax: X,Y,Z;R;S\n" - " * from_single_camera: Camera UID or image filename", + " * from_single_camera: Camera UID or simplified regular expression to match image filepath (like '*camera2*.jpg')", value='', uid=[0], enabled=lambda node: node.method.value == "transformation" or node.method.value == "from_single_camera", From f26cbda2c0489c5d7c0d33db802a6b230e14c3b7 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Wed, 21 Jul 2021 11:03:35 +0200 Subject: [PATCH 10/20] [nodes] StructureFromMotion: expose new parameters minNbCamerasToRefinePrincipalPoint and rigMinNbCamerasForCalibration --- .../nodes/aliceVision/StructureFromMotion.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/meshroom/nodes/aliceVision/StructureFromMotion.py b/meshroom/nodes/aliceVision/StructureFromMotion.py index 2a392693..09914ac0 100644 --- a/meshroom/nodes/aliceVision/StructureFromMotion.py +++ b/meshroom/nodes/aliceVision/StructureFromMotion.py @@ -270,15 +270,36 @@ It iterates like that, adding cameras and triangulating new 2D features into 3D uid=[0], advanced=True, ), + desc.IntParam( + name='rigMinNbCamerasForCalibration', + label='Min Nb Cameras For Rig Calibration', + description='Minimal number of cameras to start the calibration of the rig', + value=20, + range=(1, 50, 1), + uid=[0], + advanced=True, + ), desc.BoolParam( name='lockAllIntrinsics', - label='Force Lock of All Intrinsic Camera Parameters.', + label='Force Lock of All Intrinsic Camera Parameters', description='Force to keep constant all the intrinsics parameters of the cameras (focal length, \n' 'principal point, distortion if any) during the reconstruction.\n' 'This may be helpful if the input cameras are already fully calibrated.', value=False, uid=[0], ), + desc.IntParam( + name='minNbCamerasToRefinePrincipalPoint', + label='Min Nb Cameras To Refine Principal Point', + description='Minimal number of cameras to refine the principal point of the cameras (one of the intrinsic parameters of the camera). ' + 'If we do not have enough cameras, the principal point in consider is considered in the center of the image. ' + 'If minNbCamerasToRefinePrincipalPoint<=0, the principal point is never refined. ' + 'If minNbCamerasToRefinePrincipalPoint==1, the principal point is always refined.', + value=3, + range=(0, 20, 1), + uid=[0], + advanced=True, + ), desc.BoolParam( name='filterTrackForks', label='Filter Track Forks', From 9be41d023083e687dd06608f0c2503db0fe550e0 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Tue, 5 Nov 2019 11:58:18 +0100 Subject: [PATCH 11/20] [nodes] add new node SfMDistances From 844c55e2df7f2ec9dfd4016e09c59b64a5078b36 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Tue, 5 Nov 2019 11:58:59 +0100 Subject: [PATCH 12/20] [nodes] SfMTransform: update param tooltip From bfe0ba5ce66a601978523b7efd5fbbf4a33ae4a8 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Tue, 17 Dec 2019 22:35:58 +0100 Subject: [PATCH 13/20] [nodes] ImageMasking: add AutoGrayscaleThreshold algorithm and depthMap inputs --- meshroom/nodes/aliceVision/ImageMasking.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index a3a9348b..1feb9fee 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -21,8 +21,8 @@ class ImageMasking(desc.CommandLineNode): name='algorithm', label='Algorithm', description='', - value='hsv', - values=['hsv'], + value='HSV', + values=['HSV', 'AutoGrayscaleThreshold'], exclusive=True, uid=[0], ), @@ -110,6 +110,20 @@ class ImageMasking(desc.CommandLineNode): range=(0, 50, 1), uid=[0] ), + desc.File( + name='depthMapFolder', + label='Depth Mask Folder', + description='''Depth Mask Folder''', + value='', + uid=[0], + ), + desc.File( + name='depthMapExp', + label='Depth Mask Expression', + description='''Depth Mask Expression, like "{inputFolder}/{stem}-depth.{ext}".''', + value='', + uid=[0], + ), desc.ChoiceParam( name='verboseLevel', label='Verbose Level', From 4063b2ce7a3a04de809fa4bbe6b4fdbf3c6bdea5 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Wed, 18 Dec 2019 09:33:23 +0100 Subject: [PATCH 14/20] [nodes] ImageMasking: use StringParam instead of FileParam for depthMapExp --- meshroom/nodes/aliceVision/ImageMasking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index 1feb9fee..9ba12266 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -117,7 +117,7 @@ class ImageMasking(desc.CommandLineNode): value='', uid=[0], ), - desc.File( + desc.StringParam( name='depthMapExp', label='Depth Mask Expression', description='''Depth Mask Expression, like "{inputFolder}/{stem}-depth.{ext}".''', From 7128bb4d9afa240b89e4f4912ab20f0a78c230fb Mon Sep 17 00:00:00 2001 From: GuillaumeDev Date: Mon, 14 Jun 2021 17:44:54 +0200 Subject: [PATCH 15/20] [nodes] new MeshMasking node --- meshroom/nodes/aliceVision/MeshMasking.py | 75 +++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 meshroom/nodes/aliceVision/MeshMasking.py diff --git a/meshroom/nodes/aliceVision/MeshMasking.py b/meshroom/nodes/aliceVision/MeshMasking.py new file mode 100644 index 00000000..9d4d9bff --- /dev/null +++ b/meshroom/nodes/aliceVision/MeshMasking.py @@ -0,0 +1,75 @@ +__version__ = "1.0" + +from meshroom.core import desc + + +class MeshMasking(desc.CommandLineNode): + commandLine = 'aliceVision_meshMasking {allParams}' + category = 'Mesh Post-Processing' + documentation = ''' +Decimate triangles based on image masks. +''' + + inputs = [ + desc.File( + name='input', + label='Input', + description='''SfMData file.''', + value='', + uid=[0], + ), + desc.File( + name='inputMesh', + label='Input Mesh', + description='''Input Mesh (OBJ file format).''', + value='', + uid=[0], + ), + desc.ListAttribute( + elementDesc=desc.File( + name="masksFolder", + label="Masks Folder", + description="", + value="", + uid=[0], + ), + name="masksFolders", + label="Masks Folders", + description='Use masks from specific folder(s). Filename should be the same or the image uid.', + ), + desc.IntParam( + name='threshold', + label='Threshold', + description='The minimum number of visibility to keep a vertex.', + value=1, + range=(1, 100, 1), + uid=[0] + ), + desc.BoolParam( + name='invert', + label='Invert', + description='''If ticked, the selected area is ignored. + If not, only the selected area is considered.''', + value=False, + uid=[0] + ), + 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='outputMesh', + label='Output Mesh', + description='''Output mesh (OBJ file format).''', + value=desc.Node.internalFolder + 'mesh.obj', + uid=[], + ), + ] From f16402ff06a7254089a3e21cf9656ac14491b4b6 Mon Sep 17 00:00:00 2001 From: GuillaumeDev Date: Mon, 21 Jun 2021 15:54:13 +0200 Subject: [PATCH 16/20] [nodes] add "smooth" option for MeshMasking --- meshroom/nodes/aliceVision/MeshMasking.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/meshroom/nodes/aliceVision/MeshMasking.py b/meshroom/nodes/aliceVision/MeshMasking.py index 9d4d9bff..3dad506e 100644 --- a/meshroom/nodes/aliceVision/MeshMasking.py +++ b/meshroom/nodes/aliceVision/MeshMasking.py @@ -45,6 +45,13 @@ Decimate triangles based on image masks. range=(1, 100, 1), uid=[0] ), + desc.BoolParam( + name='smoothBoundary', + label='Smooth Boundary', + description='Modify the triangles at the boundary to fit the masks.', + value=False, + uid=[0] + ), desc.BoolParam( name='invert', label='Invert', From 85f3ae4bddb1a190ea1de918634d175bb24f4c54 Mon Sep 17 00:00:00 2001 From: GuillaumeDev Date: Thu, 8 Jul 2021 17:51:21 +0200 Subject: [PATCH 17/20] [nodes] add "undistortMasks" option for MeshMasking --- meshroom/nodes/aliceVision/MeshMasking.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/meshroom/nodes/aliceVision/MeshMasking.py b/meshroom/nodes/aliceVision/MeshMasking.py index 3dad506e..8a582d6a 100644 --- a/meshroom/nodes/aliceVision/MeshMasking.py +++ b/meshroom/nodes/aliceVision/MeshMasking.py @@ -60,6 +60,14 @@ Decimate triangles based on image masks. value=False, uid=[0] ), + desc.BoolParam( + name='undistortMasks', + label='Undistort Masks', + description='''Undistort the masks with the same parameters as the matching image. + Tick it if the masks are drawn on the original images.''', + value=False, + uid=[0] + ), desc.ChoiceParam( name='verboseLevel', label='Verbose Level', From 787c1c4c0224167be265a3932519d7cadb4ce600 Mon Sep 17 00:00:00 2001 From: GuillaumeDev Date: Tue, 13 Jul 2021 09:56:37 +0200 Subject: [PATCH 18/20] [nodes] add "usePointsVisibilities" and rename "input" option for MeshMasking --- meshroom/nodes/aliceVision/MeshMasking.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/meshroom/nodes/aliceVision/MeshMasking.py b/meshroom/nodes/aliceVision/MeshMasking.py index 8a582d6a..8ae68111 100644 --- a/meshroom/nodes/aliceVision/MeshMasking.py +++ b/meshroom/nodes/aliceVision/MeshMasking.py @@ -13,8 +13,8 @@ Decimate triangles based on image masks. inputs = [ desc.File( name='input', - label='Input', - description='''SfMData file.''', + label='Dense SfMData', + description='SfMData file.', value='', uid=[0], ), @@ -68,6 +68,14 @@ Decimate triangles based on image masks. value=False, uid=[0] ), + desc.BoolParam( + name='usePointsVisibilities', + label='Use points visibilities', + description='''Use the points visibilities from the meshing to filter triangles. + Example: when they are occluded, back-face, etc.''', + value=False, + uid=[0] + ), desc.ChoiceParam( name='verboseLevel', label='Verbose Level', From 112280043ccfaa145b5e912dfb053b853ce865f8 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Sat, 17 Jul 2021 23:00:33 +0200 Subject: [PATCH 19/20] [ui] declare .stl support in 3D viewer --- meshroom/ui/qml/Viewer3D/MediaLoader.qml | 1 + meshroom/ui/qml/Viewer3D/Viewer3DSettings.qml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/meshroom/ui/qml/Viewer3D/MediaLoader.qml b/meshroom/ui/qml/Viewer3D/MediaLoader.qml index 96bfc516..1ca01331 100644 --- a/meshroom/ui/qml/Viewer3D/MediaLoader.qml +++ b/meshroom/ui/qml/Viewer3D/MediaLoader.qml @@ -49,6 +49,7 @@ import Utils 1.0 case ".abc": if(Viewer3DSettings.supportAlembic) component = abcLoaderEntityComponent; break; case ".exr": if(Viewer3DSettings.supportDepthMap) component = exrLoaderComponent; break; case ".obj": + case ".stl": default: component = sceneLoaderEntityComponent; break; } diff --git a/meshroom/ui/qml/Viewer3D/Viewer3DSettings.qml b/meshroom/ui/qml/Viewer3D/Viewer3DSettings.qml index aded135a..2cf485b6 100644 --- a/meshroom/ui/qml/Viewer3D/Viewer3DSettings.qml +++ b/meshroom/ui/qml/Viewer3D/Viewer3DSettings.qml @@ -13,7 +13,7 @@ Item { // supported 3D files extensions readonly property var supportedExtensions: { - var exts = ['.obj']; + var exts = ['.obj', '.stl']; if(supportAlembic) exts.push('.abc'); if(supportDepthMap) From e3616e304082116b3337151ee5256e6593764abb Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Fri, 23 Jul 2021 00:07:04 +0200 Subject: [PATCH 20/20] [nodes] ImageMasking: enable hsv params only for hvs algorithm --- meshroom/nodes/aliceVision/ImageMasking.py | 1 + 1 file changed, 1 insertion(+) diff --git a/meshroom/nodes/aliceVision/ImageMasking.py b/meshroom/nodes/aliceVision/ImageMasking.py index 9ba12266..93a8540d 100644 --- a/meshroom/nodes/aliceVision/ImageMasking.py +++ b/meshroom/nodes/aliceVision/ImageMasking.py @@ -35,6 +35,7 @@ class ImageMasking(desc.CommandLineNode): - Black: Tolerance = 1, minSaturation = 0, maxSaturation = 0.1, minValue = 0, maxValue = 0.2 """, group=None, + enabled=lambda node: node.algorithm.value == 'HSV', groupDesc=[ desc.FloatParam( name='hsvHue',