mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-06-07 05:12:00 +02:00
[ui] Add visualisation for automatically detected fisheye circle
This commit is contained in:
parent
b7b1f49bc5
commit
bb60c9b78d
4 changed files with 72 additions and 12 deletions
|
@ -45,6 +45,13 @@ class PanoramaInit(desc.CommandLineNode):
|
||||||
value=False,
|
value=False,
|
||||||
uid=[0],
|
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(
|
desc.GroupAttribute(
|
||||||
name="fisheyeCenterOffset",
|
name="fisheyeCenterOffset",
|
||||||
label="Fisheye Center",
|
label="Fisheye Center",
|
||||||
|
|
|
@ -3,6 +3,8 @@ import QtQuick 2.11
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property bool readOnly: false
|
||||||
|
|
||||||
signal moved()
|
signal moved()
|
||||||
signal incrementRadius(real radiusOffset)
|
signal incrementRadius(real radiusOffset)
|
||||||
|
|
||||||
|
@ -10,7 +12,20 @@ Rectangle {
|
||||||
height: width
|
height: width
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
border.width: 5
|
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 {
|
Behavior on x {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
@ -32,11 +47,15 @@ Rectangle {
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mArea
|
id: mArea
|
||||||
|
enabled: !root.readOnly
|
||||||
anchors.fill: parent
|
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
|
property bool controlModifierEnabled: false
|
||||||
onPositionChanged: {
|
onPositionChanged: {
|
||||||
mArea.controlModifierEnabled = (mouse.modifiers & Qt.ControlModifier)
|
mArea.controlModifierEnabled = (mouse.modifiers & Qt.ControlModifier)
|
||||||
|
mouse.accepted = false;
|
||||||
}
|
}
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
|
@ -249,24 +249,35 @@ FocusScope {
|
||||||
// note: use a Loader to evaluate if a PanoramaInit node exist and displayFisheyeCircle checked at runtime
|
// note: use a Loader to evaluate if a PanoramaInit node exist and displayFisheyeCircle checked at runtime
|
||||||
Loader {
|
Loader {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
active: (_reconstruction.panoramaInit && displayFisheyeCircleLoader.checked)
|
active: (displayFisheyeCircleLoader.checked && _reconstruction.panoramaInit)
|
||||||
sourceComponent: CircleGizmo {
|
sourceComponent: CircleGizmo {
|
||||||
x: _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x").value
|
property bool useAuto: _reconstruction.panoramaInit.attribute("estimateFisheyeCircle").value
|
||||||
y: _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y").value
|
readOnly: useAuto
|
||||||
property real fisheyeRadius: _reconstruction.panoramaInit.attribute("fisheyeRadius").value
|
visible: (!useAuto) || _reconstruction.panoramaInit.isComputed
|
||||||
radius: (imgContainer.image ? Math.min(imgContainer.image.width, imgContainer.image.height) : 1.0) * 0.5 * (fisheyeRadius * 0.01)
|
property real userFisheyeRadius: _reconstruction.panoramaInit.attribute("fisheyeRadius").value
|
||||||
border.width: Math.max(1, (3.0 / imgContainer.scale))
|
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: {
|
onMoved: {
|
||||||
|
if(!useAuto)
|
||||||
|
{
|
||||||
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x"), x)
|
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x"), x)
|
||||||
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y"), y)
|
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y"), y)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
onIncrementRadius: {
|
onIncrementRadius: {
|
||||||
|
if(!useAuto)
|
||||||
|
{
|
||||||
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeRadius"), _reconstruction.panoramaInit.attribute("fisheyeRadius").value + radiusOffset)
|
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeRadius"), _reconstruction.panoramaInit.attribute("fisheyeRadius").value + radiusOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -406,7 +417,7 @@ FocusScope {
|
||||||
}
|
}
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
id: displayFisheyeCircleLoader
|
id: displayFisheyeCircleLoader
|
||||||
ToolTip.text: "Display Fisheye Circle"
|
ToolTip.text: "Display Fisheye Circle: " + (_reconstruction.panoramaInit ? _reconstruction.panoramaInit.label : "No Node")
|
||||||
text: MaterialIcons.panorama_fish_eye
|
text: MaterialIcons.panorama_fish_eye
|
||||||
font.pointSize: 11
|
font.pointSize: 11
|
||||||
Layout.minimumWidth: 0
|
Layout.minimumWidth: 0
|
||||||
|
|
|
@ -559,6 +559,29 @@ class Reconstruction(UIGraph):
|
||||||
tmpCameraInit = Node("CameraInit", viewpoints=views, intrinsics=intrinsics)
|
tmpCameraInit = Node("CameraInit", viewpoints=views, intrinsics=intrinsics)
|
||||||
self.hdrCameraInit = tmpCameraInit
|
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):
|
def updatePanoramaInitNode(self):
|
||||||
""" Set the current FeatureExtraction node based on the current CameraInit node. """
|
""" Set the current FeatureExtraction node based on the current CameraInit node. """
|
||||||
self.panoramaInit = self.lastNodeOfType('PanoramaInit', self.cameraInit) if self.cameraInit else None
|
self.panoramaInit = self.lastNodeOfType('PanoramaInit', self.cameraInit) if self.cameraInit else None
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue