use semantic field in file attributes to specify images and store displayed node in 2D viewer to adapt UI

This commit is contained in:
Loïc Vital 2022-09-20 18:26:20 +02:00 committed by Fabien Castan
parent fc5ab540f5
commit a574c0fc9b
9 changed files with 139 additions and 83 deletions

View file

@ -489,7 +489,6 @@ class Node(object):
parallelization = None parallelization = None
documentation = '' documentation = ''
category = 'Other' category = 'Other'
outputImageTypes = ['image']
def __init__(self): def __init__(self):
pass pass

View file

@ -568,9 +568,6 @@ class BaseNode(BaseObject):
def getAttributes(self): def getAttributes(self):
return self._attributes return self._attributes
def getOutputImageTypes(self):
return self.nodeDesc.outputImageTypes
@Slot(str, result=bool) @Slot(str, result=bool)
def hasAttribute(self, name): def hasAttribute(self, name):
return name in self._attributes.keys() return name in self._attributes.keys()
@ -1056,7 +1053,6 @@ class BaseNode(BaseObject):
x = Property(float, lambda self: self._position.x, notify=positionChanged) x = Property(float, lambda self: self._position.x, notify=positionChanged)
y = Property(float, lambda self: self._position.y, notify=positionChanged) y = Property(float, lambda self: self._position.y, notify=positionChanged)
attributes = Property(BaseObject, getAttributes, constant=True) attributes = Property(BaseObject, getAttributes, constant=True)
outputImageTypes = Property(Variant, getOutputImageTypes, constant=True)
internalFolderChanged = Signal() internalFolderChanged = Signal()
internalFolder = Property(str, internalFolder.fget, notify=internalFolderChanged) internalFolder = Property(str, internalFolder.fget, notify=internalFolderChanged)
depthChanged = Signal() depthChanged = Signal()

View file

@ -293,11 +293,27 @@ Use a downscale factor of one (full-resolution) only if the quality of the input
outputs = [ outputs = [
desc.File( desc.File(
name='output', name='output',
label='Output', label='Output Folder',
description='Output folder for generated depth maps.', description='Output folder for generated depth maps.',
value=desc.Node.internalFolder, value=desc.Node.internalFolder,
uid=[], uid=[],
), ),
desc.File(
name='depth',
label='Depth Maps',
description='Generated depth maps.',
semantic='image',
value=desc.Node.internalFolder + '<VIEW_ID>_depthMap.exr',
uid=[],
group='', # do not export on the command line
),
desc.File(
name='sim',
label='Sim Maps',
description='Generated sim maps.',
semantic='image',
value=desc.Node.internalFolder + '<VIEW_ID>_simMap.exr',
uid=[],
group='', # do not export on the command line
),
] ]
outputImageTypes = ['depth', 'sim']

View file

@ -128,4 +128,22 @@ This allows to filter unstable points before starting the fusion of all depth ma
value=desc.Node.internalFolder, value=desc.Node.internalFolder,
uid=[], uid=[],
), ),
desc.File(
name='depth',
label='Depth Maps',
description='Filtered depth maps.',
semantic='image',
value=desc.Node.internalFolder + '<VIEW_ID>_depthMap.exr',
uid=[],
group='', # do not export on the command line
),
desc.File(
name='sim',
label='Sim Maps',
description='Filtered sim maps.',
semantic='image',
value=desc.Node.internalFolder + '<VIEW_ID>_simMap.exr',
uid=[],
group='', # do not export on the command line
),
] ]

View file

@ -338,12 +338,21 @@ Convert or apply filtering to the input images.
value=desc.Node.internalFolder, value=desc.Node.internalFolder,
uid=[], uid=[],
), ),
#desc.File(
# name='outputImages',
# label='Output Images',
# description='Output Image Files.',
# value= outputImagesValueFunct,
# group='', # do not export on the command line
# uid=[],
#),
desc.File( desc.File(
name='outputImages', name='outputImages',
label='Output Images', label='Output Images',
description='Output Image Files.', description='Output Image Files.',
value= outputImagesValueFunct, semantic='image',
value=desc.Node.internalFolder + '<VIEW_ID>.exr',
group='', # do not export on the command line group='', # do not export on the command line
uid=[], uid=[]
), )
] ]

View file

@ -342,14 +342,23 @@ Many cameras are contributing to the low frequencies and only the best ones cont
uid=[], uid=[],
group='', group='',
), ),
#desc.File(
# name='outputTextures',
# label='Textures',
# description='Output Texture files.',
# value= lambda attr: desc.Node.internalFolder + 'texture_*.' + attr.node.colorMapping.colorMappingFileType.value if attr.node.colorMapping.enable.value else '',
# uid=[],
# group='',
# ),
desc.File( desc.File(
name='outputTextures', name='outputTextures',
label='Textures', label='Textures',
description='Output Texture files.', description='Output Texture files.',
value= lambda attr: desc.Node.internalFolder + 'texture_*.' + attr.node.colorMapping.colorMappingFileType.value if attr.node.colorMapping.enable.value else '', semantic='image',
value=desc.Node.internalFolder+'texture_*.exr',
uid=[], uid=[],
group='', group=''
), )
] ]
def upgradeAttributeValues(self, attrValues, fromVersion): def upgradeAttributeValues(self, attrValues, fromVersion):

View file

@ -9,6 +9,7 @@ FocusScope {
clip: true clip: true
property var displayedNode: _reconstruction.cameraInit
property url source property url source
property var metadata property var metadata
property var viewIn3D property var viewIn3D
@ -153,20 +154,66 @@ FocusScope {
imgContainer.y = Math.max((imgLayout.height - imgContainer.image.height * imgContainer.scale)*0.5, 0) imgContainer.y = Math.max((imgLayout.height - imgContainer.image.height * imgContainer.scale)*0.5, 0)
} }
function getImageFile(type) { function getImageFile() {
if(!_reconstruction.activeNodes) if (outputAttribute.name == "") {
return ""; return getViewpointPath(_reconstruction.selectedViewId);
var depthMapNode = _reconstruction.activeNodes.get('allDepthMap').node; }
if (type == "image") { return getFileAttributePath(displayedNode, outputAttribute.name, _reconstruction.selectedViewId);
return root.source; }
} else if (depthMapNode != undefined && _reconstruction.selectedViewId >= 0) {
return Filepath.stringToUrl(depthMapNode.internalFolder+_reconstruction.selectedViewId+"_"+type+"Map.exr"); function getFileAttributePath(node, attrName, viewId) {
// get output attribute with matching name
// and parse its value to get the image filepath
for (var i = 0; i < node.attributes.count; i++) {
var attr = node.attributes.at(i);
if (attr.name == attrName) {
return Filepath.stringToUrl(String(attr.value).replace("<VIEW_ID>", viewId));
}
} }
return ""; return "";
} }
function setImageTypes(types) { function getViewpointPath(viewId) {
imageType.types = types; // get viewpoint from cameraInit with matching id
// and get its image filepath
for (var i = 0; i < _reconstruction.viewpoints.count; i++) {
var vp = _reconstruction.viewpoints.at(i);
if (vp.childAttribute("viewId").value == viewId) {
return Filepath.stringToUrl(vp.childAttribute("path").value);
}
}
return "";
}
function getViewpointMetadata(viewId) {
// get viewpoint from cameraInit with matching id
// and get its image filepath
for (var i = 0; i < _reconstruction.viewpoints.count; i++) {
var vp = _reconstruction.viewpoints.at(i);
if (vp.childAttribute("viewId").value == viewId) {
return vp.childAttribute("metadata").value;
}
}
return "";
}
onDisplayedNodeChanged: {
var names = [];
for (var i = 0; i < displayedNode.attributes.count; i++) {
var attr = displayedNode.attributes.at(i);
if (attr.isOutput && attr.desc.semantic == "image") {
names.push(attr.name);
}
}
outputAttribute.names = names;
}
Connections {
target: _reconstruction
onSelectedViewIdChanged: {
root.source = getImageFile();
root.metadata = getViewpointMetadata(_reconstruction.selectedViewId);
}
} }
// context menu // context menu
@ -276,7 +323,7 @@ FocusScope {
// Note: It does not work to use previously created component, so we re-create it with setSource. // Note: It does not work to use previously created component, so we re-create it with setSource.
// floatViewerComp.createObject(floatImageViewerLoader, { // floatViewerComp.createObject(floatImageViewerLoader, {
setSource("FloatImage.qml", { setSource("FloatImage.qml", {
'source': Qt.binding(function() { return getImageFile(imageType.type); }), 'source': Qt.binding(function() { return getImageFile(); }),
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue; }), 'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue; }),
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue; }), 'gain': Qt.binding(function() { return hdrImageToolbar.gainValue; }),
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue; }), 'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue; }),
@ -340,7 +387,7 @@ FocusScope {
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
autoTransform: true autoTransform: true
onWidthChanged: if(status==Image.Ready) fit() onWidthChanged: if(status==Image.Ready) fit()
source: getImageFile(imageType.type) source: getImageFile()
onStatusChanged: { onStatusChanged: {
// update cache source when image is loaded // update cache source when image is loaded
if(status === Image.Ready) if(status === Image.Ready)
@ -504,7 +551,7 @@ FocusScope {
font.pointSize: 8 font.pointSize: 8
readOnly: true readOnly: true
selectByMouse: true selectByMouse: true
text: Filepath.urlToString(getImageFile(imageType.type)) text: Filepath.urlToString(getImageFile())
} }
// show which depthmap node is active // show which depthmap node is active
@ -994,7 +1041,7 @@ FocusScope {
} }
ComboBox { ComboBox {
id: imageType id: outputAttribute
property var activeNode: root.oiioPluginAvailable ? _reconstruction.activeNodes.get('allDepthMap').node : null property var activeNode: root.oiioPluginAvailable ? _reconstruction.activeNodes.get('allDepthMap').node : null
// set min size to 5 characters + one margin for the combobox // set min size to 5 characters + one margin for the combobox
clip: true clip: true
@ -1002,10 +1049,10 @@ FocusScope {
Layout.preferredWidth: 6.0 * Qt.application.font.pixelSize Layout.preferredWidth: 6.0 * Qt.application.font.pixelSize
flat: true flat: true
property var types: ["image"] property var names: []
property string type: enabled ? types[currentIndex] : types[0] property string name: (names.length > 0) ? (enabled ? names[currentIndex] : names[0]) : ""
model: types model: names
enabled: activeNode enabled: activeNode
} }
@ -1018,7 +1065,7 @@ FocusScope {
Layout.minimumWidth: 0 Layout.minimumWidth: 0
onClicked: { onClicked: {
root.viewIn3D(root.getImageFile("depth")) root.viewIn3D(root.getFileAttributePath(activeNode, "depth", _reconstruction.selectedViewId));
} }
} }

View file

@ -148,14 +148,6 @@ Item {
viewIn3D: root.load3DMedia viewIn3D: root.load3DMedia
Connections {
target: imageGallery
onCurrentItemChanged: {
viewer2D.source = imageGallery.currentItemSource
viewer2D.metadata = imageGallery.currentItemMetadata
}
}
DropArea { DropArea {
anchors.fill: parent anchors.fill: parent
keys: ["text/uri-list"] keys: ["text/uri-list"]

View file

@ -851,11 +851,17 @@ ApplicationWindow {
reconstruction: _reconstruction reconstruction: _reconstruction
readOnly: _reconstruction.computing readOnly: _reconstruction.computing
function viewAttribute(attribute, mouse) { function viewNode(node, mouse) {
let viewable = false; // 2D viewer
viewable = workspaceView.viewIn2D(attribute); viewer2D.displayedNode = node;
viewable |= workspaceView.viewIn3D(attribute, mouse);
return viewable; // 3D viewer
for(var i=0; i < node.attributes.count; ++i)
{
var attr = node.attributes.at(i)
if(attr.isOutput && workspaceView.viewIn3D(attr, mouse))
break;
}
} }
function viewIn3D(attribute, mouse) { function viewIn3D(attribute, mouse) {
@ -867,33 +873,6 @@ ApplicationWindow {
panel3dViewer.viewer3D.solo(attribute); panel3dViewer.viewer3D.solo(attribute);
return loaded; return loaded;
} }
function viewIn2D(attribute) {
var imageExts = ['.exr', '.jpg', '.tif', '.png'];
var ext = Filepath.extension(attribute.value);
if(imageExts.indexOf(ext) == -1)
{
return false;
}
if(attribute.value.includes('*'))
{
// For now, the viewer only supports a single image.
var firstFile = Filepath.globFirst(attribute.value)
viewer2D.source = Filepath.stringToUrl(firstFile);
}
else
{
viewer2D.source = Filepath.stringToUrl(attribute.value);
return true;
}
return false;
}
function setImageTypes(types) {
viewer2D.setImageTypes(types);
}
} }
} }
@ -958,17 +937,8 @@ ApplicationWindow {
nodeTypesModel: _nodeTypes nodeTypesModel: _nodeTypes
onNodeDoubleClicked: { onNodeDoubleClicked: {
workspaceView.setImageTypes(node.outputImageTypes);
_reconstruction.setActiveNode(node); _reconstruction.setActiveNode(node);
workspaceView.viewNode(node, mouse);
let viewable = false;
for(var i=0; i < node.attributes.count; ++i)
{
var attr = node.attributes.at(i)
if(attr.isOutput && workspaceView.viewAttribute(attr, mouse))
break;
}
} }
onComputeRequest: computeManager.compute(node) onComputeRequest: computeManager.compute(node)
onSubmitRequest: computeManager.submit(node) onSubmitRequest: computeManager.submit(node)