Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Features Viewer #539

Merged
merged 6 commits into from
Jul 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions meshroom/ui/qml/Controls/ColorChart.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import QtQuick 2.10
import QtQuick.Controls 2.10

import Utils 1.0

/**
* ColorChart is a color picker based on a set of predefined colors.
* It takes the form of a ToolButton that pops-up its palette when pressed.
*/
ToolButton {
id: root

property var colors: ["red", "green", "blue"]
property int currentIndex: 0

signal colorPicked(var colorIndex)

background: Rectangle {
color: root.colors[root.currentIndex]
border.width: hovered ? 1 : 0
border.color: Colors.sysPalette.midlight
}

onPressed: palettePopup.open()

// Popup for the color palette
Popup {
id: palettePopup

padding: 4
// content width is missing side padding (hence the + padding*2)
implicitWidth: colorChart.contentItem.width + padding*2

// center the current color
y: - (root.height - padding) / 2
x: - colorChart.currentItem.x - padding

// Colors palette
ListView {
id: colorChart
implicitHeight: contentItem.childrenRect.height
implicitWidth: contentWidth
orientation: ListView.Horizontal
spacing: 2
currentIndex: root.currentIndex
model: root.colors
// display each color as a ToolButton with a custom background
delegate: ToolButton {
padding: 0
width: root.width
height: root.height
background: Rectangle {
color: modelData
// display border of current/selected item
border.width: hovered || index === colorChart.currentIndex ? 1 : 0
border.color: Colors.sysPalette.midlight
}

onClicked: {
colorPicked(index);
palettePopup.close();
}
}
}
}
}
1 change: 1 addition & 0 deletions meshroom/ui/qml/Controls/qmldir
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Controls

ColorChart 1.0 ColorChart.qml
FloatingPane 1.0 FloatingPane.qml
Group 1.0 Group.qml
MessageDialog 1.0 MessageDialog.qml
Expand Down
3 changes: 3 additions & 0 deletions meshroom/ui/qml/Utils/Colors.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ QtObject {
readonly property color yellow: "#FFEB3B"
readonly property color red: "#F44336"
readonly property color blue: "#03A9F4"
readonly property color cyan: "#00BCD4"
readonly property color pink: "#E91E63"
readonly property color lime: "#CDDC39"
}
111 changes: 111 additions & 0 deletions meshroom/ui/qml/Viewer/FeaturesInfoOverlay.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import MaterialIcons 2.2

import Utils 1.0
import Controls 1.0

/**
* FeaturesInfoOverlay is an overlay that displays info and
* provides controls over a FeaturesViewer component.
*/
FloatingPane {
id: root

property int pluginStatus: Loader.Null
property Item featuresViewer: null
property var featureExtractionNode: null

ColumnLayout {

// Header
RowLayout {
// FeatureExtraction node name
Label {
text: featureExtractionNode.label
Layout.fillWidth: true
}
// Settings menu
Loader {
active: root.pluginStatus === Loader.Ready
sourceComponent: MaterialToolButton {
text: MaterialIcons.settings
font.pointSize: 10
onClicked: settingsMenu.popup(width, 0)
Menu {
id: settingsMenu
padding: 4
implicitWidth: 210

RowLayout {
Label {
text: "Display Mode:"
}
ComboBox {
id: displayModeCB
flat: true
Layout.fillWidth: true
model: featuresViewer.displayModes
onActivated: featuresViewer.displayMode = currentIndex
}
}
}
}
}
}

// Error message if AliceVision plugin is unavailable
Label {
visible: root.pluginStatus === Loader.Error
text: "AliceVision plugin is required to display Features"
color: Colors.red
}

// Feature types
ListView {
implicitHeight: contentHeight
implicitWidth: contentItem.childrenRect.width

model: featuresViewer !== null ? featuresViewer.model : 0

delegate: RowLayout {
id: featureType

property var viewer: featuresViewer.itemAt(index)
spacing: 4

// Visibility toogle
MaterialToolButton {
text: featureType.viewer.visible ? MaterialIcons.visibility : MaterialIcons.visibility_off
onClicked: featureType.viewer.visible = !featureType.viewer.visible
font.pointSize: 10
opacity: featureType.viewer.visible ? 1.0 : 0.6
}
// ColorChart picker
ColorChart {
implicitWidth: 12
implicitHeight: implicitWidth
colors: featuresViewer.colors
currentIndex: featureType.viewer.colorIndex
// offset FeaturesViewer color set when changing the color of one feature type
onColorPicked: featuresViewer.colorOffset = colorIndex - index
}
// Feature type name
Label {
text: featureType.viewer.describerType + (featureType.viewer.loading ? "" : ": " + featureType.viewer.features.length)
}
// Feature loading status
Loader {
active: featureType.viewer.loading
sourceComponent: BusyIndicator {
padding: 0
implicitWidth: 12
implicitHeight: 12
running: true
}
}
}
}
}
}
40 changes: 40 additions & 0 deletions meshroom/ui/qml/Viewer/FeaturesViewer.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import QtQuick 2.11
import AliceVision 1.0 as AliceVision

import Utils 1.0

/**
* FeaturesViewer displays the extracted feature points of a View.
* Requires QtAliceVision plugin.
*/
Repeater {
id: root

/// ViewID to display the features of
property int viewId
/// Folder containing the features files
property string folder
/// The list of describer types to load
property alias describerTypes: root.model
/// List of available display modes
readonly property var displayModes: ['Points', 'Squares', 'Oriented Squares']
/// Current display mode index
property int displayMode: 0
/// The list of colors used for displaying several describers
property var colors: [Colors.blue, Colors.red, Colors.yellow, Colors.green, Colors.orange, Colors.cyan, Colors.pink, Colors.lime]
/// Offset the color list
property int colorOffset: 0

model: root.describerTypes

// instantiate one FeaturesViewer by describer type
delegate: AliceVision.FeaturesViewer {
readonly property int colorIndex: (index+root.colorOffset)%root.colors.length
describerType: modelData
folder: root.folder
viewId: root.viewId
color: root.colors[colorIndex]
displayMode: root.displayMode
}

}
53 changes: 53 additions & 0 deletions meshroom/ui/qml/Viewer/Viewer2D.qml
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,39 @@ FocusScope {

visible: image.status === Image.Loading
}

// FeatureViewer: display view extracted feature points
// note: requires QtAliceVision plugin - use a Loader to evaluate plugin avaibility at runtime
Loader {
id: featuresViewerLoader

active: displayFeatures.checked

// handle rotation/position based on available metadata
rotation: {
var orientation = metadata ? metadata["Orientation"] : 0
switch(orientation) {
case "6": return 90;
case "8": return -90;
default: return 0;
}
}
x: rotation === 90 ? image.paintedWidth : 0
y: rotation === -90 ? image.paintedHeight : 0

Component.onCompleted: {
// instantiate and initialize a FeaturesViewer component dynamically using Loader.setSource
setSource("FeaturesViewer.qml", {
'active': Qt.binding(function() { return displayFeatures.checked; }),
'viewId': Qt.binding(function() { return _reconstruction.selectedViewId; }),
'model': Qt.binding(function() { return _reconstruction.featureExtraction.attribute("describerTypes").value; }),
'folder': Qt.binding(function() { return Filepath.stringToUrl(_reconstruction.featureExtraction.attribute("output").value); }),
})
}
}
}


// Busy indicator
BusyIndicator {
anchors.centerIn: parent
Expand Down Expand Up @@ -147,6 +178,21 @@ FocusScope {
metadata: visible ? root.metadata : {}
}


Loader {
id: featuresOverlay
anchors.bottom: bottomToolbar.top
anchors.left: parent.left
anchors.margins: 2
active: displayFeatures.checked

sourceComponent: FeaturesInfoOverlay {
featureExtractionNode: _reconstruction.featureExtraction
pluginStatus: featuresViewerLoader.status
featuresViewer: featuresViewerLoader.item
}
}

FloatingPane {
id: bottomToolbar
anchors.bottom: parent.bottom
Expand All @@ -163,6 +209,13 @@ FocusScope {
text: (image.status == Image.Ready ? image.scale.toFixed(2) : "1.00") + "x"
state: "xsmall"
}
MaterialToolButton {
id: displayFeatures
font.pointSize: 11
ToolTip.text: "Display Features"
checkable: true
text: MaterialIcons.scatter_plot
}

Item {
Layout.fillWidth: true
Expand Down
6 changes: 3 additions & 3 deletions meshroom/ui/qml/WorkspaceView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,16 @@ Item {

// Load reconstructed model
Button {
readonly property var outputAttribute: _reconstruction.endNode ? _reconstruction.endNode.attribute("outputMesh") : null
readonly property bool outputReady: outputAttribute && _reconstruction.endNode.globalStatus === "SUCCESS"
readonly property var outputAttribute: _reconstruction.texturing ? _reconstruction.texturing.attribute("outputMesh") : null
readonly property bool outputReady: outputAttribute && _reconstruction.texturing.globalStatus === "SUCCESS"
readonly property int outputMediaIndex: viewer3D.library.find(outputAttribute)

text: "Load Model"
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
visible: outputReady && outputMediaIndex == -1
onClicked: viewer3D.view(_reconstruction.endNode.attribute("outputMesh"))
onClicked: viewer3D.view(_reconstruction.texturing.attribute("outputMesh"))
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions meshroom/ui/qml/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,14 @@ ApplicationWindow {
{
_reconstruction.sfm = node;
}
else if(node.nodeType === "FeatureExtraction")
{
_reconstruction.featureExtraction = node;
}
else if(node.nodeType === "CameraInit")
{
_reconstruction.cameraInit = node;
}
for(var i=0; i < node.attributes.count; ++i)
{
var attr = node.attributes.at(i)
Expand Down
Loading