mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-15 18:16:57 +02:00
use semantic field in file attributes to specify images and store displayed node in 2D viewer to adapt UI
This commit is contained in:
parent
fc5ab540f5
commit
a574c0fc9b
9 changed files with 139 additions and 83 deletions
|
@ -489,7 +489,6 @@ class Node(object):
|
||||||
parallelization = None
|
parallelization = None
|
||||||
documentation = ''
|
documentation = ''
|
||||||
category = 'Other'
|
category = 'Other'
|
||||||
outputImageTypes = ['image']
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -568,9 +568,6 @@ class BaseNode(BaseObject):
|
||||||
def getAttributes(self):
|
def getAttributes(self):
|
||||||
return self._attributes
|
return self._attributes
|
||||||
|
|
||||||
def getOutputImageTypes(self):
|
|
||||||
return self.nodeDesc.outputImageTypes
|
|
||||||
|
|
||||||
@Slot(str, result=bool)
|
@Slot(str, result=bool)
|
||||||
def hasAttribute(self, name):
|
def hasAttribute(self, name):
|
||||||
return name in self._attributes.keys()
|
return name in self._attributes.keys()
|
||||||
|
@ -1056,7 +1053,6 @@ class BaseNode(BaseObject):
|
||||||
x = Property(float, lambda self: self._position.x, notify=positionChanged)
|
x = Property(float, lambda self: self._position.x, notify=positionChanged)
|
||||||
y = Property(float, lambda self: self._position.y, notify=positionChanged)
|
y = Property(float, lambda self: self._position.y, notify=positionChanged)
|
||||||
attributes = Property(BaseObject, getAttributes, constant=True)
|
attributes = Property(BaseObject, getAttributes, constant=True)
|
||||||
outputImageTypes = Property(Variant, getOutputImageTypes, constant=True)
|
|
||||||
internalFolderChanged = Signal()
|
internalFolderChanged = Signal()
|
||||||
internalFolder = Property(str, internalFolder.fget, notify=internalFolderChanged)
|
internalFolder = Property(str, internalFolder.fget, notify=internalFolderChanged)
|
||||||
depthChanged = Signal()
|
depthChanged = Signal()
|
||||||
|
|
|
@ -293,11 +293,27 @@ Use a downscale factor of one (full-resolution) only if the quality of the input
|
||||||
outputs = [
|
outputs = [
|
||||||
desc.File(
|
desc.File(
|
||||||
name='output',
|
name='output',
|
||||||
label='Output',
|
label='Output Folder',
|
||||||
description='Output folder for generated depth maps.',
|
description='Output folder for generated depth maps.',
|
||||||
value=desc.Node.internalFolder,
|
value=desc.Node.internalFolder,
|
||||||
uid=[],
|
uid=[],
|
||||||
),
|
),
|
||||||
|
desc.File(
|
||||||
|
name='depth',
|
||||||
|
label='Depth Maps',
|
||||||
|
description='Generated depth maps.',
|
||||||
|
semantic='image',
|
||||||
|
value=desc.Node.internalFolder + '<VIEW_ID>_depthMap.exr',
|
||||||
|
uid=[],
|
||||||
|
group='', # do not export on the command line
|
||||||
|
),
|
||||||
|
desc.File(
|
||||||
|
name='sim',
|
||||||
|
label='Sim Maps',
|
||||||
|
description='Generated sim maps.',
|
||||||
|
semantic='image',
|
||||||
|
value=desc.Node.internalFolder + '<VIEW_ID>_simMap.exr',
|
||||||
|
uid=[],
|
||||||
|
group='', # do not export on the command line
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
outputImageTypes = ['depth', 'sim']
|
|
||||||
|
|
|
@ -128,4 +128,22 @@ This allows to filter unstable points before starting the fusion of all depth ma
|
||||||
value=desc.Node.internalFolder,
|
value=desc.Node.internalFolder,
|
||||||
uid=[],
|
uid=[],
|
||||||
),
|
),
|
||||||
|
desc.File(
|
||||||
|
name='depth',
|
||||||
|
label='Depth Maps',
|
||||||
|
description='Filtered depth maps.',
|
||||||
|
semantic='image',
|
||||||
|
value=desc.Node.internalFolder + '<VIEW_ID>_depthMap.exr',
|
||||||
|
uid=[],
|
||||||
|
group='', # do not export on the command line
|
||||||
|
),
|
||||||
|
desc.File(
|
||||||
|
name='sim',
|
||||||
|
label='Sim Maps',
|
||||||
|
description='Filtered sim maps.',
|
||||||
|
semantic='image',
|
||||||
|
value=desc.Node.internalFolder + '<VIEW_ID>_simMap.exr',
|
||||||
|
uid=[],
|
||||||
|
group='', # do not export on the command line
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -338,12 +338,21 @@ Convert or apply filtering to the input images.
|
||||||
value=desc.Node.internalFolder,
|
value=desc.Node.internalFolder,
|
||||||
uid=[],
|
uid=[],
|
||||||
),
|
),
|
||||||
|
#desc.File(
|
||||||
|
# name='outputImages',
|
||||||
|
# label='Output Images',
|
||||||
|
# description='Output Image Files.',
|
||||||
|
# value= outputImagesValueFunct,
|
||||||
|
# group='', # do not export on the command line
|
||||||
|
# uid=[],
|
||||||
|
#),
|
||||||
desc.File(
|
desc.File(
|
||||||
name='outputImages',
|
name='outputImages',
|
||||||
label='Output Images',
|
label='Output Images',
|
||||||
description='Output Image Files.',
|
description='Output Image Files.',
|
||||||
value= outputImagesValueFunct,
|
semantic='image',
|
||||||
|
value=desc.Node.internalFolder + '<VIEW_ID>.exr',
|
||||||
group='', # do not export on the command line
|
group='', # do not export on the command line
|
||||||
uid=[],
|
uid=[]
|
||||||
),
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -342,14 +342,23 @@ Many cameras are contributing to the low frequencies and only the best ones cont
|
||||||
uid=[],
|
uid=[],
|
||||||
group='',
|
group='',
|
||||||
),
|
),
|
||||||
|
#desc.File(
|
||||||
|
# name='outputTextures',
|
||||||
|
# label='Textures',
|
||||||
|
# description='Output Texture files.',
|
||||||
|
# value= lambda attr: desc.Node.internalFolder + 'texture_*.' + attr.node.colorMapping.colorMappingFileType.value if attr.node.colorMapping.enable.value else '',
|
||||||
|
# uid=[],
|
||||||
|
# group='',
|
||||||
|
# ),
|
||||||
desc.File(
|
desc.File(
|
||||||
name='outputTextures',
|
name='outputTextures',
|
||||||
label='Textures',
|
label='Textures',
|
||||||
description='Output Texture files.',
|
description='Output Texture files.',
|
||||||
value= lambda attr: desc.Node.internalFolder + 'texture_*.' + attr.node.colorMapping.colorMappingFileType.value if attr.node.colorMapping.enable.value else '',
|
semantic='image',
|
||||||
|
value=desc.Node.internalFolder+'texture_*.exr',
|
||||||
uid=[],
|
uid=[],
|
||||||
group='',
|
group=''
|
||||||
),
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def upgradeAttributeValues(self, attrValues, fromVersion):
|
def upgradeAttributeValues(self, attrValues, fromVersion):
|
||||||
|
|
|
@ -9,6 +9,7 @@ FocusScope {
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
property var displayedNode: _reconstruction.cameraInit
|
||||||
property url source
|
property url source
|
||||||
property var metadata
|
property var metadata
|
||||||
property var viewIn3D
|
property var viewIn3D
|
||||||
|
@ -153,20 +154,66 @@ FocusScope {
|
||||||
imgContainer.y = Math.max((imgLayout.height - imgContainer.image.height * imgContainer.scale)*0.5, 0)
|
imgContainer.y = Math.max((imgLayout.height - imgContainer.image.height * imgContainer.scale)*0.5, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImageFile(type) {
|
function getImageFile() {
|
||||||
if(!_reconstruction.activeNodes)
|
if (outputAttribute.name == "") {
|
||||||
return "";
|
return getViewpointPath(_reconstruction.selectedViewId);
|
||||||
var depthMapNode = _reconstruction.activeNodes.get('allDepthMap').node;
|
}
|
||||||
if (type == "image") {
|
return getFileAttributePath(displayedNode, outputAttribute.name, _reconstruction.selectedViewId);
|
||||||
return root.source;
|
}
|
||||||
} else if (depthMapNode != undefined && _reconstruction.selectedViewId >= 0) {
|
|
||||||
return Filepath.stringToUrl(depthMapNode.internalFolder+_reconstruction.selectedViewId+"_"+type+"Map.exr");
|
function getFileAttributePath(node, attrName, viewId) {
|
||||||
|
// get output attribute with matching name
|
||||||
|
// and parse its value to get the image filepath
|
||||||
|
for (var i = 0; i < node.attributes.count; i++) {
|
||||||
|
var attr = node.attributes.at(i);
|
||||||
|
if (attr.name == attrName) {
|
||||||
|
return Filepath.stringToUrl(String(attr.value).replace("<VIEW_ID>", viewId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function setImageTypes(types) {
|
function getViewpointPath(viewId) {
|
||||||
imageType.types = types;
|
// get viewpoint from cameraInit with matching id
|
||||||
|
// and get its image filepath
|
||||||
|
for (var i = 0; i < _reconstruction.viewpoints.count; i++) {
|
||||||
|
var vp = _reconstruction.viewpoints.at(i);
|
||||||
|
if (vp.childAttribute("viewId").value == viewId) {
|
||||||
|
return Filepath.stringToUrl(vp.childAttribute("path").value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewpointMetadata(viewId) {
|
||||||
|
// get viewpoint from cameraInit with matching id
|
||||||
|
// and get its image filepath
|
||||||
|
for (var i = 0; i < _reconstruction.viewpoints.count; i++) {
|
||||||
|
var vp = _reconstruction.viewpoints.at(i);
|
||||||
|
if (vp.childAttribute("viewId").value == viewId) {
|
||||||
|
return vp.childAttribute("metadata").value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
onDisplayedNodeChanged: {
|
||||||
|
var names = [];
|
||||||
|
for (var i = 0; i < displayedNode.attributes.count; i++) {
|
||||||
|
var attr = displayedNode.attributes.at(i);
|
||||||
|
if (attr.isOutput && attr.desc.semantic == "image") {
|
||||||
|
names.push(attr.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputAttribute.names = names;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: _reconstruction
|
||||||
|
onSelectedViewIdChanged: {
|
||||||
|
root.source = getImageFile();
|
||||||
|
root.metadata = getViewpointMetadata(_reconstruction.selectedViewId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// context menu
|
// context menu
|
||||||
|
@ -276,7 +323,7 @@ FocusScope {
|
||||||
// Note: It does not work to use previously created component, so we re-create it with setSource.
|
// Note: It does not work to use previously created component, so we re-create it with setSource.
|
||||||
// floatViewerComp.createObject(floatImageViewerLoader, {
|
// floatViewerComp.createObject(floatImageViewerLoader, {
|
||||||
setSource("FloatImage.qml", {
|
setSource("FloatImage.qml", {
|
||||||
'source': Qt.binding(function() { return getImageFile(imageType.type); }),
|
'source': Qt.binding(function() { return getImageFile(); }),
|
||||||
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue; }),
|
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue; }),
|
||||||
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue; }),
|
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue; }),
|
||||||
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue; }),
|
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue; }),
|
||||||
|
@ -340,7 +387,7 @@ FocusScope {
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
autoTransform: true
|
autoTransform: true
|
||||||
onWidthChanged: if(status==Image.Ready) fit()
|
onWidthChanged: if(status==Image.Ready) fit()
|
||||||
source: getImageFile(imageType.type)
|
source: getImageFile()
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
// update cache source when image is loaded
|
// update cache source when image is loaded
|
||||||
if(status === Image.Ready)
|
if(status === Image.Ready)
|
||||||
|
@ -504,7 +551,7 @@ FocusScope {
|
||||||
font.pointSize: 8
|
font.pointSize: 8
|
||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
text: Filepath.urlToString(getImageFile(imageType.type))
|
text: Filepath.urlToString(getImageFile())
|
||||||
}
|
}
|
||||||
|
|
||||||
// show which depthmap node is active
|
// show which depthmap node is active
|
||||||
|
@ -994,7 +1041,7 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: imageType
|
id: outputAttribute
|
||||||
property var activeNode: root.oiioPluginAvailable ? _reconstruction.activeNodes.get('allDepthMap').node : null
|
property var activeNode: root.oiioPluginAvailable ? _reconstruction.activeNodes.get('allDepthMap').node : null
|
||||||
// set min size to 5 characters + one margin for the combobox
|
// set min size to 5 characters + one margin for the combobox
|
||||||
clip: true
|
clip: true
|
||||||
|
@ -1002,10 +1049,10 @@ FocusScope {
|
||||||
Layout.preferredWidth: 6.0 * Qt.application.font.pixelSize
|
Layout.preferredWidth: 6.0 * Qt.application.font.pixelSize
|
||||||
flat: true
|
flat: true
|
||||||
|
|
||||||
property var types: ["image"]
|
property var names: []
|
||||||
property string type: enabled ? types[currentIndex] : types[0]
|
property string name: (names.length > 0) ? (enabled ? names[currentIndex] : names[0]) : ""
|
||||||
|
|
||||||
model: types
|
model: names
|
||||||
enabled: activeNode
|
enabled: activeNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,7 +1065,7 @@ FocusScope {
|
||||||
Layout.minimumWidth: 0
|
Layout.minimumWidth: 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.viewIn3D(root.getImageFile("depth"))
|
root.viewIn3D(root.getFileAttributePath(activeNode, "depth", _reconstruction.selectedViewId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,14 +148,6 @@ Item {
|
||||||
|
|
||||||
viewIn3D: root.load3DMedia
|
viewIn3D: root.load3DMedia
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: imageGallery
|
|
||||||
onCurrentItemChanged: {
|
|
||||||
viewer2D.source = imageGallery.currentItemSource
|
|
||||||
viewer2D.metadata = imageGallery.currentItemMetadata
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DropArea {
|
DropArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
keys: ["text/uri-list"]
|
keys: ["text/uri-list"]
|
||||||
|
|
|
@ -851,11 +851,17 @@ ApplicationWindow {
|
||||||
reconstruction: _reconstruction
|
reconstruction: _reconstruction
|
||||||
readOnly: _reconstruction.computing
|
readOnly: _reconstruction.computing
|
||||||
|
|
||||||
function viewAttribute(attribute, mouse) {
|
function viewNode(node, mouse) {
|
||||||
let viewable = false;
|
// 2D viewer
|
||||||
viewable = workspaceView.viewIn2D(attribute);
|
viewer2D.displayedNode = node;
|
||||||
viewable |= workspaceView.viewIn3D(attribute, mouse);
|
|
||||||
return viewable;
|
// 3D viewer
|
||||||
|
for(var i=0; i < node.attributes.count; ++i)
|
||||||
|
{
|
||||||
|
var attr = node.attributes.at(i)
|
||||||
|
if(attr.isOutput && workspaceView.viewIn3D(attr, mouse))
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewIn3D(attribute, mouse) {
|
function viewIn3D(attribute, mouse) {
|
||||||
|
@ -867,33 +873,6 @@ ApplicationWindow {
|
||||||
panel3dViewer.viewer3D.solo(attribute);
|
panel3dViewer.viewer3D.solo(attribute);
|
||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewIn2D(attribute) {
|
|
||||||
var imageExts = ['.exr', '.jpg', '.tif', '.png'];
|
|
||||||
var ext = Filepath.extension(attribute.value);
|
|
||||||
if(imageExts.indexOf(ext) == -1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(attribute.value.includes('*'))
|
|
||||||
{
|
|
||||||
// For now, the viewer only supports a single image.
|
|
||||||
var firstFile = Filepath.globFirst(attribute.value)
|
|
||||||
viewer2D.source = Filepath.stringToUrl(firstFile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
viewer2D.source = Filepath.stringToUrl(attribute.value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setImageTypes(types) {
|
|
||||||
viewer2D.setImageTypes(types);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -958,17 +937,8 @@ ApplicationWindow {
|
||||||
nodeTypesModel: _nodeTypes
|
nodeTypesModel: _nodeTypes
|
||||||
|
|
||||||
onNodeDoubleClicked: {
|
onNodeDoubleClicked: {
|
||||||
workspaceView.setImageTypes(node.outputImageTypes);
|
|
||||||
|
|
||||||
_reconstruction.setActiveNode(node);
|
_reconstruction.setActiveNode(node);
|
||||||
|
workspaceView.viewNode(node, mouse);
|
||||||
let viewable = false;
|
|
||||||
for(var i=0; i < node.attributes.count; ++i)
|
|
||||||
{
|
|
||||||
var attr = node.attributes.at(i)
|
|
||||||
if(attr.isOutput && workspaceView.viewAttribute(attr, mouse))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onComputeRequest: computeManager.compute(node)
|
onComputeRequest: computeManager.compute(node)
|
||||||
onSubmitRequest: computeManager.submit(node)
|
onSubmitRequest: computeManager.submit(node)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue