mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-06-03 19:31:58 +02:00
[ui] handle multiple CameraInit nodes in a Reconstruction
* [ui] ImageGallery: add control to navigate image groups * [graph] expose Node.attribute as a Slot
This commit is contained in:
parent
1f0ed1f2c9
commit
273cfd9f0e
5 changed files with 220 additions and 122 deletions
|
@ -5,6 +5,7 @@ from threading import Thread
|
|||
from PySide2.QtCore import QObject, Slot, Property, Signal
|
||||
|
||||
from meshroom import multiview
|
||||
from meshroom.common.qt import QObjectListModel
|
||||
from meshroom.core import graph
|
||||
from meshroom.ui.graph import UIGraph
|
||||
|
||||
|
@ -19,7 +20,9 @@ class Reconstruction(UIGraph):
|
|||
def __init__(self, graphFilepath='', parent=None):
|
||||
super(Reconstruction, self).__init__(graphFilepath, parent)
|
||||
self._buildIntrinsicsThread = None
|
||||
self._buildingIntrinsics = False
|
||||
self._cameraInit = None
|
||||
self._cameraInits = QObjectListModel(parent=self)
|
||||
self._endChunk = None
|
||||
self._meshFile = ''
|
||||
self.intrinsicsBuilt.connect(self.onIntrinsicsAvailable)
|
||||
|
@ -38,7 +41,7 @@ class Reconstruction(UIGraph):
|
|||
""" React to the change of the internal graph. """
|
||||
self._endChunk = None
|
||||
self.setMeshFile('')
|
||||
self.updateCameraInit()
|
||||
self.updateCameraInits()
|
||||
if not self._graph:
|
||||
return
|
||||
|
||||
|
@ -51,7 +54,7 @@ class Reconstruction(UIGraph):
|
|||
except KeyError:
|
||||
self._endChunk = None
|
||||
# TODO: listen specifically for cameraInit creation/deletion
|
||||
self._graph.nodes.countChanged.connect(self.updateCameraInit)
|
||||
self._graph.nodes.countChanged.connect(self.updateCameraInits)
|
||||
|
||||
@staticmethod
|
||||
def runAsync(func, args=(), kwargs=None):
|
||||
|
@ -64,12 +67,11 @@ class Reconstruction(UIGraph):
|
|||
# TODO: handle multiple Viewpoints models
|
||||
return self._cameraInit.viewpoints.value if self._cameraInit else None
|
||||
|
||||
def updateCameraInit(self):
|
||||
""" Update internal CameraInit node (Viewpoints model owner) based on graph content. """
|
||||
# TODO: handle multiple CameraInit nodes
|
||||
if self._cameraInit in self._graph.nodes:
|
||||
def updateCameraInits(self):
|
||||
cameraInits = self._graph.nodesByType("CameraInit", sortedByIndex=True)
|
||||
if set(self._cameraInits.objectList()) == set(cameraInits):
|
||||
return
|
||||
cameraInits = self._graph.findNodeCandidates("CameraInit")
|
||||
self._cameraInits.setObjectList(cameraInits)
|
||||
self.setCameraInit(cameraInits[0] if cameraInits else None)
|
||||
|
||||
def setCameraInit(self, cameraInit):
|
||||
|
@ -78,7 +80,15 @@ class Reconstruction(UIGraph):
|
|||
if self._cameraInit == cameraInit:
|
||||
return
|
||||
self._cameraInit = cameraInit
|
||||
self.viewpointsChanged.emit()
|
||||
self.cameraInitChanged.emit()
|
||||
|
||||
def getCameraInitIndex(self):
|
||||
if not self._cameraInit:
|
||||
return -1
|
||||
return self._cameraInits.indexOf(self._cameraInit)
|
||||
|
||||
def setCameraInitIndex(self, idx):
|
||||
self.setCameraInit(self._cameraInits[idx])
|
||||
|
||||
def updateMeshFile(self):
|
||||
if self._endChunk and self._endChunk.status.status == graph.Status.SUCCESS:
|
||||
|
@ -92,22 +102,22 @@ class Reconstruction(UIGraph):
|
|||
self._meshFile = mf
|
||||
self.meshFileChanged.emit()
|
||||
|
||||
@Slot(QObject)
|
||||
def handleFilesDrop(self, drop):
|
||||
@Slot(QObject, graph.Node)
|
||||
def handleFilesDrop(self, drop, cameraInit):
|
||||
""" Handle drop events aiming to add images to the Reconstruction.
|
||||
Fetching urls from dropEvent is generally expensive in QML/JS (bug ?).
|
||||
This method allows to reduce process time by doing it on Python side.
|
||||
"""
|
||||
self.importImagesFromUrls(drop.property("urls"))
|
||||
self.importImages(self.getImageFilesFromDrop(drop), cameraInit)
|
||||
|
||||
@staticmethod
|
||||
def isImageFile(filepath):
|
||||
""" Return whether filepath is a path to an image file supported by Meshroom. """
|
||||
return os.path.splitext(filepath)[1].lower() in Reconstruction.imageExtensions
|
||||
|
||||
@Slot(QObject)
|
||||
def importImagesFromUrls(self, urls):
|
||||
""" Add the given list of images (as QUrl) to the Reconstruction. """
|
||||
@staticmethod
|
||||
def getImageFilesFromDrop(drop):
|
||||
urls = drop.property("urls")
|
||||
# Build the list of images paths
|
||||
images = []
|
||||
for url in urls:
|
||||
|
@ -117,43 +127,52 @@ class Reconstruction(UIGraph):
|
|||
else:
|
||||
files = [localFile]
|
||||
images.extend([f for f in files if Reconstruction.isImageFile(f)])
|
||||
if not images:
|
||||
return
|
||||
# Start the process of updating views and intrinsics
|
||||
self._buildIntrinsicsThread = self.runAsync(self.buildIntrinsics, args=(images,))
|
||||
return images
|
||||
|
||||
def buildIntrinsics(self, additionalViews):
|
||||
def importImages(self, images, cameraInit):
|
||||
""" Add the given list of images to the Reconstruction. """
|
||||
# Start the process of updating views and intrinsics
|
||||
self._buildIntrinsicsThread = self.runAsync(self.buildIntrinsics, args=(cameraInit, images,))
|
||||
|
||||
def buildIntrinsics(self, cameraInit, additionalViews):
|
||||
"""
|
||||
Build up-to-date intrinsics and views based on already loaded + additional images.
|
||||
Does not modify the graph, can be called outside the main thread.
|
||||
Emits intrinsicBuilt(views, intrinsics) when done.
|
||||
"""
|
||||
try:
|
||||
self.buildingIntrinsicsChanged.emit()
|
||||
self.setBuildingIntrinsics(True)
|
||||
# Retrieve the list of updated viewpoints and intrinsics
|
||||
views, intrinsics = self._cameraInit.nodeDesc.buildIntrinsics(self._cameraInit, additionalViews)
|
||||
self.intrinsicsBuilt.emit(views, intrinsics)
|
||||
views, intrinsics = cameraInit.nodeDesc.buildIntrinsics(cameraInit, additionalViews)
|
||||
self.intrinsicsBuilt.emit(cameraInit, views, intrinsics)
|
||||
return views, intrinsics
|
||||
except Exception as e:
|
||||
logging.error("Error while building intrinsics : {}".format(e))
|
||||
except Exception:
|
||||
import traceback
|
||||
logging.error("Error while building intrinsics : {}".format(traceback.format_exc()))
|
||||
finally:
|
||||
self.buildingIntrinsicsChanged.emit()
|
||||
self.setBuildingIntrinsics(False)
|
||||
|
||||
def onIntrinsicsAvailable(self, views, intrinsics):
|
||||
def onIntrinsicsAvailable(self, cameraInit, views, intrinsics):
|
||||
""" Update CameraInit with given views and intrinsics. """
|
||||
with self.groupedGraphModification("Add Images"):
|
||||
self.setAttribute(self._cameraInit.viewpoints, views)
|
||||
self.setAttribute(self._cameraInit.intrinsics, intrinsics)
|
||||
self.setAttribute(cameraInit.viewpoints, views)
|
||||
self.setAttribute(cameraInit.intrinsics, intrinsics)
|
||||
self.setCameraInit(cameraInit)
|
||||
|
||||
def isBuildingIntrinsics(self):
|
||||
""" Whether intrinsics are being built """
|
||||
return self._buildIntrinsicsThread and self._buildIntrinsicsThread.isAlive()
|
||||
def setBuildingIntrinsics(self, value):
|
||||
if self._buildingIntrinsics == value:
|
||||
return
|
||||
self._buildingIntrinsics = value
|
||||
self.buildingIntrinsicsChanged.emit()
|
||||
|
||||
viewpointsChanged = Signal()
|
||||
viewpoints = Property(QObject, getViewpoints, notify=viewpointsChanged)
|
||||
intrinsicsBuilt = Signal(list, list)
|
||||
cameraInitChanged = Signal()
|
||||
cameraInit = Property(QObject, lambda self: self._cameraInit, notify=cameraInitChanged)
|
||||
cameraInitIndex = Property(int, getCameraInitIndex, setCameraInitIndex, notify=cameraInitChanged)
|
||||
viewpoints = Property(QObject, getViewpoints, notify=cameraInitChanged)
|
||||
cameraInits = Property(QObject, lambda self: self._cameraInits, constant=True)
|
||||
intrinsicsBuilt = Signal(QObject, list, list)
|
||||
buildingIntrinsicsChanged = Signal()
|
||||
buildingIntrinsics = Property(bool, isBuildingIntrinsics, notify=buildingIntrinsicsChanged)
|
||||
buildingIntrinsics = Property(bool, lambda self: self._buildingIntrinsics, notify=buildingIntrinsicsChanged)
|
||||
meshFileChanged = Signal()
|
||||
meshFile = Property(str, lambda self: self._meshFile, notify=meshFileChanged)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue