diff --git a/meshroom/ui/qml/ImageGallery/ImageDelegate.qml b/meshroom/ui/qml/ImageGallery/ImageDelegate.qml
index d29420ec..367ca097 100644
--- a/meshroom/ui/qml/ImageGallery/ImageDelegate.qml
+++ b/meshroom/ui/qml/ImageGallery/ImageDelegate.qml
@@ -26,7 +26,7 @@ Item {
id: _viewpoint
property url source: viewpoint ? Filepath.stringToUrl(viewpoint.get("path").value) : ''
property string metadataStr: viewpoint ? viewpoint.get("metadata").value : ''
- property var metadata: metadataStr ? JSON.parse(viewpoint.get("metadata").value) : null
+ property var metadata: metadataStr ? JSON.parse(viewpoint.get("metadata").value) : {}
}
MouseArea {
diff --git a/meshroom/ui/qml/ImageGallery/ImageGallery.qml b/meshroom/ui/qml/ImageGallery/ImageGallery.qml
index 8813818f..62f1b92c 100644
--- a/meshroom/ui/qml/ImageGallery/ImageGallery.qml
+++ b/meshroom/ui/qml/ImageGallery/ImageGallery.qml
@@ -80,6 +80,7 @@ Panel {
}
delegate: ImageDelegate {
+ id: imageDelegate
viewpoint: object.value
width: grid.cellWidth
@@ -108,37 +109,29 @@ Panel {
onRemoveRequest: sendRemoveRequest()
Keys.onDeletePressed: sendRemoveRequest()
- Row {
+ RowLayout {
anchors.top: parent.top
+ anchors.left: parent.left
anchors.right: parent.right
- anchors.margins: 4
+ anchors.margins: 2
spacing: 2
property bool valid: Qt.isQtObject(object) // object can be evaluated to null at some point during creation/deletion
- property bool noMetadata: valid && !_reconstruction.hasMetadata(model.object)
- property bool noIntrinsic: valid && !_reconstruction.hasValidIntrinsic(model.object)
+ property string intrinsicInitMode: valid ? _reconstruction.getIntrinsicInitMode(object) : ""
property bool inViews: valid && _reconstruction.sfmReport && _reconstruction.isInViews(object)
- // Missing metadata indicator
- Loader {
- active: parent.noMetadata
- visible: active
- sourceComponent: MaterialLabel {
- text: MaterialIcons.info_outline
- color: "#FF9800"
- ToolTip.text: "No Metadata"
- }
- }
- // Unknown camera instrinsics indicator
- Loader {
- active: parent.noIntrinsic
- visible: active
- sourceComponent: MaterialLabel {
- text: MaterialIcons.camera
- color: "#FF9800"
- ToolTip.text: "No Camera Instrinsic Parameters (missing Metadata?)"
- }
+
+ // Camera Initialization indicator
+ IntrinsicsIndicator {
+ intrinsicInitMode: parent.intrinsicInitMode
+ metadata: imageDelegate.metadata
+ font.pointSize: 10
+ padding: 2
+ background: Rectangle { color: Colors.sysPalette.window; opacity: 0.6 }
}
+
+ Item { Layout.fillWidth: true }
+
// Reconstruction status indicator
Loader {
active: parent.inViews
diff --git a/meshroom/ui/qml/ImageGallery/IntrinsicsIndicator.qml b/meshroom/ui/qml/ImageGallery/IntrinsicsIndicator.qml
new file mode 100644
index 00000000..17e28d78
--- /dev/null
+++ b/meshroom/ui/qml/ImageGallery/IntrinsicsIndicator.qml
@@ -0,0 +1,94 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.4
+import MaterialIcons 2.2
+import Utils 1.0
+
+
+/**
+ * Display camera initialization status and the value of metadata
+ * that take part in this process.
+ */
+MaterialLabel {
+ id: root
+
+ property string intrinsicInitMode
+ property var metadata: ({})
+
+ // access useful metadata
+ readonly property string make: metadata["Make"]
+ readonly property string model: metadata["Model"]
+ readonly property string focalLength: metadata["Exif:FocalLength"]
+ readonly property string focalLength35: metadata["Exif:FocalLengthIn35mmFilm"]
+ readonly property string bodySerialNumber: metadata["Exif:BodySerialNumber"]
+ readonly property string lensSerialNumber: metadata["Exif:LensSerialNumber"]
+ readonly property string sensorWidth: metadata["AliceVision:SensorWidth"]
+ readonly property string sensorWidthEstimation: metadata["AliceVision:SensorWidthEstimation"]
+
+ property string statusText: ""
+ property string detailsText: ""
+ property string helperText: ""
+
+ text: MaterialIcons.camera
+
+ function metaStr(value) {
+ return value || "undefined"
+ }
+
+ ToolTip.text: "Camera Intrinsics: " + statusText + "
"
+ + (detailsText ? detailsText + "
" : "")
+ + (helperText ? helperText + "
" : "")
+ + "
"
+ + "[Metadata]
"
+ + " - Make: " + metaStr(make) + "
"
+ + " - Model: " + metaStr(model) + "
"
+ + " - FocalLength: " + metaStr(focalLength) + "
"
+ + ((focalLength && sensorWidth) ? "" : " - FocalLengthIn35mmFilm: " + metaStr(focalLength35) + "
")
+ + " - SensorWidth: " + metaStr(sensorWidth) + (sensorWidthEstimation ? " (estimation: "+ sensorWidthEstimation + ")" : "")
+ + ((bodySerialNumber || lensSerialNumber) ? "" : "
Warning: SerialNumber metadata is missing.
Images from different devices might incorrectly share the same camera internal settings.")
+
+
+ state: intrinsicInitMode ? intrinsicInitMode : "unknown"
+
+ states: [
+ State {
+ name: "calibrated"
+ PropertyChanges {
+ target: root
+ color: Colors.green
+ statusText: "Calibrated"
+ detailsText: "Focal Length has been initialized externally."
+ }
+ },
+ State {
+ name: "estimated"
+ PropertyChanges {
+ target: root
+ statusText: sensorWidth ? "Estimated" : "Approximated"
+ color: sensorWidth ? Colors.green : Colors.yellow
+ detailsText: "Focal Length was estimated from Metadata" + (sensorWidth ? " and Sensor Database." : " only.")
+ helperText: !sensorWidth ? "Add your Camera Model to the Sensor Database for more accurate results." : ""
+ }
+ },
+ State {
+ name: "unknown"
+ PropertyChanges {
+ target: root
+ color: focalLength ? Colors.orange : Colors.red
+ statusText: "Unknown"
+ detailsText: "Focal Length could not be determined from metadata.
"
+ + "The default Field of View value was used as a fallback, which may lead to inaccurate result or failure."
+ helperText: "Check for missing Image metadata"
+ + (make && model && !sensorWidth ? " and/or add your Camera Model to the Sensor Database." : ".")
+ }
+ },
+ State {
+ // fallback status when initialization mode is unset
+ name: "none"
+ PropertyChanges {
+ target: root
+ visible: false
+ }
+ }
+
+ ]
+}
diff --git a/meshroom/ui/qml/Utils/Colors.qml b/meshroom/ui/qml/Utils/Colors.qml
index 15e79b9f..4dea34be 100644
--- a/meshroom/ui/qml/Utils/Colors.qml
+++ b/meshroom/ui/qml/Utils/Colors.qml
@@ -11,6 +11,7 @@ QtObject {
readonly property color green: "#4CAF50"
readonly property color orange: "#FF9800"
+ readonly property color yellow: "#FFEB3B"
readonly property color red: "#F44336"
readonly property color blue: "#03A9F4"
}
diff --git a/meshroom/ui/reconstruction.py b/meshroom/ui/reconstruction.py
index 1c682ca3..84e4e225 100755
--- a/meshroom/ui/reconstruction.py
+++ b/meshroom/ui/reconstruction.py
@@ -514,6 +514,38 @@ class Reconstruction(UIGraph):
allIntrinsicIds = [i.intrinsicId.value for i in self._cameraInit.intrinsics.value]
return viewpoint.intrinsicId.value in allIntrinsicIds
+ @Slot(QObject, result=QObject)
+ def getIntrinsic(self, viewpoint):
+ """
+ Get the intrinsic attribute associated to 'viewpoint' based on its intrinsicId.
+
+ Args:
+ viewpoint (Attribute): the Viewpoint to consider.
+ Returns:
+ Attribute: the Viewpoint's corresponding intrinsic or None if not found.
+ """
+ return next((i for i in self._cameraInit.intrinsics.value if i.intrinsicId.value == viewpoint.intrinsicId.value)
+ , None)
+
+ @Slot(QObject, result=str)
+ def getIntrinsicInitMode(self, viewpoint):
+ """
+ Get the initialization mode for the intrinsic associated to 'viewpoint'.
+
+ Args:
+ viewpoint (Attribute): the Viewpoint to consider.
+ Returns:
+ str: the initialization mode of the Viewpoint's intrinsic or an empty string if none.
+ """
+ intrinsic = self.getIntrinsic(viewpoint)
+ if not intrinsic:
+ return ""
+ try:
+ return intrinsic.initializationMode.value
+ except AttributeError:
+ # handle older versions that did not have this attribute
+ return ""
+
@Slot(QObject, result=bool)
def hasMetadata(self, viewpoint):
# Should be greater than 2 to avoid the particular case of ""