mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-31 23:38:48 +02:00
[ui] Load image sequence from node output in SequencePlayer
This commit is contained in:
parent
3eb90322a8
commit
b0063b0dde
3 changed files with 81 additions and 30 deletions
|
@ -4,6 +4,7 @@ from PySide2.QtCore import QUrl, QFileInfo
|
||||||
from PySide2.QtCore import QObject, Slot
|
from PySide2.QtCore import QObject, Slot
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
|
||||||
class FilepathHelper(QObject):
|
class FilepathHelper(QObject):
|
||||||
|
@ -103,19 +104,44 @@ class FilepathHelper(QObject):
|
||||||
def resolve(self, path, vp):
|
def resolve(self, path, vp):
|
||||||
# Resolve dynamic path that depends on viewpoint
|
# Resolve dynamic path that depends on viewpoint
|
||||||
|
|
||||||
vpPath = vp.childAttribute("path").value
|
replacements = {}
|
||||||
replacements = {
|
if vp == None:
|
||||||
"<VIEW_ID>": str(vp.childAttribute("viewId").value),
|
replacements = FilepathHelper.getFilenamesFromFolder(FilepathHelper, FilepathHelper.dirname(FilepathHelper, path), FilepathHelper.extension(FilepathHelper, path))
|
||||||
"<INTRINSIC_ID>": str(vp.childAttribute("intrinsicId").value),
|
resolved = [path for i in range(len(replacements))]
|
||||||
"<POSE_ID>": str(vp.childAttribute("poseId").value),
|
for key in replacements:
|
||||||
"<PATH>": vpPath,
|
for i in range(len(resolved)):
|
||||||
"<FILENAME>": FilepathHelper.basename(FilepathHelper, vpPath),
|
resolved[i] = resolved[i].replace("<FRAMEID>", replacements[i])
|
||||||
"<FILESTEM>": FilepathHelper.removeExtension(FilepathHelper, FilepathHelper.basename(FilepathHelper, vpPath)),
|
return resolved
|
||||||
"<EXTENSION>": FilepathHelper.extension(FilepathHelper, vpPath),
|
else:
|
||||||
}
|
|
||||||
|
vpPath = vp.childAttribute("path").value
|
||||||
|
filename = FilepathHelper.basename(FilepathHelper, vpPath)
|
||||||
|
replacements = {
|
||||||
|
"<VIEW_ID>": str(vp.childAttribute("viewId").value),
|
||||||
|
"<INTRINSIC_ID>": str(vp.childAttribute("intrinsicId").value),
|
||||||
|
"<POSE_ID>": str(vp.childAttribute("poseId").value),
|
||||||
|
"<PATH>": vpPath,
|
||||||
|
"<FILENAME>": filename,
|
||||||
|
"<FILESTEM>": FilepathHelper.removeExtension(FilepathHelper, filename),
|
||||||
|
"<EXTENSION>": FilepathHelper.extension(FilepathHelper, filename),
|
||||||
|
}
|
||||||
|
|
||||||
resolved = path
|
resolved = path
|
||||||
for key in replacements:
|
for key in replacements:
|
||||||
resolved = resolved.replace(key, replacements[key])
|
resolved = resolved.replace(key, replacements[key])
|
||||||
|
|
||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
|
@Slot(str, result="QVariantList")
|
||||||
|
@Slot(str, str, result="QVariantList")
|
||||||
|
def getFilenamesFromFolder(self, folderPath: str, extension: str = None):
|
||||||
|
"""
|
||||||
|
Get all filenames from a folder with a specific extension.
|
||||||
|
|
||||||
|
:param folderPath: Path to the folder.
|
||||||
|
:param extension: Extension of the files to get.
|
||||||
|
:return: List of filenames.
|
||||||
|
"""
|
||||||
|
if extension is None:
|
||||||
|
extension = ".*"
|
||||||
|
return [self.removeExtension(FilepathHelper, self.basename(FilepathHelper, f)) for f in glob.glob(os.path.join(folderPath, f"*{extension}")) if os.path.isfile(f)]
|
||||||
|
|
|
@ -27,6 +27,7 @@ FloatingPane {
|
||||||
readonly property alias syncFeaturesSelected: m.syncFeaturesSelected
|
readonly property alias syncFeaturesSelected: m.syncFeaturesSelected
|
||||||
property bool loading: fetchButton.checked || m.playing
|
property bool loading: fetchButton.checked || m.playing
|
||||||
property alias settings_SequencePlayer: settings_SequencePlayer
|
property alias settings_SequencePlayer: settings_SequencePlayer
|
||||||
|
property alias frameId: m.frame
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
id: settings_SequencePlayer
|
id: settings_SequencePlayer
|
||||||
|
@ -94,7 +95,7 @@ FloatingPane {
|
||||||
id: timer
|
id: timer
|
||||||
|
|
||||||
repeat: true
|
repeat: true
|
||||||
running: m.playing
|
running: m.playing && root.visible
|
||||||
interval: 1000 / m.fps
|
interval: 1000 / m.fps
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
|
|
@ -30,6 +30,8 @@ FocusScope {
|
||||||
property bool enableSequencePlayer: enableSequencePlayerAction.checked
|
property bool enableSequencePlayer: enableSequencePlayerAction.checked
|
||||||
|
|
||||||
readonly property alias sync3DSelected: sequencePlayer.sync3DSelected
|
readonly property alias sync3DSelected: sequencePlayer.sync3DSelected
|
||||||
|
property var sequence: []
|
||||||
|
property int currentFrame: sequencePlayer.frameId
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: m
|
id: m
|
||||||
|
@ -181,7 +183,7 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
// node must have at least one output attribute with the image semantic
|
// node must have at least one output attribute with the image semantic
|
||||||
if (!node.hasImageOutput) {
|
if (!node.hasImageOutput && !node.hasSequenceOutput) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +225,6 @@ FocusScope {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getImageFile() {
|
function getImageFile() {
|
||||||
// Entry point for getting the image file URL
|
// Entry point for getting the image file URL
|
||||||
|
|
||||||
|
@ -237,6 +238,11 @@ FocusScope {
|
||||||
return Filepath.stringToUrl(path)
|
return Filepath.stringToUrl(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_reconstruction && displayedNode && displayedNode.hasSequenceOutput) {
|
||||||
|
var path = sequence[currentFrame]
|
||||||
|
return Filepath.stringToUrl(path)
|
||||||
|
}
|
||||||
|
|
||||||
if (_reconstruction) {
|
if (_reconstruction) {
|
||||||
let vp = getViewpoint(_reconstruction.pickedViewId)
|
let vp = getViewpoint(_reconstruction.pickedViewId)
|
||||||
let attr = getAttributeByName(displayedNode, outputAttribute.name)
|
let attr = getAttributeByName(displayedNode, outputAttribute.name)
|
||||||
|
@ -253,17 +259,24 @@ FocusScope {
|
||||||
// ordered by path
|
// ordered by path
|
||||||
|
|
||||||
let objs = []
|
let objs = []
|
||||||
for (let i = 0; i < _reconstruction.viewpoints.count; i++) {
|
|
||||||
objs.push(_reconstruction.viewpoints.at(i))
|
|
||||||
}
|
|
||||||
objs.sort((a, b) => { return a.childAttribute("path").value < b.childAttribute("path").value ? -1 : 1; })
|
|
||||||
|
|
||||||
let seq = [];
|
if (displayedNode && displayedNode.hasSequenceOutput) {
|
||||||
for (let i = 0; i < objs.length; i++) {
|
objs = Filepath.resolve(path_template, null)
|
||||||
seq.push(Filepath.resolve(path_template, objs[i]))
|
//order by path
|
||||||
}
|
objs.sort()
|
||||||
|
return objs
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < _reconstruction.viewpoints.count; i++) {
|
||||||
|
objs.push(_reconstruction.viewpoints.at(i))
|
||||||
|
}
|
||||||
|
objs.sort((a, b) => { return a.childAttribute("path").value < b.childAttribute("path").value ? -1 : 1; })
|
||||||
|
let seq = [];
|
||||||
|
for (let i = 0; i < objs.length; i++) {
|
||||||
|
seq.push(Filepath.resolve(path_template, objs[i]))
|
||||||
|
}
|
||||||
|
|
||||||
return seq
|
return seq
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSequence() {
|
function getSequence() {
|
||||||
|
@ -300,12 +313,23 @@ FocusScope {
|
||||||
if (attr.isOutput && attr.desc.semantic === "image" && attr.enabled) {
|
if (attr.isOutput && attr.desc.semantic === "image" && attr.enabled) {
|
||||||
names.push(attr.name)
|
names.push(attr.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attr.isOutput && attr.desc.semantic === "sequence" && attr.enabled) {
|
||||||
|
names.push(attr.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!displayedNode || displayedNode.isComputable) names.push("gallery")
|
|
||||||
outputAttribute.names = names
|
|
||||||
|
|
||||||
root.source = getImageFile()
|
if (!displayedNode || displayedNode.isComputable) names.push("gallery")
|
||||||
|
|
||||||
|
outputAttribute.names = names
|
||||||
|
if (displayedNode && !displayedNode.hasSequenceOutput) {
|
||||||
|
|
||||||
|
root.source = getImageFile()
|
||||||
|
} else {
|
||||||
|
root.sequence = getSequence()
|
||||||
|
enableSequencePlayerAction.checked = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
@ -457,11 +481,11 @@ FocusScope {
|
||||||
'sfmRequired': Qt.binding(function() { return displayLensDistortionViewer.checked ? true : false }),
|
'sfmRequired': Qt.binding(function() { return displayLensDistortionViewer.checked ? true : false }),
|
||||||
'surface.msfmData': Qt.binding(function() { return (msfmDataLoader.status === Loader.Ready && msfmDataLoader.item != null && msfmDataLoader.item.status === 2) ? msfmDataLoader.item : null }),
|
'surface.msfmData': Qt.binding(function() { return (msfmDataLoader.status === Loader.Ready && msfmDataLoader.item != null && msfmDataLoader.item.status === 2) ? msfmDataLoader.item : null }),
|
||||||
'canBeHovered': false,
|
'canBeHovered': false,
|
||||||
'idView': Qt.binding(function() { return (_reconstruction ? _reconstruction.selectedViewId : -1) }),
|
'idView': Qt.binding(function() { return ((root.displayedNode && !root.displayedNode.hasSequenceOutput && _reconstruction) ? _reconstruction.selectedViewId : -1) }),
|
||||||
'cropFisheye': false,
|
'cropFisheye': false,
|
||||||
'sequence': Qt.binding(function() { return ((root.enableSequencePlayer && _reconstruction && _reconstruction.viewpoints.count > 0) ? getSequence() : []) }),
|
'sequence': Qt.binding(function() { return ((root.enableSequencePlayer && (_reconstruction || (root.displayedNode && root.displayedNode.hasSequenceOutput))) ? getSequence() : []) }),
|
||||||
'targetSize': Qt.binding(function() { return floatImageViewerLoader.targetSize }),
|
'targetSize': Qt.binding(function() { return floatImageViewerLoader.targetSize }),
|
||||||
'useSequence': Qt.binding(function() { return root.enableSequencePlayer && !useExternal && _reconstruction }),
|
'useSequence': Qt.binding(function() { return (root.enableSequencePlayer && !useExternal && (_reconstruction || (root.displayedNode && root.displayedNode.hasSequenceOutput))) }),
|
||||||
'fetchingSequence': Qt.binding(function() { return sequencePlayer.loading }),
|
'fetchingSequence': Qt.binding(function() { return sequencePlayer.loading }),
|
||||||
'memoryLimit': Qt.binding(function() { return sequencePlayer.settings_SequencePlayer.maxCacheMemory }),
|
'memoryLimit': Qt.binding(function() { return sequencePlayer.settings_SequencePlayer.maxCacheMemory }),
|
||||||
})
|
})
|
||||||
|
@ -1434,7 +1458,7 @@ FocusScope {
|
||||||
id: sequencePlayer
|
id: sequencePlayer
|
||||||
anchors.margins: 0
|
anchors.margins: 0
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
sortedViewIds: (root.enableSequencePlayer && _reconstruction && _reconstruction.viewpoints.count > 0) ? buildOrderedSequence("<VIEW_ID>") : []
|
sortedViewIds: { return (root.enableSequencePlayer && (root.displayedNode && root.displayedNode.hasSequenceOutput)) ? root.sequence : (_reconstruction && _reconstruction.viewpoints.count > 0) ? buildOrderedSequence("<VIEW_ID>") : [] }
|
||||||
viewer: floatImageViewerLoader.status === Loader.Ready ? floatImageViewerLoader.item : null
|
viewer: floatImageViewerLoader.status === Loader.Ready ? floatImageViewerLoader.item : null
|
||||||
visible: root.enableSequencePlayer
|
visible: root.enableSequencePlayer
|
||||||
enabled: root.enableSequencePlayer
|
enabled: root.enableSequencePlayer
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue