diff --git a/meshroom/core/desc.py b/meshroom/core/desc.py index 1810c9d2..6766f729 100644 --- a/meshroom/core/desc.py +++ b/meshroom/core/desc.py @@ -489,7 +489,6 @@ class Node(object): parallelization = None documentation = '' category = 'Other' - outputImageTypes = ['image'] def __init__(self): pass diff --git a/meshroom/core/node.py b/meshroom/core/node.py index 25b05443..372c5384 100644 --- a/meshroom/core/node.py +++ b/meshroom/core/node.py @@ -568,9 +568,6 @@ class BaseNode(BaseObject): def getAttributes(self): return self._attributes - def getOutputImageTypes(self): - return self.nodeDesc.outputImageTypes - @Slot(str, result=bool) def hasAttribute(self, name): return name in self._attributes.keys() @@ -1056,7 +1053,6 @@ class BaseNode(BaseObject): x = Property(float, lambda self: self._position.x, notify=positionChanged) y = Property(float, lambda self: self._position.y, notify=positionChanged) attributes = Property(BaseObject, getAttributes, constant=True) - outputImageTypes = Property(Variant, getOutputImageTypes, constant=True) internalFolderChanged = Signal() internalFolder = Property(str, internalFolder.fget, notify=internalFolderChanged) depthChanged = Signal() diff --git a/meshroom/nodes/aliceVision/DepthMap.py b/meshroom/nodes/aliceVision/DepthMap.py index 8bd810e2..53da77f0 100644 --- a/meshroom/nodes/aliceVision/DepthMap.py +++ b/meshroom/nodes/aliceVision/DepthMap.py @@ -293,11 +293,27 @@ Use a downscale factor of one (full-resolution) only if the quality of the input outputs = [ desc.File( name='output', - label='Output', + label='Output Folder', description='Output folder for generated depth maps.', value=desc.Node.internalFolder, uid=[], ), + desc.File( + name='depth', + label='Depth Maps', + description='Generated depth maps.', + semantic='image', + value=desc.Node.internalFolder + '_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 + '_simMap.exr', + uid=[], + group='', # do not export on the command line + ), ] - - outputImageTypes = ['depth', 'sim'] diff --git a/meshroom/nodes/aliceVision/DepthMapFilter.py b/meshroom/nodes/aliceVision/DepthMapFilter.py index 5043d074..5280c808 100644 --- a/meshroom/nodes/aliceVision/DepthMapFilter.py +++ b/meshroom/nodes/aliceVision/DepthMapFilter.py @@ -128,4 +128,22 @@ This allows to filter unstable points before starting the fusion of all depth ma value=desc.Node.internalFolder, uid=[], ), + desc.File( + name='depth', + label='Depth Maps', + description='Filtered depth maps.', + semantic='image', + value=desc.Node.internalFolder + '_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 + '_simMap.exr', + uid=[], + group='', # do not export on the command line + ), ] diff --git a/meshroom/nodes/aliceVision/ImageProcessing.py b/meshroom/nodes/aliceVision/ImageProcessing.py index e276c365..652ef3b3 100644 --- a/meshroom/nodes/aliceVision/ImageProcessing.py +++ b/meshroom/nodes/aliceVision/ImageProcessing.py @@ -338,12 +338,21 @@ Convert or apply filtering to the input images. value=desc.Node.internalFolder, 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( name='outputImages', label='Output Images', description='Output Image Files.', - value= outputImagesValueFunct, - group='', # do not export on the command line - uid=[], - ), + semantic='image', + value=desc.Node.internalFolder + '.exr', + group='', # do not export on the command line + uid=[] + ) ] diff --git a/meshroom/nodes/aliceVision/Texturing.py b/meshroom/nodes/aliceVision/Texturing.py index 34ed45d4..1f1c9bba 100644 --- a/meshroom/nodes/aliceVision/Texturing.py +++ b/meshroom/nodes/aliceVision/Texturing.py @@ -342,14 +342,23 @@ Many cameras are contributing to the low frequencies and only the best ones cont uid=[], 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( 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 '', + semantic='image', + value=desc.Node.internalFolder+'texture_*.exr', uid=[], - group='', - ), + group='' + ) ] def upgradeAttributeValues(self, attrValues, fromVersion): diff --git a/meshroom/ui/qml/Viewer/Viewer2D.qml b/meshroom/ui/qml/Viewer/Viewer2D.qml index f60e5e26..c181ae73 100644 --- a/meshroom/ui/qml/Viewer/Viewer2D.qml +++ b/meshroom/ui/qml/Viewer/Viewer2D.qml @@ -9,6 +9,7 @@ FocusScope { clip: true + property var displayedNode: _reconstruction.cameraInit property url source property var metadata property var viewIn3D @@ -153,20 +154,66 @@ FocusScope { imgContainer.y = Math.max((imgLayout.height - imgContainer.image.height * imgContainer.scale)*0.5, 0) } - function getImageFile(type) { - if(!_reconstruction.activeNodes) - return ""; - var depthMapNode = _reconstruction.activeNodes.get('allDepthMap').node; - if (type == "image") { - return root.source; - } else if (depthMapNode != undefined && _reconstruction.selectedViewId >= 0) { - return Filepath.stringToUrl(depthMapNode.internalFolder+_reconstruction.selectedViewId+"_"+type+"Map.exr"); + function getImageFile() { + if (outputAttribute.name == "") { + return getViewpointPath(_reconstruction.selectedViewId); + } + return getFileAttributePath(displayedNode, outputAttribute.name, _reconstruction.selectedViewId); + } + + 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("", viewId)); + } } return ""; } - function setImageTypes(types) { - imageType.types = types; + function getViewpointPath(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 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 @@ -276,7 +323,7 @@ FocusScope { // Note: It does not work to use previously created component, so we re-create it with setSource. // floatViewerComp.createObject(floatImageViewerLoader, { setSource("FloatImage.qml", { - 'source': Qt.binding(function() { return getImageFile(imageType.type); }), + 'source': Qt.binding(function() { return getImageFile(); }), 'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue; }), 'gain': Qt.binding(function() { return hdrImageToolbar.gainValue; }), 'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue; }), @@ -340,7 +387,7 @@ FocusScope { fillMode: Image.PreserveAspectFit autoTransform: true onWidthChanged: if(status==Image.Ready) fit() - source: getImageFile(imageType.type) + source: getImageFile() onStatusChanged: { // update cache source when image is loaded if(status === Image.Ready) @@ -504,7 +551,7 @@ FocusScope { font.pointSize: 8 readOnly: true selectByMouse: true - text: Filepath.urlToString(getImageFile(imageType.type)) + text: Filepath.urlToString(getImageFile()) } // show which depthmap node is active @@ -994,7 +1041,7 @@ FocusScope { } ComboBox { - id: imageType + id: outputAttribute property var activeNode: root.oiioPluginAvailable ? _reconstruction.activeNodes.get('allDepthMap').node : null // set min size to 5 characters + one margin for the combobox clip: true @@ -1002,10 +1049,10 @@ FocusScope { Layout.preferredWidth: 6.0 * Qt.application.font.pixelSize flat: true - property var types: ["image"] - property string type: enabled ? types[currentIndex] : types[0] + property var names: [] + property string name: (names.length > 0) ? (enabled ? names[currentIndex] : names[0]) : "" - model: types + model: names enabled: activeNode } @@ -1018,7 +1065,7 @@ FocusScope { Layout.minimumWidth: 0 onClicked: { - root.viewIn3D(root.getImageFile("depth")) + root.viewIn3D(root.getFileAttributePath(activeNode, "depth", _reconstruction.selectedViewId)); } } diff --git a/meshroom/ui/qml/WorkspaceView.qml b/meshroom/ui/qml/WorkspaceView.qml index ced16fe2..c4582e8f 100644 --- a/meshroom/ui/qml/WorkspaceView.qml +++ b/meshroom/ui/qml/WorkspaceView.qml @@ -148,14 +148,6 @@ Item { viewIn3D: root.load3DMedia - Connections { - target: imageGallery - onCurrentItemChanged: { - viewer2D.source = imageGallery.currentItemSource - viewer2D.metadata = imageGallery.currentItemMetadata - } - } - DropArea { anchors.fill: parent keys: ["text/uri-list"] diff --git a/meshroom/ui/qml/main.qml b/meshroom/ui/qml/main.qml index bd3977dd..db84ae24 100755 --- a/meshroom/ui/qml/main.qml +++ b/meshroom/ui/qml/main.qml @@ -851,11 +851,17 @@ ApplicationWindow { reconstruction: _reconstruction readOnly: _reconstruction.computing - function viewAttribute(attribute, mouse) { - let viewable = false; - viewable = workspaceView.viewIn2D(attribute); - viewable |= workspaceView.viewIn3D(attribute, mouse); - return viewable; + function viewNode(node, mouse) { + // 2D viewer + viewer2D.displayedNode = node; + + // 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) { @@ -867,33 +873,6 @@ ApplicationWindow { panel3dViewer.viewer3D.solo(attribute); 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 onNodeDoubleClicked: { - workspaceView.setImageTypes(node.outputImageTypes); - _reconstruction.setActiveNode(node); - - 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; - } + workspaceView.viewNode(node, mouse); } onComputeRequest: computeManager.compute(node) onSubmitRequest: computeManager.submit(node)