diff --git a/meshroom/core/__init__.py b/meshroom/core/__init__.py index c1856e51fa..cba9556201 100644 --- a/meshroom/core/__init__.py +++ b/meshroom/core/__init__.py @@ -86,9 +86,9 @@ def loadPlugins(folder, packageName, classType): errors.append(' * {}: {}'.format(pluginName, str(e))) if errors: - logging.warning('== The following plugins could not be loaded ==\n' - '{}\n' - .format('\n'.join(errors))) + logging.warning('== The following "{package}" plugins could not be loaded ==\n' + '{errorMsg}\n' + .format(package=packageName, errorMsg='\n'.join(errors))) return pluginTypes diff --git a/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml b/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml index 7f5be284e1..c9cf40a1c5 100644 --- a/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml +++ b/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml @@ -16,7 +16,7 @@ MessageDialog { // alias to underlying compatibilityNodes model readonly property var nodesModel: uigraph.graph.compatibilityNodes // the total number of compatibility issues - readonly property int issueCount: nodesModel.count + readonly property int issueCount: (nodesModel != undefined) ? nodesModel.count : 0 // the number of CompatibilityNodes that can be upgraded readonly property int upgradableCount: { var count = 0 diff --git a/meshroom/ui/qml/Viewer/Viewer2D.qml b/meshroom/ui/qml/Viewer/Viewer2D.qml index 1a4eb8c4b7..a9efa144ef 100644 --- a/meshroom/ui/qml/Viewer/Viewer2D.qml +++ b/meshroom/ui/qml/Viewer/Viewer2D.qml @@ -2,15 +2,16 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import MaterialIcons 2.2 - import Controls 1.0 FocusScope { id: root clip: true + property url source property var metadata + property var viewIn3D function clear() { @@ -35,6 +36,15 @@ FocusScope { image.y = Math.max((root.height-image.height*image.scale)*0.5, 0) } + function getImageFile(type) { + if (type == "image") { + return root.source; + } else if (_reconstruction.depthMap != undefined && _reconstruction.selectedViewId >= 0) { + return Filepath.stringToUrl(_reconstruction.depthMap.internalFolder+_reconstruction.selectedViewId+"_"+type+"Map.exr"); + } + return ""; + } + // context menu property Component contextMenu: Menu { MenuItem { @@ -56,7 +66,7 @@ FocusScope { fillMode: Image.PreserveAspectFit autoTransform: true onWidthChanged: if(status==Image.Ready) fit() - source: root.source + source: getImageFile(imageType.type) onStatusChanged: { // update cache source when image is loaded if(status === Image.Ready) @@ -152,15 +162,31 @@ FocusScope { width: parent.width radius: 0 padding: 4 - // selectable filepath to source image - TextField { - width: parent.width - padding: 0 - background: Item {} - font.pointSize: 8 - readOnly: true - selectByMouse: true - text: Filepath.urlToString(source) + + RowLayout { + anchors.fill: parent + // selectable filepath to source image + TextField { + padding: 0 + background: Item {} + horizontalAlignment: TextInput.AlignLeft + Layout.fillWidth: true + font.pointSize: 8 + readOnly: true + selectByMouse: true + text: Filepath.urlToString(image.source) + } + // show which depthmap node is active + Label { + id: depthMapNodeName + visible: (_reconstruction.depthMap != undefined) && (imageType.type != "image") + text: (_reconstruction.depthMap != undefined ? _reconstruction.depthMap.label : "") + font.pointSize: 8 + + horizontalAlignment: TextInput.AlignLeft + Layout.fillWidth: false + Layout.preferredWidth: contentWidth + } } } @@ -227,6 +253,31 @@ FocusScope { } } + ComboBox { + id: imageType + // set min size to 5 characters + one margin for the combobox + Layout.minimumWidth: 6.0 * Qt.application.font.pixelSize + Layout.preferredWidth: Layout.minimumWidth + flat: true + + property var types: ["image", "depth", "sim"] + property string type: types[currentIndex] + + model: types + enabled: _reconstruction.depthMap != undefined + } + + MaterialToolButton { + font.pointSize: 11 + enabled: _reconstruction.depthMap != undefined + ToolTip.text: "View Depth Map in 3D (" + (_reconstruction.depthMap != undefined ? _reconstruction.depthMap.label : "No DepthMap Node Selected") + ")" + text: MaterialIcons.input + + onClicked: { + root.viewIn3D(root.getImageFile("depth")) + } + } + ToolButton { id: metadataCB padding: 3 diff --git a/meshroom/ui/qml/WorkspaceView.qml b/meshroom/ui/qml/WorkspaceView.qml index 4a8264e159..f309341c24 100644 --- a/meshroom/ui/qml/WorkspaceView.qml +++ b/meshroom/ui/qml/WorkspaceView.qml @@ -81,11 +81,13 @@ Item { title: "Image Viewer" Layout.fillHeight: true Layout.fillWidth: true - Layout.minimumWidth: 40 + Layout.minimumWidth: 280 Viewer2D { id: viewer2D anchors.fill: parent + viewIn3D: root.load3DMedia + Connections { target: imageGallery onCurrentItemChanged: { diff --git a/meshroom/ui/reconstruction.py b/meshroom/ui/reconstruction.py index cc77a44bb7..e5c54d321b 100755 --- a/meshroom/ui/reconstruction.py +++ b/meshroom/ui/reconstruction.py @@ -387,6 +387,10 @@ def __init__(self, defaultPipeline='', parent=None): # - Prepare Dense Scene (undistorted images) self._prepareDenseScene = None + # - Depth Map + self._depthMap = None + self.cameraInitChanged.connect(self.updateDepthMapNode) + # - Texturing self._texturing = None @@ -441,6 +445,7 @@ def onGraphChanged(self): self.featureExtraction = None self.sfm = None self.prepareDenseScene = None + self.depthMap = None self.texturing = None self.updateCameraInits() if not self._graph: @@ -482,6 +487,10 @@ def updateFeatureExtraction(self): """ Set the current FeatureExtraction node based on the current CameraInit node. """ self.featureExtraction = self.lastNodeOfType('FeatureExtraction', self.cameraInit) if self.cameraInit else None + def updateDepthMapNode(self): + """ Set the current FeatureExtraction node based on the current CameraInit node. """ + self.depthMap = self.lastNodeOfType('DepthMapFilter', self.cameraInit) if self.cameraInit else None + def lastSfmNode(self): """ Retrieve the last SfM node from the initial CameraInit node. """ return self.lastNodeOfType("StructureFromMotion", self._cameraInit, Status.SUCCESS) @@ -787,6 +796,8 @@ def setActiveNodeOfType(self, node): self.cameraInit = node elif node.nodeType == "PrepareDenseScene": self.prepareDenseScene = node + elif node.nodeType in ("DepthMap", "DepthMapFilter"): + self.depthMap = node def updateSfMResults(self): """ @@ -947,8 +958,13 @@ def getPoseRT(self, viewpoint): # convenient property for QML binding re-evaluation when sfm report changes sfmReport = Property(bool, lambda self: len(self._poses) > 0, notify=sfmReportChanged) sfmAugmented = Signal(Node, Node) + prepareDenseSceneChanged = Signal() prepareDenseScene = makeProperty(QObject, "_prepareDenseScene", notify=prepareDenseSceneChanged, resetOnDestroy=True) + + depthMapChanged = Signal() + depthMap = makeProperty(QObject, "_depthMap", depthMapChanged, resetOnDestroy=True) + texturingChanged = Signal() texturing = makeProperty(QObject, "_texturing", notify=texturingChanged)