[ui] ImageGallery: allow to visualize the list of HDR images created by LDRToHDR

Conflicts:
	meshroom/ui/reconstruction.py
This commit is contained in:
Fabien Castan 2020-03-05 20:50:36 +01:00
parent 85dd992927
commit 19d2f90554
5 changed files with 85 additions and 12 deletions

View file

@ -606,6 +606,10 @@ class BaseNode(BaseObject):
return False return False
return True return True
@Slot(result=bool)
def isComputed(self):
return self.hasStatus(Status.SUCCESS)
@Slot() @Slot()
def clearData(self): def clearData(self):
""" Delete this Node internal folder. """ Delete this Node internal folder.

View file

@ -160,6 +160,9 @@ class CameraInit(desc.CommandLineNode):
), ),
] ]
def readSfMData(self, sfmFile):
return readSfMData(sfmFile)
def buildIntrinsics(self, node, additionalViews=()): def buildIntrinsics(self, node, additionalViews=()):
""" Build intrinsics from node current views and optional additional views """ Build intrinsics from node current views and optional additional views

View file

@ -16,13 +16,13 @@ Panel {
property variant cameraInits property variant cameraInits
property variant cameraInit property variant cameraInit
property variant hdrCameraInit
readonly property alias currentItem: grid.currentItem readonly property alias currentItem: grid.currentItem
readonly property string currentItemSource: grid.currentItem ? grid.currentItem.source : "" readonly property string currentItemSource: grid.currentItem ? grid.currentItem.source : ""
readonly property var currentItemMetadata: grid.currentItem ? grid.currentItem.metadata : undefined readonly property var currentItemMetadata: grid.currentItem ? grid.currentItem.metadata : undefined
property int defaultCellSize: 160 property int defaultCellSize: 160
property int currentIndex: 0 property int currentIndex: 0
property bool readOnly: false property bool readOnly: false
readonly property variant viewpoints: cameraInit.attribute('viewpoints').value
signal removeImageRequest(var attribute) signal removeImageRequest(var attribute)
signal filesDropped(var drop, var augmentSfm) signal filesDropped(var drop, var augmentSfm)
@ -30,6 +30,17 @@ Panel {
title: "Images" title: "Images"
implicitWidth: (root.defaultCellSize + 2) * 2 implicitWidth: (root.defaultCellSize + 2) * 2
function changeCurrentIndex(newIndex) {
_reconstruction.cameraInitIndex = newIndex
}
QtObject {
id: m
property variant currentCameraInit: displayHDR.checked ? _reconstruction.hdrCameraInit : root.cameraInit
property variant viewpoints: currentCameraInit ? currentCameraInit.attribute('viewpoints').value : undefined
property bool readOnly: root.readOnly || displayHDR.checked
}
headerBar: RowLayout { headerBar: RowLayout {
MaterialToolButton { MaterialToolButton {
text: MaterialIcons.more_vert text: MaterialIcons.more_vert
@ -99,7 +110,7 @@ Panel {
model: SortFilterDelegateModel { model: SortFilterDelegateModel {
id: sortedModel id: sortedModel
model: _reconstruction.viewpoints model: m.viewpoints
sortRole: "path" sortRole: "path"
// TODO: provide filtering on reconstruction status // TODO: provide filtering on reconstruction status
// filterRole: _reconstruction.sfmReport ? "reconstructed" : "" // filterRole: _reconstruction.sfmReport ? "reconstructed" : ""
@ -123,7 +134,7 @@ Panel {
viewpoint: object.value viewpoint: object.value
width: grid.cellWidth width: grid.cellWidth
height: grid.cellHeight height: grid.cellHeight
readOnly: root.readOnly readOnly: m.readOnly
displayViewId: displayViewIdsAction.checked displayViewId: displayViewIdsAction.checked
isCurrentItem: GridView.isCurrentItem isCurrentItem: GridView.isCurrentItem
@ -202,9 +213,9 @@ Panel {
{ {
event.accepted = true event.accepted = true
if(event.key == Qt.Key_Right) if(event.key == Qt.Key_Right)
root.currentIndex = Math.min(root.cameraInits.count - 1, root.currentIndex + 1) root.changeCurrentIndex(Math.min(root.cameraInits.count - 1, root.currentIndex + 1))
else if(event.key == Qt.Key_Left) else if(event.key == Qt.Key_Left)
root.currentIndex = Math.max(0, root.currentIndex - 1) root.changeCurrentIndex(Math.max(0, root.currentIndex - 1))
} }
} }
@ -227,7 +238,7 @@ Panel {
DropArea { DropArea {
id: dropArea id: dropArea
anchors.fill: parent anchors.fill: parent
enabled: !root.readOnly enabled: !m.readOnly
keys: ["text/uri-list"] keys: ["text/uri-list"]
// TODO: onEntered: call specific method to filter files based on extension // TODO: onEntered: call specific method to filter files based on extension
onDropped: { onDropped: {
@ -274,7 +285,7 @@ Panel {
text: "Augment Reconstruction" text: "Augment Reconstruction"
font.bold: true font.bold: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere wrapMode: Text.WrapAtWordBoundaryOrAnywhere
visible: viewpoints.count > 0 visible: m.viewpoints ? m.viewpoints.count > 0 : false
background: Rectangle { background: Rectangle {
color: parent.hovered ? palette.highlight : palette.window color: parent.hovered ? palette.highlight : palette.window
opacity: 0.8 opacity: 0.8
@ -299,14 +310,13 @@ Panel {
enabled: nodesCB.currentIndex > 0 enabled: nodesCB.currentIndex > 0
onClicked: nodesCB.decrementCurrentIndex() onClicked: nodesCB.decrementCurrentIndex()
} }
Label { text: "Group " } Label { id: groupLabel; text: "Group " }
ComboBox { ComboBox {
id: nodesCB id: nodesCB
model: root.cameraInits.count model: root.cameraInits.count
implicitWidth: 40 implicitWidth: 40
currentIndex: root.currentIndex currentIndex: root.currentIndex
onActivated: root.currentIndex = currentIndex onActivated: root.changeCurrentIndex(currentIndex)
} }
Label { text: "/ " + (root.cameraInits.count - 1) } Label { text: "/ " + (root.cameraInits.count - 1) }
ToolButton { ToolButton {
@ -337,6 +347,26 @@ Panel {
} }
} }
MaterialToolButton {
id: displayHDR
font.pointSize: 20
padding: 0
anchors.margins: 0
implicitHeight: 14
ToolTip.text: "Visualize HDR images"
text: MaterialIcons.hdr_on
visible: _reconstruction.ldr2hdr
enabled: visible && _reconstruction.ldr2hdr.isComputed()
onEnabledChanged: {
// Reset the toggle to avoid getting stuck
// with the HDR node checked but disabled.
checked = false;
}
checkable: true
checked: false
onClicked: { _reconstruction.setupLDRToHDRCameraInit(); }
}
Item { Layout.fillHeight: true; Layout.fillWidth: true } Item { Layout.fillHeight: true; Layout.fillWidth: true }
// Thumbnail size icon and slider // Thumbnail size icon and slider

View file

@ -64,9 +64,9 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
readOnly: root.readOnly readOnly: root.readOnly
cameraInits: root.cameraInits cameraInits: root.cameraInits
cameraInit: _reconstruction.cameraInit cameraInit: reconstruction.cameraInit
hdrCameraInit: reconstruction.hdrCameraInit
currentIndex: reconstruction.cameraInitIndex currentIndex: reconstruction.cameraInitIndex
onCurrentIndexChanged: reconstruction.cameraInitIndex = currentIndex
onRemoveImageRequest: reconstruction.removeAttribute(attribute) onRemoveImageRequest: reconstruction.removeAttribute(attribute)
onFilesDropped: reconstruction.handleFilesDrop(drop, augmentSfm ? null : cameraInit) onFilesDropped: reconstruction.handleFilesDrop(drop, augmentSfm ? null : cameraInit)
} }

View file

@ -7,6 +7,7 @@ from threading import Thread
from PySide2.QtCore import QObject, Slot, Property, Signal, QUrl, QSizeF from PySide2.QtCore import QObject, Slot, Property, Signal, QUrl, QSizeF
from PySide2.QtGui import QMatrix4x4, QMatrix3x3, QQuaternion, QVector3D, QVector2D from PySide2.QtGui import QMatrix4x4, QMatrix3x3, QQuaternion, QVector3D, QVector2D
import meshroom.core
from meshroom import multiview from meshroom import multiview
from meshroom.common.qt import QObjectListModel from meshroom.common.qt import QObjectListModel
from meshroom.core import Version from meshroom.core import Version
@ -369,6 +370,8 @@ class Reconstruction(UIGraph):
self._buildingIntrinsics = False self._buildingIntrinsics = False
self.intrinsicsBuilt.connect(self.onIntrinsicsAvailable) self.intrinsicsBuilt.connect(self.onIntrinsicsAvailable)
self._hdrCameraInit = None
self.importImagesFailed.connect(self.onImportImagesFailed) self.importImagesFailed.connect(self.onImportImagesFailed)
# - Feature Extraction # - Feature Extraction
@ -394,6 +397,10 @@ class Reconstruction(UIGraph):
# - Texturing # - Texturing
self._texturing = None self._texturing = None
# - LDR2HDR
self._ldr2hdr = None
self.cameraInitChanged.connect(self.updateLdr2hdrNode)
# react to internal graph changes to update those variables # react to internal graph changes to update those variables
self.graphChanged.connect(self.onGraphChanged) self.graphChanged.connect(self.onGraphChanged)
@ -449,6 +456,8 @@ class Reconstruction(UIGraph):
self.prepareDenseScene = None self.prepareDenseScene = None
self.depthMap = None self.depthMap = None
self.texturing = None self.texturing = None
self.ldr2hdr = None
self.hdrCameraInit = None
self.updateCameraInits() self.updateCameraInits()
if not self._graph: if not self._graph:
return return
@ -478,6 +487,10 @@ class Reconstruction(UIGraph):
def getCameraInitIndex(self): def getCameraInitIndex(self):
if not self._cameraInit: if not self._cameraInit:
# No CameraInit node
return -1
if not self._cameraInit.graph:
# The CameraInit node is a temporary one not attached to a graph
return -1 return -1
return self._cameraInits.indexOf(self._cameraInit) return self._cameraInits.indexOf(self._cameraInit)
@ -493,6 +506,24 @@ class Reconstruction(UIGraph):
""" Set the current FeatureExtraction node based on the current CameraInit node. """ """ Set the current FeatureExtraction node based on the current CameraInit node. """
self.depthMap = self.lastNodeOfType('DepthMapFilter', self.cameraInit) if self.cameraInit else None 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('LDRToHDR', self.cameraInit) if self.cameraInit else None
@Slot()
def setupLDRToHDRCameraInit(self):
if not self.ldr2hdr:
self.hdrCameraInit = Node("CameraInit")
return
sfmFile = self.ldr2hdr.attribute("outSfMDataFilename").value
if not sfmFile or not os.path.isfile(sfmFile):
self.hdrCameraInit = Node("CameraInit")
return
nodeDesc = meshroom.core.nodesDesc["CameraInit"]()
views, intrinsics = nodeDesc.readSfMData(sfmFile)
tmpCameraInit = Node("CameraInit", viewpoints=views, intrinsics=intrinsics)
self.hdrCameraInit = tmpCameraInit
def lastSfmNode(self): def lastSfmNode(self):
""" Retrieve the last SfM node from the initial CameraInit node. """ """ Retrieve the last SfM node from the initial CameraInit node. """
return self.lastNodeOfType("StructureFromMotion", self._cameraInit, Status.SUCCESS) return self.lastNodeOfType("StructureFromMotion", self._cameraInit, Status.SUCCESS)
@ -778,6 +809,8 @@ class Reconstruction(UIGraph):
cameraInitChanged = Signal() cameraInitChanged = Signal()
cameraInit = makeProperty(QObject, "_cameraInit", cameraInitChanged, resetOnDestroy=True) cameraInit = makeProperty(QObject, "_cameraInit", cameraInitChanged, resetOnDestroy=True)
hdrCameraInitChanged = Signal()
hdrCameraInit = makeProperty(QObject, "_hdrCameraInit", hdrCameraInitChanged, resetOnDestroy=True)
cameraInitIndex = Property(int, getCameraInitIndex, setCameraInitIndex, notify=cameraInitChanged) cameraInitIndex = Property(int, getCameraInitIndex, setCameraInitIndex, notify=cameraInitChanged)
viewpoints = Property(QObject, getViewpoints, notify=cameraInitChanged) viewpoints = Property(QObject, getViewpoints, notify=cameraInitChanged)
cameraInits = Property(QObject, lambda self: self._cameraInits, constant=True) cameraInits = Property(QObject, lambda self: self._cameraInits, constant=True)
@ -970,6 +1003,9 @@ class Reconstruction(UIGraph):
texturingChanged = Signal() texturingChanged = Signal()
texturing = makeProperty(QObject, "_texturing", notify=texturingChanged) texturing = makeProperty(QObject, "_texturing", notify=texturingChanged)
ldr2hdrChanged = Signal()
ldr2hdr = makeProperty(QObject, "_ldr2hdr", notify=ldr2hdrChanged, resetOnDestroy=True)
nbCameras = Property(int, reconstructedCamerasCount, notify=sfmReportChanged) nbCameras = Property(int, reconstructedCamerasCount, notify=sfmReportChanged)
# Signals to propagate high-level messages # Signals to propagate high-level messages