Merge branch 'develop' of https://github.com/alicevision/meshroom into dev_depthMapOptim

Conflicts:
	meshroom/core/desc.py
	meshroom/nodes/aliceVision/CameraInit.py
	meshroom/nodes/aliceVision/ImageMasking.py
This commit is contained in:
Grégoire De Lillo 2021-09-29 19:34:10 +02:00
commit 0aa173c982
73 changed files with 4187 additions and 522 deletions

View file

@ -1,4 +1,4 @@
from meshroom.common import BaseObject, Property, Variant, VariantList, JSValue, ListModel
from meshroom.common import BaseObject, Property, Variant, VariantList, JSValue
from meshroom.core import pyCompatibility
from enum import Enum # available by default in python3. For python2: "pip install enum34"
import math
@ -10,7 +10,7 @@ class Attribute(BaseObject):
"""
"""
def __init__(self, name, label, description, value, advanced, semantic, uid, group, enabled, formatter):
def __init__(self, name, label, description, value, advanced, semantic, uid, group, enabled):
super(Attribute, self).__init__()
self._name = name
self._label = label
@ -21,7 +21,6 @@ class Attribute(BaseObject):
self._advanced = advanced
self._enabled = enabled
self._semantic = semantic
self._formatter = formatter or self._defaultFormatter
name = Property(str, lambda self: self._name, constant=True)
label = Property(str, lambda self: self._label, constant=True)
@ -42,45 +41,29 @@ class Attribute(BaseObject):
"""
raise NotImplementedError("Attribute.validateValue is an abstract function that should be implemented in the derived class.")
def matchDescription(self, value, conform=False):
def matchDescription(self, value, strict=True):
""" Returns whether the value perfectly match attribute's description.
Args:
value: the value
conform: try to adapt value to match the description
strict: strict test for the match (for instance, regarding a group with some parameter changes)
"""
try:
self.validateValue(value)
except ValueError:
return False
return True
def format(self, value):
""" Returns a list of (group, name, value) parameters """
return self._formatter(self, value)
@staticmethod
def _defaultFormatter(desc, value):
result_value = value
if isinstance(desc, ChoiceParam) and not desc.exclusive:
assert(isinstance(value, collections.Sequence) and not isinstance(value, pyCompatibility.basestring))
result_value = desc.joinChar.join(value)
elif isinstance(desc, (StringParam, File)):
result_value = '"{}"'.format(value)
else:
result_value = str(value)
return ((desc.group, desc.name, result_value),)
class ListAttribute(Attribute):
""" A list of Attributes """
def __init__(self, elementDesc, name, label, description, group='allParams', advanced=False, semantic='', enabled=True, joinChar=' ', formatter=None):
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, semantic=semantic, enabled=enabled, formatter=formatter)
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)
@ -99,32 +82,25 @@ class ListAttribute(Attribute):
raise ValueError('ListAttribute only supports list/tuple input values (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
return value
def matchDescription(self, value, conform=False):
def matchDescription(self, value, strict=True):
""" Check that 'value' content matches ListAttribute's element description. """
if not super(ListAttribute, self).matchDescription(value, conform):
if not super(ListAttribute, self).matchDescription(value, strict):
return False
# list must be homogeneous: only test first element
if value:
return self._elementDesc.matchDescription(value[0], conform)
return self._elementDesc.matchDescription(value[0], strict)
return True
@staticmethod
def _defaultFormatter(desc, value):
result_value = value
if isinstance(value, ListModel):
result_value = desc.joinChar.join([subv for v in value for _, _, subv in v.format()])
return Attribute._defaultFormatter(desc, result_value)
class GroupAttribute(Attribute):
""" A macro Attribute composed of several Attributes """
def __init__(self, groupDesc, name, label, description, group='allParams', advanced=False, enabled=True, joinChar=' ', semantic='', formatter=None):
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, semantic=semantic, formatter=formatter)
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)
@ -139,9 +115,13 @@ class GroupAttribute(Attribute):
value = ast.literal_eval(value)
if isinstance(value, dict):
invalidKeys = set(value.keys()).difference([attr.name for attr in self._groupDesc])
if invalidKeys:
raise ValueError('Value contains key that does not match group description : {}'.format(invalidKeys))
# invalidKeys = set(value.keys()).difference([attr.name for attr in self._groupDesc])
# if invalidKeys:
# raise ValueError('Value contains key that does not match group description : {}'.format(invalidKeys))
if self._groupDesc:
commonKeys = set(value.keys()).intersection([attr.name for attr in self._groupDesc])
if not commonKeys:
raise ValueError('Value contains no key that matches with the group description: {}'.format(commonKeys))
elif isinstance(value, (list, tuple)):
if len(value) != len(self._groupDesc):
raise ValueError('Value contains incoherent number of values: desc size: {}, value size: {}'.format(len(self._groupDesc), len(value)))
@ -150,34 +130,29 @@ class GroupAttribute(Attribute):
return value
def matchDescription(self, value, conform=False):
def matchDescription(self, value, strict=True):
"""
Check that 'value' contains the exact same set of keys as GroupAttribute's group description
and that every child value match corresponding child attribute description.
Args:
value: the value
conform: remove entries that don't exist in the description.
strict: strict test for the match (for instance, regarding a group with some parameter changes)
"""
if not super(GroupAttribute, self).matchDescription(value):
return False
attrMap = {attr.name: attr for attr in self._groupDesc}
if conform:
# remove invalid keys
invalidKeys = set(value.keys()).difference([attr.name for attr in self._groupDesc])
for k in invalidKeys:
del self._groupDesc[k]
else:
# must have the exact same child attributes
if sorted(value.keys()) != sorted(attrMap.keys()):
return False
matchCount = 0
for k, v in value.items():
# each child value must match corresponding child attribute description
if not attrMap[k].matchDescription(v, conform):
return False
return True
if k in attrMap and attrMap[k].matchDescription(v, strict):
matchCount += 1
if strict:
return matchCount == len(value.items()) == len(self._groupDesc)
return matchCount > 0
def retrieveChildrenUids(self):
allUids = []
@ -185,21 +160,6 @@ class GroupAttribute(Attribute):
allUids.extend(desc.uid)
return allUids
@staticmethod
def _defaultFormatter(desc, value):
# sort values based on child attributes group description order
sortedSubValues = [subv for attr in desc.groupDesc for _, _, subv in value.get(attr.name).format()]
result_value = desc.joinChar.join(sortedSubValues)
return Attribute._defaultFormatter(desc, result_value)
@staticmethod
def prefixFormatter(desc, value):
return [(group, desc.joinChar.join((desc.name, name)), v) for attr in desc.groupDesc for group, name, v in value.get(attr.name).format()]
@staticmethod
def passthroughFormatter(desc, value):
return [item for attr in desc.groupDesc for item in value.get(attr.name).format()]
uid = Property(Variant, retrieveChildrenUids, constant=True)
joinChar = Property(str, lambda self: self._joinChar, constant=True)
@ -207,15 +167,15 @@ class GroupAttribute(Attribute):
class Param(Attribute):
"""
"""
def __init__(self, name, label, description, value, uid, group, advanced, enabled, semantic='', formatter=None):
super(Param, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=enabled, semantic=semantic, formatter=formatter)
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, semantic='', formatter=None):
super(File, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=True, semantic=semantic, formatter=formatter)
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):
@ -226,8 +186,8 @@ class File(Attribute):
class BoolParam(Param):
"""
"""
def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, enabled=True, semantic='', formatter=None):
super(BoolParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, enabled=True, semantic=semantic, formatter=formatter)
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:
@ -239,9 +199,9 @@ class BoolParam(Param):
class IntParam(Param):
"""
"""
def __init__(self, name, label, description, value, range, uid, group='allParams', advanced=False, enabled=True, semantic='', formatter=None):
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, semantic=semantic, formatter=formatter)
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
@ -258,9 +218,9 @@ class IntParam(Param):
class FloatParam(Param):
"""
"""
def __init__(self, name, label, description, value, range, uid, group='allParams', advanced=False, enabled=True, semantic='', formatter=None):
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, semantic=semantic, formatter=formatter)
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:
@ -274,13 +234,13 @@ class FloatParam(Param):
class ChoiceParam(Param):
"""
"""
def __init__(self, name, label, description, value, values, exclusive, uid, group='allParams', joinChar=' ', advanced=False, enabled=True, semantic='', formatter=None):
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, semantic=semantic, formatter=formatter)
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 """
@ -305,8 +265,8 @@ class ChoiceParam(Param):
class StringParam(Param):
"""
"""
def __init__(self, name, label, description, value, uid, group='allParams', advanced=False, enabled=True, semantic='', formatter=None):
super(StringParam, self).__init__(name=name, label=label, description=description,value=value, uid=uid, group=group, advanced=advanced, enabled=True, semantic=semantic, formatter=formatter)
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):
@ -466,6 +426,9 @@ class Node(object):
def __init__(self):
pass
def upgradeAttributeValues(self, attrValues, fromVersion):
return attrValues
@classmethod
def update(cls, node):
""" Method call before node's internal update on invalidation.
@ -511,7 +474,7 @@ class CommandLineNode(Node):
if not alreadyInEnv:
cmdPrefix = '{rez} {packageFullName} -- '.format(rez=os.environ.get('REZ_ENV'), packageFullName=chunk.node.packageFullName)
cmdSuffix = ''
if chunk.node.isParallelized:
if chunk.node.isParallelized and chunk.node.size > 1:
cmdSuffix = ' ' + self.commandLineRange.format(**chunk.range.toDict())
return cmdPrefix + chunk.node.nodeDesc.commandLine.format(**chunk.node._cmdVars) + cmdSuffix