mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-17 08:37:17 +02:00
[ui] new generic way to manage "active" nodes per node type
This commit is contained in:
parent
ea5b639245
commit
ec67c772fa
12 changed files with 344 additions and 205 deletions
|
@ -881,7 +881,7 @@ class Graph(BaseObject):
|
|||
filterTypes (str list): (optional) only return the nodes of the given types
|
||||
(does not stop the visit, this is a post-process only)
|
||||
Returns:
|
||||
The list of nodes from startNode to the graph leaves following edges.
|
||||
The list of nodes and edges, from startNode to the graph leaves following edges.
|
||||
"""
|
||||
nodes = []
|
||||
edges = []
|
||||
|
|
|
@ -58,8 +58,9 @@ Item {
|
|||
}
|
||||
MenuItem {
|
||||
text: "Define As Center Image"
|
||||
enabled: !root.readOnly && _viewpoint.viewId != -1 && _reconstruction && _reconstruction.sfmTransform
|
||||
onClicked: _reconstruction.sfmTransform.attribute("transformation").value = _viewpoint.viewId.toString()
|
||||
property var activeNode: _reconstruction.activeNodes.get("SfMTransform").node
|
||||
enabled: !root.readOnly && _viewpoint.viewId != -1 && _reconstruction && activeNode
|
||||
onClicked: activeNode.attribute("transformation").value = _viewpoint.viewId.toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ Panel {
|
|||
|
||||
property variant cameraInits
|
||||
property variant cameraInit
|
||||
property variant hdrCameraInit
|
||||
property variant tempCameraInit
|
||||
readonly property alias currentItem: grid.currentItem
|
||||
readonly property string currentItemSource: grid.currentItem ? grid.currentItem.source : ""
|
||||
readonly property var currentItemMetadata: grid.currentItem ? grid.currentItem.metadata : undefined
|
||||
|
@ -38,7 +38,7 @@ Panel {
|
|||
|
||||
QtObject {
|
||||
id: m
|
||||
property variant currentCameraInit: displayHDR.checked ? _reconstruction.hdrCameraInit : root.cameraInit
|
||||
property variant currentCameraInit: _reconstruction.tempCameraInit ? _reconstruction.tempCameraInit : root.cameraInit
|
||||
property variant viewpoints: currentCameraInit ? currentCameraInit.attribute('viewpoints').value : undefined
|
||||
property bool readOnly: root.readOnly || displayHDR.checked
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ Panel {
|
|||
// Center of SfMTransform
|
||||
Loader {
|
||||
id: sfmTransformIndicator
|
||||
active: (viewpoint.get("viewId").value == centerViewId)
|
||||
active: viewpoint && (viewpoint.get("viewId").value == centerViewId)
|
||||
sourceComponent: ImageBadge {
|
||||
text: MaterialIcons.gamepad
|
||||
ToolTip.text: "Camera used to define the center of the scene."
|
||||
|
@ -343,54 +343,114 @@ Panel {
|
|||
}
|
||||
|
||||
footerContent: RowLayout {
|
||||
|
||||
// Image count
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
RowLayout {
|
||||
MaterialLabel { text: MaterialIcons.image }
|
||||
Label { text: grid.model.count }
|
||||
}
|
||||
RowLayout {
|
||||
visible: _reconstruction.cameraInit && _reconstruction.nbCameras
|
||||
MaterialLabel { text: MaterialIcons.videocam }
|
||||
Label { text: _reconstruction.cameraInit ? _reconstruction.nbCameras : 0 }
|
||||
}
|
||||
// Images count
|
||||
MaterialToolLabel {
|
||||
ToolTip.text: grid.model.count + " Input Images"
|
||||
iconText: MaterialIcons.image
|
||||
label: grid.model.count.toString()
|
||||
// enabled: grid.model.count > 0
|
||||
// margin: 4
|
||||
}
|
||||
// cameras count
|
||||
MaterialToolLabel {
|
||||
ToolTip.text: label + " Estimated Cameras"
|
||||
iconText: MaterialIcons.videocam
|
||||
label: _reconstruction ? _reconstruction.nbCameras.toString() : "0"
|
||||
// margin: 4
|
||||
// enabled: _reconstruction.cameraInit && _reconstruction.nbCameras
|
||||
}
|
||||
|
||||
MaterialToolButton {
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
|
||||
MaterialToolLabelButton {
|
||||
id: displayHDR
|
||||
font.pointSize: 20
|
||||
padding: 0
|
||||
anchors.margins: 0
|
||||
implicitHeight: 14
|
||||
ToolTip.text: "Visualize HDR images: " + (_reconstruction.ldr2hdr ? _reconstruction.ldr2hdr.label : "No Node")
|
||||
text: MaterialIcons.hdr_on
|
||||
visible: _reconstruction.ldr2hdr
|
||||
enabled: _reconstruction.ldr2hdr && _reconstruction.ldr2hdr.isComputed
|
||||
property string nodeID: _reconstruction.ldr2hdr ? (_reconstruction.ldr2hdr.label + _reconstruction.ldr2hdr.isComputed) : ""
|
||||
property var activeNode: _reconstruction.activeNodes.get("LdrToHdrMerge").node
|
||||
ToolTip.text: "Visualize HDR images: " + (activeNode ? activeNode.label : "No Node")
|
||||
iconText: MaterialIcons.filter
|
||||
label: activeNode ? activeNode.attribute("nbBrackets").value : ""
|
||||
// visible: activeNode
|
||||
enabled: activeNode && activeNode.isComputed
|
||||
property string nodeID: activeNode ? (activeNode.label + activeNode.isComputed) : ""
|
||||
onNodeIDChanged: {
|
||||
if(checked)
|
||||
{
|
||||
_reconstruction.setupLDRToHDRCameraInit();
|
||||
if(checked) {
|
||||
open();
|
||||
}
|
||||
}
|
||||
onEnabledChanged: {
|
||||
// Reset the toggle to avoid getting stuck
|
||||
// with the HDR node checked but disabled.
|
||||
checked = false;
|
||||
if(checked) {
|
||||
checked = false;
|
||||
close();
|
||||
}
|
||||
}
|
||||
checkable: true
|
||||
checked: false
|
||||
onClicked: { _reconstruction.setupLDRToHDRCameraInit(); }
|
||||
onClicked: {
|
||||
if(checked) {
|
||||
open();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
function open() {
|
||||
if(imageProcessing.checked)
|
||||
imageProcessing.checked = false;
|
||||
_reconstruction.setupTempCameraInit(activeNode, "outSfMDataFilename");
|
||||
}
|
||||
function close() {
|
||||
_reconstruction.clearTempCameraInit();
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||
MaterialToolButton {
|
||||
id: imageProcessing
|
||||
property var activeNode: _reconstruction.activeNodes.get("ImageProcessing").node
|
||||
font.pointSize: 15
|
||||
padding: 0
|
||||
ToolTip.text: "Preprocessed Images: " + (activeNode ? activeNode.label : "No Node")
|
||||
text: MaterialIcons.wallpaper
|
||||
visible: activeNode && activeNode.attribute("outSfMData").value
|
||||
enabled: activeNode && activeNode.isComputed
|
||||
property string nodeID: activeNode ? (activeNode.label + activeNode.isComputed) : ""
|
||||
onNodeIDChanged: {
|
||||
if(checked) {
|
||||
open();
|
||||
}
|
||||
}
|
||||
onEnabledChanged: {
|
||||
// Reset the toggle to avoid getting stuck
|
||||
// with the HDR node checked but disabled.
|
||||
if(checked) {
|
||||
checked = false;
|
||||
close();
|
||||
}
|
||||
}
|
||||
checkable: true
|
||||
checked: false
|
||||
onClicked: {
|
||||
if(checked) {
|
||||
open();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
function open() {
|
||||
if(displayHDR.checked)
|
||||
displayHDR.checked = false;
|
||||
_reconstruction.setupTempCameraInit(activeNode, "outSfMData");
|
||||
}
|
||||
function close() {
|
||||
_reconstruction.clearTempCameraInit();
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true; width: 1 }
|
||||
|
||||
// Thumbnail size icon and slider
|
||||
MaterialToolButton {
|
||||
text: MaterialIcons.photo_size_select_large
|
||||
ToolTip.text: "Thumbnails Scale"
|
||||
padding: 0
|
||||
anchors.margins: 0
|
||||
font.pointSize: 11
|
||||
|
@ -404,5 +464,4 @@ Panel {
|
|||
implicitWidth: 70
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
23
meshroom/ui/qml/MaterialIcons/MLabel.qml
Normal file
23
meshroom/ui/qml/MaterialIcons/MLabel.qml
Normal file
|
@ -0,0 +1,23 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
|
||||
|
||||
/**
|
||||
* MLabel is a standard Label.
|
||||
* If ToolTip.text is set, it shows up a tooltip when hovered.
|
||||
*/
|
||||
Label {
|
||||
padding: 4
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
ToolTip.visible: mouseArea.containsMouse
|
||||
ToolTip.delay: 500
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: mouseArea.containsMouse ? Qt.darker(parent.palette.base, 0.6) : "transparent"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
|
||||
/**
|
||||
|
@ -7,6 +8,7 @@ import QtQuick.Controls 2.3
|
|||
* It also shows up its tooltip when hovered.
|
||||
*/
|
||||
ToolButton {
|
||||
id: control
|
||||
font.family: MaterialIcons.fontFamily
|
||||
padding: 4
|
||||
font.pointSize: 13
|
||||
|
|
45
meshroom/ui/qml/MaterialIcons/MaterialToolLabel.qml
Normal file
45
meshroom/ui/qml/MaterialIcons/MaterialToolLabel.qml
Normal file
|
@ -0,0 +1,45 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
|
||||
/**
|
||||
* MaterialToolLabel is a Label with an icon (using MaterialIcons).
|
||||
* It shows up its tooltip when hovered.
|
||||
*/
|
||||
Item {
|
||||
id: control
|
||||
property alias iconText: icon.text
|
||||
property alias iconSize: icon.font.pointSize
|
||||
property alias label: labelItem.text
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
id: icon
|
||||
font.family: MaterialIcons.fontFamily
|
||||
font.pointSize: 13
|
||||
padding: 0
|
||||
text: ""
|
||||
color: palette.text
|
||||
}
|
||||
Label {
|
||||
id: labelItem
|
||||
text: ""
|
||||
color: palette.text
|
||||
}
|
||||
Item {
|
||||
width: 5
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
ToolTip.visible: mouseArea.containsMouse
|
||||
ToolTip.delay: 500
|
||||
}
|
51
meshroom/ui/qml/MaterialIcons/MaterialToolLabelButton.qml
Normal file
51
meshroom/ui/qml/MaterialIcons/MaterialToolLabelButton.qml
Normal file
|
@ -0,0 +1,51 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
|
||||
/**
|
||||
* MaterialToolButton is a standard ToolButton using MaterialIcons font.
|
||||
* It also shows up its tooltip when hovered.
|
||||
*/
|
||||
ToolButton {
|
||||
id: control
|
||||
property alias iconText: icon.text
|
||||
property alias iconSize: icon.font.pointSize
|
||||
property alias label: labelItem.text
|
||||
padding: 0
|
||||
ToolTip.visible: ToolTip.text && hovered
|
||||
ToolTip.delay: 100
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
contentItem: RowLayout {
|
||||
Layout.margins: 0
|
||||
Label {
|
||||
id: icon
|
||||
font.family: MaterialIcons.fontFamily
|
||||
font.pointSize: 13
|
||||
padding: 0
|
||||
text: ""
|
||||
color: (checked ? palette.highlight : palette.text)
|
||||
}
|
||||
Label {
|
||||
id: labelItem
|
||||
text: ""
|
||||
padding: 0
|
||||
color: (checked ? palette.highlight : palette.text)
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: {
|
||||
if(pressed || checked || hovered)
|
||||
{
|
||||
if(pressed || checked)
|
||||
return Qt.darker(parent.palette.base, 1.3)
|
||||
if(hovered)
|
||||
return Qt.darker(parent.palette.base, 0.6)
|
||||
}
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
border.color: checked ? Qt.darker(parent.palette.base, 1.4) : "transparent"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
module MaterialIcons
|
||||
singleton MaterialIcons 2.2 MaterialIcons.qml
|
||||
MaterialToolButton 2.2 MaterialToolButton.qml
|
||||
MaterialToolLabelButton 2.2 MaterialToolLabelButton.qml
|
||||
MaterialToolLabel 2.2 MaterialToolLabel.qml
|
||||
MaterialLabel 2.2 MaterialLabel.qml
|
||||
MLabel 2.2 MLabel.qml
|
||||
|
|
|
@ -104,10 +104,11 @@ FocusScope {
|
|||
}
|
||||
|
||||
function getImageFile(type) {
|
||||
var depthMapNode = _reconstruction.activeNodes.get('allDepthMap').node;
|
||||
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");
|
||||
} else if (depthMapNode != undefined && _reconstruction.selectedViewId >= 0) {
|
||||
return Filepath.stringToUrl(depthMapNode.internalFolder+_reconstruction.selectedViewId+"_"+type+"Map.exr");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -245,8 +246,8 @@ FocusScope {
|
|||
// note: requires QtAliceVision plugin - use a Loader to evaluate plugin availability at runtime
|
||||
Loader {
|
||||
id: featuresViewerLoader
|
||||
|
||||
active: displayFeatures.checked
|
||||
property var activeNode: _reconstruction.activeNodes.get("FeatureExtraction").node
|
||||
|
||||
// handle rotation/position based on available metadata
|
||||
rotation: {
|
||||
|
@ -265,8 +266,8 @@ FocusScope {
|
|||
// instantiate and initialize a FeaturesViewer component dynamically using Loader.setSource
|
||||
setSource("FeaturesViewer.qml", {
|
||||
'viewId': Qt.binding(function() { return _reconstruction.selectedViewId; }),
|
||||
'model': Qt.binding(function() { return _reconstruction.featureExtraction ? _reconstruction.featureExtraction.attribute("describerTypes").value : ""; }),
|
||||
'featureFolder': Qt.binding(function() { return _reconstruction.featureExtraction ? Filepath.stringToUrl(_reconstruction.featureExtraction.attribute("output").value) : ""; }),
|
||||
'model': Qt.binding(function() { return activeNode ? activeNode.attribute("describerTypes").value : ""; }),
|
||||
'featureFolder': Qt.binding(function() { return activeNode ? Filepath.stringToUrl(activeNode.attribute("output").value) : ""; }),
|
||||
'tracks': Qt.binding(function() { return mtracksLoader.status === Loader.Ready ? mtracksLoader.item : null; }),
|
||||
'sfmData': Qt.binding(function() { return msfmDataLoader.status === Loader.Ready ? msfmDataLoader.item : null; }),
|
||||
})
|
||||
|
@ -281,7 +282,8 @@ FocusScope {
|
|||
// note: use a Loader to evaluate if a PanoramaInit node exist and displayFisheyeCircle checked at runtime
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: (displayFisheyeCircleLoader.checked && _reconstruction.panoramaInit)
|
||||
property var activeNode: _reconstruction.activeNodes.get("PanoramaInit").node
|
||||
active: (displayFisheyeCircleLoader.checked && activeNode)
|
||||
|
||||
// handle rotation/position based on available metadata
|
||||
rotation: {
|
||||
|
@ -294,28 +296,28 @@ FocusScope {
|
|||
}
|
||||
|
||||
sourceComponent: CircleGizmo {
|
||||
property bool useAuto: _reconstruction.panoramaInit.attribute("estimateFisheyeCircle").value
|
||||
property bool useAuto: activeNode.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)
|
||||
visible: (!useAuto) || activeNode.isComputed
|
||||
property real userFisheyeRadius: activeNode.attribute("fisheyeRadius").value
|
||||
property variant fisheyeAutoParams: _reconstruction.getAutoFisheyeCircle(activeNode)
|
||||
|
||||
x: useAuto ? fisheyeAutoParams.x : _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x").value
|
||||
y: useAuto ? fisheyeAutoParams.y : _reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y").value
|
||||
x: useAuto ? fisheyeAutoParams.x : activeNode.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x").value
|
||||
y: useAuto ? fisheyeAutoParams.y : activeNode.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: {
|
||||
if(!useAuto)
|
||||
{
|
||||
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x"), x)
|
||||
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y"), y)
|
||||
_reconstruction.setAttribute(activeNode.attribute("fisheyeCenterOffset.fisheyeCenterOffset_x"), x)
|
||||
_reconstruction.setAttribute(activeNode.attribute("fisheyeCenterOffset.fisheyeCenterOffset_y"), y)
|
||||
}
|
||||
}
|
||||
onIncrementRadius: {
|
||||
if(!useAuto)
|
||||
{
|
||||
_reconstruction.setAttribute(_reconstruction.panoramaInit.attribute("fisheyeRadius"), _reconstruction.panoramaInit.attribute("fisheyeRadius").value + radiusOffset)
|
||||
_reconstruction.setAttribute(activeNode.attribute("fisheyeRadius"), activeNode.attribute("fisheyeRadius").value + radiusOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,8 +354,9 @@ FocusScope {
|
|||
// show which depthmap node is active
|
||||
Label {
|
||||
id: depthMapNodeName
|
||||
visible: (_reconstruction.depthMap != undefined) && (imageType.type != "image")
|
||||
text: (_reconstruction.depthMap != undefined ? _reconstruction.depthMap.label : "")
|
||||
property var activeNode: _reconstruction.activeNodes.get("allDepthMap").node
|
||||
visible: (activeNode != undefined) && (imageType.type != "image")
|
||||
text: (activeNode != undefined ? activeNode.label : "")
|
||||
font.pointSize: 8
|
||||
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
|
@ -422,10 +425,9 @@ FocusScope {
|
|||
}
|
||||
Loader {
|
||||
id: mtracksLoader
|
||||
// active: _reconstruction.featureMatching
|
||||
|
||||
property bool isUsed: displayFeatures.checked || displaySfmStatsView.checked || displaySfmDataGlobalStats.checked
|
||||
property var activeNode: _reconstruction.featureMatching
|
||||
property var activeNode: _reconstruction.activeNodes.get('FeatureMatching').node
|
||||
property bool isComputed: activeNode && activeNode.isComputed
|
||||
|
||||
active: false
|
||||
|
@ -498,7 +500,7 @@ FocusScope {
|
|||
active: displayFeatures.checked && featuresViewerLoader.status === Loader.Ready
|
||||
|
||||
sourceComponent: FeaturesInfoOverlay {
|
||||
featureExtractionNode: _reconstruction.featureExtraction
|
||||
featureExtractionNode: _reconstruction.activeNodes.get('FeatureExtraction').node
|
||||
pluginStatus: featuresViewerLoader.status
|
||||
featuresViewer: featuresViewerLoader.item
|
||||
}
|
||||
|
@ -514,9 +516,8 @@ FocusScope {
|
|||
anchors.fill: parent
|
||||
|
||||
// zoom label
|
||||
Label {
|
||||
MLabel {
|
||||
text: ((imgContainer.image && (imgContainer.image.status === Image.Ready)) ? imgContainer.scale.toFixed(2) : "1.00") + "x"
|
||||
state: "xsmall"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
@ -533,6 +534,7 @@ FocusScope {
|
|||
}
|
||||
}
|
||||
}
|
||||
ToolTip.text: "Zoom"
|
||||
}
|
||||
MaterialToolButton {
|
||||
id: displayAlphaBackground
|
||||
|
@ -566,31 +568,30 @@ FocusScope {
|
|||
}
|
||||
MaterialToolButton {
|
||||
id: displayFisheyeCircleLoader
|
||||
ToolTip.text: "Display Fisheye Circle: " + (_reconstruction.panoramaInit ? _reconstruction.panoramaInit.label : "No Node")
|
||||
text: MaterialIcons.panorama_fish_eye
|
||||
property var activeNode: _reconstruction.activeNodes.get('PanoramaInit').node
|
||||
ToolTip.text: "Display Fisheye Circle: " + (activeNode ? activeNode.label : "No Node")
|
||||
text: MaterialIcons.vignette
|
||||
// text: MaterialIcons.panorama_fish_eye
|
||||
font.pointSize: 11
|
||||
Layout.minimumWidth: 0
|
||||
checkable: true
|
||||
checked: false
|
||||
enabled: _reconstruction.panoramaInit && _reconstruction.panoramaInit.attribute("useFisheye").value
|
||||
visible: _reconstruction.panoramaInit
|
||||
enabled: activeNode && activeNode.attribute("useFisheye").value
|
||||
visible: activeNode
|
||||
}
|
||||
|
||||
Label {
|
||||
id: resolutionLabel
|
||||
Layout.fillWidth: true
|
||||
text: imgContainer.image ? (imgContainer.image.sourceSize.width + "x" + imgContainer.image.sourceSize.height) : ""
|
||||
text: (imgContainer.image && imgContainer.image.sourceSize.width > 0) ? (imgContainer.image.sourceSize.width + "x" + imgContainer.image.sourceSize.height) : ""
|
||||
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
/*Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "blue"
|
||||
}*/
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: imageType
|
||||
property var activeNode: _reconstruction.activeNodes.get('allDepthMap').node
|
||||
// set min size to 5 characters + one margin for the combobox
|
||||
clip: true
|
||||
Layout.minimumWidth: 0
|
||||
|
@ -601,12 +602,13 @@ FocusScope {
|
|||
property string type: enabled ? types[currentIndex] : types[0]
|
||||
|
||||
model: types
|
||||
enabled: _reconstruction.depthMap != undefined
|
||||
enabled: activeNode
|
||||
}
|
||||
|
||||
MaterialToolButton {
|
||||
enabled: _reconstruction.depthMap != undefined
|
||||
ToolTip.text: "View Depth Map in 3D (" + (_reconstruction.depthMap != undefined ? _reconstruction.depthMap.label : "No DepthMap Node Selected") + ")"
|
||||
property var activeNode: _reconstruction.activeNodes.get('allDepthMap').node
|
||||
enabled: activeNode
|
||||
ToolTip.text: "View Depth Map in 3D (" + (activeNode ? activeNode.label : "No DepthMap Node Selected") + ")"
|
||||
text: MaterialIcons.input
|
||||
font.pointSize: 11
|
||||
Layout.minimumWidth: 0
|
||||
|
@ -618,6 +620,7 @@ FocusScope {
|
|||
|
||||
MaterialToolButton {
|
||||
id: displaySfmStatsView
|
||||
property var activeNode: _reconstruction.activeNodes.get('sfm').node
|
||||
|
||||
font.family: MaterialIcons.fontFamily
|
||||
text: MaterialIcons.assessment
|
||||
|
@ -630,10 +633,9 @@ FocusScope {
|
|||
smooth: false
|
||||
flat: true
|
||||
checkable: enabled
|
||||
enabled: _reconstruction.sfm && _reconstruction.sfm.isComputed && _reconstruction.selectedViewId >= 0
|
||||
enabled: activeNode && activeNode.isComputed && _reconstruction.selectedViewId >= 0
|
||||
onCheckedChanged: {
|
||||
if(checked == true)
|
||||
{
|
||||
if(checked == true) {
|
||||
displaySfmDataGlobalStats.checked = false
|
||||
metadataCB.checked = false
|
||||
}
|
||||
|
@ -642,6 +644,7 @@ FocusScope {
|
|||
|
||||
MaterialToolButton {
|
||||
id: displaySfmDataGlobalStats
|
||||
property var activeNode: _reconstruction.activeNodes.get('sfm').node
|
||||
|
||||
font.family: MaterialIcons.fontFamily
|
||||
text: MaterialIcons.language
|
||||
|
@ -654,10 +657,9 @@ FocusScope {
|
|||
smooth: false
|
||||
flat: true
|
||||
checkable: enabled
|
||||
enabled: _reconstruction.sfm && _reconstruction.sfm.isComputed
|
||||
enabled: activeNode && activeNode.isComputed
|
||||
onCheckedChanged: {
|
||||
if(checked == true)
|
||||
{
|
||||
if(checked == true) {
|
||||
displaySfmStatsView.checked = false
|
||||
metadataCB.checked = false
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ Item {
|
|||
readOnly: root.readOnly
|
||||
cameraInits: root.cameraInits
|
||||
cameraInit: reconstruction.cameraInit
|
||||
hdrCameraInit: reconstruction.hdrCameraInit
|
||||
tempCameraInit: reconstruction.tempCameraInit
|
||||
currentIndex: reconstruction.cameraInitIndex
|
||||
onRemoveImageRequest: reconstruction.removeAttribute(attribute)
|
||||
onFilesDropped: reconstruction.handleFilesDrop(drop, augmentSfm ? null : cameraInit)
|
||||
|
@ -191,7 +191,7 @@ Item {
|
|||
mediaLibrary: viewer3D.library
|
||||
camera: viewer3D.mainCamera
|
||||
uigraph: reconstruction
|
||||
onNodeActivated: _reconstruction.setActiveNodeOfType(node)
|
||||
onNodeActivated: _reconstruction.setActiveNode(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -703,7 +703,6 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
GraphEditor {
|
||||
id: graphEditor
|
||||
|
||||
|
@ -713,7 +712,7 @@ ApplicationWindow {
|
|||
readOnly: graphLocked
|
||||
|
||||
onNodeDoubleClicked: {
|
||||
_reconstruction.setActiveNodeOfType(node);
|
||||
_reconstruction.setActiveNode(node);
|
||||
|
||||
let viewable = false;
|
||||
for(var i=0; i < node.attributes.count; ++i)
|
||||
|
|
|
@ -8,6 +8,7 @@ from PySide2.QtCore import QObject, Slot, Property, Signal, QUrl, QSizeF
|
|||
from PySide2.QtGui import QMatrix4x4, QMatrix3x3, QQuaternion, QVector3D, QVector2D
|
||||
|
||||
import meshroom.core
|
||||
import meshroom.common
|
||||
from meshroom import multiview
|
||||
from meshroom.common.qt import QObjectListModel
|
||||
from meshroom.core import Version
|
||||
|
@ -196,6 +197,7 @@ class ViewpointWrapper(QObject):
|
|||
self._reconstructed = False
|
||||
# PrepareDenseScene
|
||||
self._undistortedImagePath = ''
|
||||
self._activeNode_PrepareDenseScene = self._reconstruction.activeNodes.get("PrepareDenseScene")
|
||||
|
||||
# update internally cached variables
|
||||
self._updateInitialParams()
|
||||
|
@ -205,7 +207,7 @@ class ViewpointWrapper(QObject):
|
|||
# trigger internal members updates when reconstruction members changes
|
||||
self._reconstruction.cameraInitChanged.connect(self._updateInitialParams)
|
||||
self._reconstruction.sfmReportChanged.connect(self._updateSfMParams)
|
||||
self._reconstruction.prepareDenseSceneChanged.connect(self._updateDenseSceneParams)
|
||||
self._activeNode_PrepareDenseScene.nodeChanged.connect(self._updateDenseSceneParams)
|
||||
|
||||
def _updateInitialParams(self):
|
||||
""" Update internal members depending on CameraInit. """
|
||||
|
@ -235,11 +237,11 @@ class ViewpointWrapper(QObject):
|
|||
def _updateDenseSceneParams(self):
|
||||
""" Update internal members depending on PrepareDenseScene. """
|
||||
# undistorted image path
|
||||
if not self._reconstruction.prepareDenseScene:
|
||||
if not self._activeNode_PrepareDenseScene.node:
|
||||
self._undistortedImagePath = ''
|
||||
else:
|
||||
filename = "{}.{}".format(self._viewpoint.viewId.value, self._reconstruction.prepareDenseScene.outputFileType.value)
|
||||
self._undistortedImagePath = os.path.join(self._reconstruction.prepareDenseScene.output.value, filename)
|
||||
filename = "{}.{}".format(self._viewpoint.viewId.value, self._activeNode_PrepareDenseScene.node.outputFileType.value)
|
||||
self._undistortedImagePath = os.path.join(self._activeNode_PrepareDenseScene.node.output.value, filename)
|
||||
self.denseSceneParamsChanged.emit()
|
||||
|
||||
@Property(type=QObject, constant=True)
|
||||
|
@ -388,37 +390,50 @@ def parseSfMJsonFile(sfmJsonFile):
|
|||
return views, poses, intrinsics
|
||||
|
||||
|
||||
sfmHolderNodeTypes = ["StructureFromMotion", "GlobalSfM", "PanoramaEstimation", "SfMTransfer", "SfMTransform", "SfMAlignment"]
|
||||
class ActiveNode(QObject):
|
||||
"""
|
||||
Hold one active node for a given NodeType.
|
||||
"""
|
||||
def __init__(self, nodeType, parent=None):
|
||||
super(ActiveNode, self).__init__(parent)
|
||||
self.nodeType = nodeType
|
||||
self._node = None
|
||||
|
||||
nodeChanged = Signal()
|
||||
node = makeProperty(QObject, "_node", nodeChanged, resetOnDestroy=True)
|
||||
|
||||
|
||||
class Reconstruction(UIGraph):
|
||||
"""
|
||||
Specialization of a UIGraph designed to manage a 3D reconstruction.
|
||||
"""
|
||||
activeNodeCategories = {
|
||||
"sfm": ["StructureFromMotion", "GlobalSfM", "PanoramaEstimation", "SfMTransfer", "SfMTransform",
|
||||
"SfMAlignment"],
|
||||
"undistort": ["PrepareDenseScene", "PanoramaWarping"],
|
||||
"allDepthMap": ["DepthMap", "DepthMapFilter"],
|
||||
}
|
||||
|
||||
def __init__(self, defaultPipeline='', parent=None):
|
||||
super(Reconstruction, self).__init__(parent)
|
||||
|
||||
# initialize member variables for key steps of the 3D reconstruction pipeline
|
||||
|
||||
self._activeNodes = meshroom.common.DictModel(keyAttrName="nodeType")
|
||||
self.initActiveNodes()
|
||||
|
||||
# - CameraInit
|
||||
self._cameraInit = None # current CameraInit node
|
||||
self._cameraInits = QObjectListModel(parent=self) # all CameraInit nodes
|
||||
self._buildingIntrinsics = False
|
||||
self.intrinsicsBuilt.connect(self.onIntrinsicsAvailable)
|
||||
|
||||
self._hdrCameraInit = None
|
||||
self.cameraInitChanged.connect(self.onCameraInitChanged)
|
||||
|
||||
self._tempCameraInit = None
|
||||
|
||||
self.importImagesFailed.connect(self.onImportImagesFailed)
|
||||
|
||||
# - Feature Extraction
|
||||
self._featureExtraction = None
|
||||
self.cameraInitChanged.connect(self.updateFeatureExtraction)
|
||||
|
||||
# - Feature Matching
|
||||
self._featureMatching = None
|
||||
self.cameraInitChanged.connect(self.updateFeatureMatching)
|
||||
|
||||
# - SfM
|
||||
self._sfm = None
|
||||
self._views = None
|
||||
|
@ -428,28 +443,6 @@ class Reconstruction(UIGraph):
|
|||
self._selectedViewpoint = None
|
||||
self._liveSfmManager = LiveSfmManager(self)
|
||||
|
||||
# - Prepare Dense Scene (undistorted images)
|
||||
self._prepareDenseScene = None
|
||||
|
||||
# - Depth Map
|
||||
self._depthMap = None
|
||||
self.cameraInitChanged.connect(self.updateDepthMapNode)
|
||||
|
||||
# - Texturing
|
||||
self._texturing = None
|
||||
|
||||
# - LDR2HDR
|
||||
self._ldr2hdr = None
|
||||
self.cameraInitChanged.connect(self.updateLdr2hdrNode)
|
||||
|
||||
# - PanoramaInit
|
||||
self._panoramaInit = None
|
||||
self.cameraInitChanged.connect(self.updatePanoramaInitNode)
|
||||
|
||||
# - PanoramaInit
|
||||
self._sfmTransform = None
|
||||
self.cameraInitChanged.connect(self.updateSfMTransformNode)
|
||||
|
||||
# react to internal graph changes to update those variables
|
||||
self.graphChanged.connect(self.onGraphChanged)
|
||||
|
||||
|
@ -458,6 +451,18 @@ class Reconstruction(UIGraph):
|
|||
def setDefaultPipeline(self, defaultPipeline):
|
||||
self._defaultPipeline = defaultPipeline
|
||||
|
||||
def initActiveNodes(self):
|
||||
# Create all possible entries
|
||||
for category, _ in self.activeNodeCategories.iteritems():
|
||||
self._activeNodes.add(ActiveNode(category, self))
|
||||
for nodeType, _ in meshroom.core.nodesDesc.iteritems():
|
||||
self._activeNodes.add(ActiveNode(nodeType, self))
|
||||
|
||||
def onCameraInitChanged(self):
|
||||
# Update active nodes when CameraInit changes
|
||||
nodes = self._graph.nodesFromNode(self._cameraInit)[0]
|
||||
self.setActiveNodes(nodes)
|
||||
|
||||
@Slot()
|
||||
@Slot(str)
|
||||
def new(self, pipeline=None):
|
||||
|
@ -528,16 +533,8 @@ class Reconstruction(UIGraph):
|
|||
""" React to the change of the internal graph. """
|
||||
self._liveSfmManager.reset()
|
||||
self.selectedViewId = "-1"
|
||||
self.featureExtraction = None
|
||||
self.featureMatching = None
|
||||
self.sfm = None
|
||||
self.prepareDenseScene = None
|
||||
self.depthMap = None
|
||||
self.texturing = None
|
||||
self.ldr2hdr = None
|
||||
self.hdrCameraInit = None
|
||||
self.panoramaInit = None
|
||||
self.sfmTransform = None
|
||||
self.tempCameraInit = None
|
||||
self.updateCameraInits()
|
||||
if not self._graph:
|
||||
return
|
||||
|
@ -578,35 +575,23 @@ class Reconstruction(UIGraph):
|
|||
camInit = self._cameraInits[idx] if self._cameraInits else None
|
||||
self.cameraInit = camInit
|
||||
|
||||
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 updateFeatureMatching(self):
|
||||
""" Set the current FeatureMatching node based on the current CameraInit node. """
|
||||
self.featureMatching = self.lastNodeOfType(['FeatureMatching'], 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 updateLdr2hdrNode(self):
|
||||
""" Set the current LDR2HDR node based on the current CameraInit node. """
|
||||
self.ldr2hdr = self.lastNodeOfType(['LdrToHdrMerge'], self.cameraInit) if self.cameraInit else None
|
||||
|
||||
@Slot()
|
||||
def setupLDRToHDRCameraInit(self):
|
||||
if not self.ldr2hdr:
|
||||
self.hdrCameraInit = Node("CameraInit")
|
||||
def clearTempCameraInit(self):
|
||||
self.tempCameraInit = None
|
||||
|
||||
@Slot(QObject, str)
|
||||
def setupTempCameraInit(self, node, attrName):
|
||||
if not node or not attrName:
|
||||
self.tempCameraInit = None
|
||||
return
|
||||
sfmFile = self.ldr2hdr.attribute("outSfMDataFilename").value
|
||||
sfmFile = node.attribute(attrName).value
|
||||
if not sfmFile or not os.path.isfile(sfmFile):
|
||||
self.hdrCameraInit = Node("CameraInit")
|
||||
self.tempCameraInit = None
|
||||
return
|
||||
nodeDesc = meshroom.core.nodesDesc["CameraInit"]()
|
||||
views, intrinsics = nodeDesc.readSfMData(sfmFile)
|
||||
tmpCameraInit = Node("CameraInit", viewpoints=views, intrinsics=intrinsics)
|
||||
self.hdrCameraInit = tmpCameraInit
|
||||
self.tempCameraInit = tmpCameraInit
|
||||
|
||||
@Slot(QObject, result=QVector3D)
|
||||
def getAutoFisheyeCircle(self, panoramaInit):
|
||||
|
@ -633,17 +618,9 @@ class Reconstruction(UIGraph):
|
|||
float(intrinsic.get("fisheyeCircleRadius", 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
|
||||
|
||||
def updateSfMTransformNode(self):
|
||||
""" Set the current SfMTransform node based on the current CameraInit node. """
|
||||
self.sfmTransform = self.lastNodeOfType(["SfMTransform"], self.cameraInit) if self.cameraInit else None
|
||||
|
||||
def lastSfmNode(self):
|
||||
""" Retrieve the last SfM node from the initial CameraInit node. """
|
||||
return self.lastNodeOfType(sfmHolderNodeTypes, self._cameraInit, Status.SUCCESS)
|
||||
return self.lastNodeOfType(self.activeNodeCategories['sfm'], self._cameraInit, Status.SUCCESS)
|
||||
|
||||
def lastNodeOfType(self, nodeTypes, startNode, preferredStatus=None):
|
||||
"""
|
||||
|
@ -938,10 +915,11 @@ class Reconstruction(UIGraph):
|
|||
self._buildingIntrinsics = value
|
||||
self.buildingIntrinsicsChanged.emit()
|
||||
|
||||
activeNodes = makeProperty(QObject, "_activeNodes", resetOnDestroy=True)
|
||||
cameraInitChanged = Signal()
|
||||
cameraInit = makeProperty(QObject, "_cameraInit", cameraInitChanged, resetOnDestroy=True)
|
||||
hdrCameraInitChanged = Signal()
|
||||
hdrCameraInit = makeProperty(QObject, "_hdrCameraInit", hdrCameraInitChanged, resetOnDestroy=True)
|
||||
tempCameraInitChanged = Signal()
|
||||
tempCameraInit = makeProperty(QObject, "_tempCameraInit", tempCameraInitChanged, resetOnDestroy=True)
|
||||
cameraInitIndex = Property(int, getCameraInitIndex, setCameraInitIndex, notify=cameraInitChanged)
|
||||
viewpoints = Property(QObject, getViewpoints, notify=cameraInitChanged)
|
||||
cameraInits = Property(QObject, lambda self: self._cameraInits, constant=True)
|
||||
|
@ -952,27 +930,30 @@ class Reconstruction(UIGraph):
|
|||
liveSfmManager = Property(QObject, lambda self: self._liveSfmManager, constant=True)
|
||||
|
||||
@Slot(QObject)
|
||||
def setActiveNodeOfType(self, node):
|
||||
def setActiveNode(self, node):
|
||||
""" Set node as the active node of its type. """
|
||||
if node.nodeType in sfmHolderNodeTypes:
|
||||
self.sfm = node
|
||||
for category, nodeTypes in self.activeNodeCategories.iteritems():
|
||||
if node.nodeType in nodeTypes:
|
||||
self.activeNodes.get(category).node = node
|
||||
if category == 'sfm':
|
||||
self.setSfm(node)
|
||||
self.activeNodes.get(node.nodeType).node = node
|
||||
|
||||
if node.nodeType == "FeatureExtraction":
|
||||
self.featureExtraction = node
|
||||
elif node.nodeType == "FeatureMatching":
|
||||
self.featureMatching = node
|
||||
elif node.nodeType == "CameraInit":
|
||||
self.cameraInit = node
|
||||
elif node.nodeType == "PrepareDenseScene":
|
||||
self.prepareDenseScene = node
|
||||
elif node.nodeType in ("DepthMap", "DepthMapFilter"):
|
||||
self.depthMap = node
|
||||
elif node.nodeType == "LdrToHdrMerge":
|
||||
self.ldr2hdr = node
|
||||
elif node.nodeType == "PanoramaInit":
|
||||
self.panoramaInit = node
|
||||
elif node.nodeType == "SfMTransform":
|
||||
self.sfmTransform = node
|
||||
@Slot(QObject)
|
||||
def setActiveNodes(self, nodes):
|
||||
""" Set node as the active node of its type. """
|
||||
# Setup the active node per category only once, on the last one
|
||||
nodesByCategory = {}
|
||||
for node in nodes:
|
||||
for category, nodeTypes in self.activeNodeCategories.iteritems():
|
||||
if node.nodeType in nodeTypes:
|
||||
nodesByCategory[category] = node
|
||||
for category, node in nodesByCategory.iteritems():
|
||||
self.activeNodes.get(category).node = node
|
||||
if category == 'sfm':
|
||||
self.setSfm(node)
|
||||
for node in nodes:
|
||||
self.activeNodes.get(node.nodeType).node = node
|
||||
|
||||
def updateSfMResults(self):
|
||||
"""
|
||||
|
@ -1021,9 +1002,6 @@ class Reconstruction(UIGraph):
|
|||
self._sfm.destroyed.disconnect(self._unsetSfm)
|
||||
self._setSfm(node)
|
||||
|
||||
self.texturing = self.lastNodeOfType(["Texturing"], self._sfm, Status.SUCCESS)
|
||||
self.prepareDenseScene = self.lastNodeOfType(["PrepareDenseScene"], self._sfm, Status.SUCCESS)
|
||||
|
||||
@Slot(QObject, result=bool)
|
||||
def isInViews(self, viewpoint):
|
||||
if not viewpoint:
|
||||
|
@ -1129,35 +1107,11 @@ class Reconstruction(UIGraph):
|
|||
sfmChanged = Signal()
|
||||
sfm = Property(QObject, getSfm, setSfm, notify=sfmChanged)
|
||||
|
||||
featureExtractionChanged = Signal()
|
||||
featureExtraction = makeProperty(QObject, "_featureExtraction", featureExtractionChanged, resetOnDestroy=True)
|
||||
|
||||
featureMatchingChanged = Signal()
|
||||
featureMatching = makeProperty(QObject, "_featureMatching", featureMatchingChanged, resetOnDestroy=True)
|
||||
|
||||
sfmReportChanged = Signal()
|
||||
# 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, resetOnDestroy=True)
|
||||
|
||||
ldr2hdrChanged = Signal()
|
||||
ldr2hdr = makeProperty(QObject, "_ldr2hdr", notify=ldr2hdrChanged, resetOnDestroy=True)
|
||||
|
||||
panoramaInitChanged = Signal()
|
||||
panoramaInit = makeProperty(QObject, "_panoramaInit", notify=panoramaInitChanged, resetOnDestroy=True)
|
||||
|
||||
sfmTransformChanged = Signal()
|
||||
sfmTransform = makeProperty(QObject, "_sfmTransform", notify=sfmTransformChanged, resetOnDestroy=True)
|
||||
|
||||
nbCameras = Property(int, reconstructedCamerasCount, notify=sfmReportChanged)
|
||||
|
||||
# Signals to propagate high-level messages
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue