Skip to content

Commit

Permalink
Merge pull request #1483 from alicevision/dev/masking
Browse files Browse the repository at this point in the history
New ImageMasking and MeshMasking nodes
  • Loading branch information
fabiencastan authored Jul 22, 2021
2 parents 7fdd102 + e3616e3 commit 66e046c
Show file tree
Hide file tree
Showing 11 changed files with 442 additions and 23 deletions.
40 changes: 21 additions & 19 deletions meshroom/core/desc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,6 +20,7 @@ def __init__(self, name, label, description, value, advanced, uid, group, enable
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)
Expand All @@ -29,6 +30,7 @@ def __init__(self, name, label, description, value, advanced, uid, group, enable
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):
Expand All @@ -55,13 +57,13 @@ def matchDescription(self, value, conform=False):

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)
Expand Down Expand Up @@ -92,13 +94,13 @@ def matchDescription(self, value, conform=False):

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)

Expand Down Expand Up @@ -166,15 +168,15 @@ def retrieveChildrenUids(self):
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):
Expand All @@ -185,8 +187,8 @@ def validateValue(self, value):
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:
Expand All @@ -198,9 +200,9 @@ def validateValue(self, value):
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
Expand All @@ -217,9 +219,9 @@ def validateValue(self, value):
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:
Expand All @@ -233,13 +235,13 @@ def validateValue(self, value):
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 """
Expand All @@ -264,8 +266,8 @@ def validateValue(self, value):
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):
Expand Down
7 changes: 7 additions & 0 deletions meshroom/nodes/aliceVision/FeatureExtraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class FeatureExtraction(desc.CommandLineNode):
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',
Expand Down
147 changes: 147 additions & 0 deletions meshroom/nodes/aliceVision/ImageMasking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
__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.ChoiceParam(
name='algorithm',
label='Algorithm',
description='',
value='HSV',
values=['HSV', 'AutoGrayscaleThreshold'],
exclusive=True,
uid=[0],
),
desc.GroupAttribute(
name="hsv",
label="HSV Parameters",
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
""",
group=None,
enabled=lambda node: node.algorithm.value == 'HSV',
groupDesc=[
desc.FloatParam(
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',
value=0.33,
range=(0, 1, 0.01),
uid=[0]
),
desc.FloatParam(
name='hsvHueRange',
label='Tolerance',
description='Tolerance around the hue value to isolate.',
value=0.1,
range=(0, 1, 0.01),
uid=[0]
),
desc.FloatParam(
name='hsvMinSaturation',
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='hsvMaxSaturation',
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='hsvMinValue',
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='hsvMaxValue',
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='''If ticked, the selected area is ignored.
If not, only the selected area is considered.''',
value=True,
uid=[0]
),
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.File(
name='depthMapFolder',
label='Depth Mask Folder',
description='''Depth Mask Folder''',
value='',
uid=[0],
),
desc.StringParam(
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',
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=[],
),
]
98 changes: 98 additions & 0 deletions meshroom/nodes/aliceVision/MeshMasking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
__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='Dense SfMData',
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='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',
description='''If ticked, the selected area is ignored.
If not, only the selected area is considered.''',
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.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',
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=[],
),
]
Loading

0 comments on commit 66e046c

Please sign in to comment.