From bb60c9b78dd1f94d865c11d73bc3cbd96089cdc8 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Mon, 23 Mar 2020 19:25:57 +0100 Subject: [PATCH] [ui] Add visualisation for automatically detected fisheye circle --- meshroom/nodes/aliceVision/PanoramaInit.py | 7 +++++ meshroom/ui/qml/Viewer/CircleGizmo.qml | 23 ++++++++++++++-- meshroom/ui/qml/Viewer/Viewer2D.qml | 31 +++++++++++++++------- meshroom/ui/reconstruction.py | 23 ++++++++++++++++ 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/meshroom/nodes/aliceVision/PanoramaInit.py b/meshroom/nodes/aliceVision/PanoramaInit.py index d1867dcf..5d3a4c07 100644 --- a/meshroom/nodes/aliceVision/PanoramaInit.py +++ b/meshroom/nodes/aliceVision/PanoramaInit.py @@ -45,6 +45,13 @@ class PanoramaInit(desc.CommandLineNode): value=False, uid=[0], ), + desc.BoolParam( + name='estimateFisheyeCircle', + label='Estimate Fisheye Circle', + description='Automatically estimate the Fisheye Circle center and radius instead of using user values.', + value=True, + uid=[0], + ), desc.GroupAttribute( name="fisheyeCenterOffset", label="Fisheye Center", diff --git a/meshroom/ui/qml/Viewer/CircleGizmo.qml b/meshroom/ui/qml/Viewer/CircleGizmo.qml index bbfcb6f4..1a48c22e 100644 --- a/meshroom/ui/qml/Viewer/CircleGizmo.qml +++ b/meshroom/ui/qml/Viewer/CircleGizmo.qml @@ -3,6 +3,8 @@ import QtQuick 2.11 Rectangle { id: root + property bool readOnly: false + signal moved() signal incrementRadius(real radiusOffset) @@ -10,7 +12,20 @@ Rectangle { height: width color: "transparent" border.width: 5 - border.color: "yellow" + border.color: readOnly ? "green" : "yellow" + + Rectangle { + color: parent.color + anchors.centerIn: parent + width: 20 + height: 2 + } + Rectangle { + color: parent.color + anchors.centerIn: parent + width: 2 + height: 20 + } Behavior on x { NumberAnimation { @@ -32,11 +47,15 @@ Rectangle { MouseArea { id: mArea + enabled: !root.readOnly anchors.fill: parent - cursorShape: controlModifierEnabled ? Qt.SizeBDiagCursor : (pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor) + cursorShape: root.readOnly ? Qt.ArrowCursor : (controlModifierEnabled ? Qt.SizeBDiagCursor : (pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor)) + propagateComposedEvents: true + property bool controlModifierEnabled: false onPositionChanged: { mArea.controlModifierEnabled = (mouse.modifiers & Qt.ControlModifier) + mouse.accepted = false; } acceptedButtons: Qt.LeftButton hoverEnabled: true diff --git a/meshroom/ui/qml/Viewer/Viewer2D.qml b/meshroom/ui/qml/Viewer/Viewer2D.qml index 23c30772..3a394395 100644 --- a/meshroom/ui/qml/Viewer/Viewer2D.qml +++ b/meshroom/ui/qml/Viewer/Viewer2D.qml @@ -249,20 +249,31 @@ FocusScope { // note: use a Loader to evaluate if a PanoramaInit node exist and displayFisheyeCircle checked at runtime Loader { anchors.centerIn: parent - active: (_reconstruction.panoramaInit && displayFisheyeCircleLoader.checked) + active: (displayFisheyeCircleLoader.checked && _reconstruction.panoramaInit) sourceComponent: CircleGizmo { - x: _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x").value - y: _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y").value - property real fisheyeRadius: _reconstruction.panoramaInit.attribute("fisheyeRadius").value - radius: (imgContainer.image ? Math.min(imgContainer.image.width, imgContainer.image.height) : 1.0) * 0.5 * (fisheyeRadius * 0.01) - border.width: Math.max(1, (3.0 / imgContainer.scale)) + property bool useAuto: _reconstruction.panoramaInit.attribute("estimateFisheyeCircle").value + readOnly: useAuto + visible: (!useAuto) || _reconstruction.panoramaInit.isComputed + property real userFisheyeRadius: _reconstruction.panoramaInit.attribute("fisheyeRadius").value + property variant fisheyeAutoParams: _reconstruction.getAutoFisheyeCircle(_reconstruction.panoramaInit) + x: useAuto ? (fisheyeAutoParams.x - imgContainer.image.width * 0.5) : _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x").value + y: useAuto ? (fisheyeAutoParams.y - imgContainer.image.height * 0.5) : _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y").value + radius: useAuto ? fisheyeAutoParams.z : ((imgContainer.image ? Math.min(imgContainer.image.width, imgContainer.image.height) : 1.0) * 0.5 * (userFisheyeRadius * 0.01)) + + border.width: Math.max(1, (3.0 / imgContainer.scale)) onMoved: { - _reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x"), x) - _reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y"), y) + if(!useAuto) + { + _reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x"), x) + _reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y"), y) + } } onIncrementRadius: { - _reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeRadius"), _reconstruction.panoramaInit.attribute("fisheyeRadius").value + radiusOffset) + if(!useAuto) + { + _reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeRadius"), _reconstruction.panoramaInit.attribute("fisheyeRadius").value + radiusOffset) + } } } } @@ -406,7 +417,7 @@ FocusScope { } MaterialToolButton { id: displayFisheyeCircleLoader - ToolTip.text: "Display Fisheye Circle" + ToolTip.text: "Display Fisheye Circle: " + (_reconstruction.panoramaInit ? _reconstruction.panoramaInit.label : "No Node") text: MaterialIcons.panorama_fish_eye font.pointSize: 11 Layout.minimumWidth: 0 diff --git a/meshroom/ui/reconstruction.py b/meshroom/ui/reconstruction.py index 0520af48..20d5e90c 100755 --- a/meshroom/ui/reconstruction.py +++ b/meshroom/ui/reconstruction.py @@ -559,6 +559,29 @@ class Reconstruction(UIGraph): tmpCameraInit = Node("CameraInit", viewpoints=views, intrinsics=intrinsics) self.hdrCameraInit = tmpCameraInit + @Slot(QObject, result=QVector3D) + def getAutoFisheyeCircle(self, panoramaInit): + if not panoramaInit or not panoramaInit.isComputed: + return QVector3D(0.0, 0.0, 0.0) + if not panoramaInit.attribute("estimateFisheyeCircle").value: + return QVector3D(0.0, 0.0, 0.0) + + sfmFile = panoramaInit.attribute('outSfMDataFilename').value + if not os.path.exists(sfmFile): + return QVector3D(0.0, 0.0, 0.0) + import io # use io.open for Python2/3 compatibility (allow to specify encoding + errors handling) + # skip decoding errors to avoid potential exceptions due to non utf-8 characters in images metadata + with io.open(sfmFile, 'r', encoding='utf-8', errors='ignore') as f: + data = json.load(f) + + intrinsics = data.get('intrinsics', []) + if len(intrinsics) == 0: + return QVector3D(0.0, 0.0, 0.0) + intrinsic = intrinsics[0] + + res = QVector3D(float(intrinsic.get("fisheyeCenterX", 0.0)), float(intrinsic.get("fisheyeCenterY", 0.0)), float(intrinsic.get("fisheyeRadius", 0.0))) + return res + def updatePanoramaInitNode(self): """ Set the current FeatureExtraction node based on the current CameraInit node. """ self.panoramaInit = self.lastNodeOfType('PanoramaInit', self.cameraInit) if self.cameraInit else None