Commit 16505fd3 authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #3981 from wingtra/upstream/camera_survey

Camera survey enhancement with additional parameters
parents d9fa1118 27146fa9
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.2 import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QGroundControl 1.0 import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0 import QGroundControl.ScreenTools 1.0
...@@ -22,40 +23,128 @@ Rectangle { ...@@ -22,40 +23,128 @@ Rectangle {
property real _margin: ScreenTools.defaultFontPixelWidth / 2 property real _margin: ScreenTools.defaultFontPixelWidth / 2
property var _cameraInfoCanonSX260: { "focalLength": 4.5, "sensorHeight": 4.55, "sensorWidth": 6.17 } property int cameraIndex: 1
ListModel {
id: cameraModelList
ListElement {
text: qsTr("Custom")
sensorWidth: 0
sensorHeight: 0
imageWidth: 0
imageHeight: 0
focalLength: 0
}
ListElement {
text: qsTr("Sony ILCE-QX1") //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-qx1-body-kit/specifications
sensorWidth: 23.2 //http://www.sony.com/electronics/camera-lenses/sel16f28/specifications
sensorHeight: 15.4
imageWidth: 5456
imageHeight: 3632
focalLength: 16
}
ListElement {
text: qsTr("Canon S100 PowerShot")
sensorWidth: 7.6
sensorHeight: 5.7
imageWidth: 4000
imageHeight: 3000
focalLength: 5.2
}
ListElement {
text: qsTr("Canon SX260 HS PowerShot ")
sensorWidth: 6.17
sensorHeight: 4.55
imageWidth: 4000
imageHeight: 3000
focalLength: 4.5
}
}
function recalcFromCameraValues() { function recalcFromCameraValues() {
var focalLength = Number(focalLengthField.text) var focalLength = cameraModelList.get(cameraIndex).focalLength
var sensorWidth = Number(sensorWidthField.text) var sensorWidth = cameraModelList.get(cameraIndex).sensorWidth
var sensorHeight = Number(sensorHeightField.text) var sensorHeight = cameraModelList.get(cameraIndex).sensorHeight
var overlap = Number(imageOverlapField.text) var imageWidth = cameraModelList.get(cameraIndex).imageWidth
var imageHeight = cameraModelList.get(cameraIndex).imageHeight
if (focalLength <= 0.0 || sensorWidth <= 0.0 || sensorHeight <= 0.0) {
var gsd = Number(gsdField.text)
var frontalOverlap = Number(frontalOverlapField.text)
var sideOverlap = Number(sideOverlapField.text)
if (focalLength <= 0.0 || sensorWidth <= 0.0 || sensorHeight <= 0.0 || imageWidth < 0 || imageHeight < 0 || gsd < 0.0 || frontalOverlap < 0 || sideOverlap < 0) {
missionItem.gridAltitude.rawValue = 0
missionItem.gridSpacing.rawValue = 0
missionItem.cameraTriggerDistance.rawValue = 0
return return
} }
var scaledFocalLengthMM = (1000.0 * missionItem.gridAltitude.rawValue) / focalLength var altitude
var imageWidthM = (sensorWidth * scaledFocalLengthMM) / 1000.0; var imageSizeSideGround //size in side (non flying) direction of the image on the ground
var imageHeightM = (sensorHeight * scaledFocalLengthMM) / 1000.0; var imageSizeFrontGround //size in front (flying) direction of the image on the ground
var gridSpacing var gridSpacing
var cameraTriggerDistance var cameraTriggerDistance
altitude = (imageWidth * gsd * focalLength) / (sensorWidth * 100)
if (cameraOrientationLandscape.checked) { if (cameraOrientationLandscape.checked) {
gridSpacing = imageWidthM imageSizeSideGround = (imageWidth * gsd) / 100
cameraTriggerDistance = imageHeightM imageSizeFrontGround = (imageHeight * gsd) / 100
} else { } else {
gridSpacing = imageHeightM imageSizeSideGround = (imageHeight * gsd) / 100
cameraTriggerDistance = imageWidthM imageSizeFrontGround = (imageWidth * gsd) / 100
} }
gridSpacing = (1.0 - (overlap / 100.0)) * gridSpacing
cameraTriggerDistance = (1.0 - (overlap / 100.0)) * cameraTriggerDistance
gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 )
cameraTriggerDistance = imageSizeFrontGround * ( (100-frontalOverlap) / 100 )
missionItem.gridAltitude.rawValue = altitude
missionItem.gridSpacing.rawValue = gridSpacing missionItem.gridSpacing.rawValue = gridSpacing
missionItem.cameraTriggerDistance.rawValue = cameraTriggerDistance missionItem.cameraTriggerDistance.rawValue = cameraTriggerDistance
} }
function recalcFromMissionValues() {
var focalLength = cameraModelList.get(cameraIndex).focalLength
var sensorWidth = cameraModelList.get(cameraIndex).sensorWidth
var sensorHeight = cameraModelList.get(cameraIndex).sensorHeight
var imageWidth = cameraModelList.get(cameraIndex).imageWidth
var imageHeight = cameraModelList.get(cameraIndex).imageHeight
var altitude = missionItem.gridAltitude.rawValue
var gridSpacing = missionItem.gridSpacing.rawValue
var cameraTriggerDistance = missionItem.cameraTriggerDistance.rawValue
if (focalLength <= 0.0 || sensorWidth <= 0.0 || sensorHeight <= 0.0 || imageWidth < 0 || imageHeight < 0 || altitude < 0.0 || gridSpacing < 0.0 || cameraTriggerDistance < 0.0) {
gsdField.text = "0.0"
sideOverlapField.text = "0"
frontalOverlapField.text = "0"
return
}
var gsd
var imageSizeSideGround //size in side (non flying) direction of the image on the ground
var imageSizeFrontGround //size in front (flying) direction of the image on the ground
gsd = (altitude * sensorWidth * 100) / (imageWidth * focalLength)
if (cameraOrientationLandscape.checked) {
imageSizeSideGround = (imageWidth * gsd) / 100
imageSizeFrontGround = (imageHeight * gsd) / 100
} else {
imageSizeSideGround = (imageHeight * gsd) / 100
imageSizeFrontGround = (imageWidth * gsd) / 100
}
var sideOverlap = (imageSizeSideGround == 0 ? 0 : 100 - (gridSpacing*100 / imageSizeSideGround))
var frontOverlap = (imageSizeFrontGround == 0 ? 0 : 100 - (cameraTriggerDistance*100 / imageSizeFrontGround))
gsdField.text = gsd.toFixed(1)
sideOverlapField.text = sideOverlap.toFixed(0)
frontalOverlapField.text = frontOverlap.toFixed(0)
}
function polygonCaptureStarted() { function polygonCaptureStarted() {
missionItem.clearPolygon() missionItem.clearPolygon()
} }
function polygonCaptureFinished(coordinates) { function polygonCaptureFinished(coordinates) {
...@@ -75,7 +164,9 @@ Rectangle { ...@@ -75,7 +164,9 @@ Rectangle {
ExclusiveGroup { ExclusiveGroup {
id: cameraOrientationGroup id: cameraOrientationGroup
onCurrentChanged: recalcFromCameraValues() onCurrentChanged: {
recalcFromMissionValues()
}
} }
Column { Column {
...@@ -112,6 +203,8 @@ Rectangle { ...@@ -112,6 +203,8 @@ Rectangle {
width: _editFieldWidth width: _editFieldWidth
showUnits: true showUnits: true
fact: modelData fact: modelData
onEditingFinished: recalcFromMissionValues()
validator: DoubleValidator{bottom:0.0; decimals:2}
} }
} }
} }
...@@ -123,6 +216,48 @@ Rectangle { ...@@ -123,6 +216,48 @@ Rectangle {
onClicked: missionItem.gridAltitudeRelative = checked onClicked: missionItem.gridAltitudeRelative = checked
} }
Grid {
columns: 2
columnSpacing: ScreenTools.defaultFontPixelWidth
rowSpacing: _margin
verticalItemAlignment: Grid.AlignVCenter
QGCLabel {
text: qsTr("GSD:")
width: _editFieldWidth
}
QGCTextField {
id: gsdField
width: _editFieldWidth
unitsLabel: "cm/px"
showUnits: true
onEditingFinished: recalcFromCameraValues()
validator: DoubleValidator{bottom:0.0; decimals:2}
}
QGCLabel { text: qsTr("Frontal Overlap:") }
QGCTextField {
id: frontalOverlapField
width: _editFieldWidth
unitsLabel: "%"
showUnits: true
onEditingFinished: recalcFromCameraValues()
validator: IntValidator {bottom:0}
}
QGCLabel { text: qsTr("Side Overlap:") }
QGCTextField {
id: sideOverlapField
width: _editFieldWidth
unitsLabel: "%"
showUnits: true
onEditingFinished: recalcFromCameraValues()
validator: IntValidator {bottom:0}
}
Component.onCompleted: recalcFromMissionValues()
}
QGCLabel { text: qsTr("Camera:") } QGCLabel { text: qsTr("Camera:") }
Rectangle { Rectangle {
...@@ -132,11 +267,14 @@ Rectangle { ...@@ -132,11 +267,14 @@ Rectangle {
color: qgcPal.text color: qgcPal.text
} }
Row { Grid {
columns: 2
spacing: ScreenTools.defaultFontPixelWidth spacing: ScreenTools.defaultFontPixelWidth
verticalItemAlignment: Grid.AlignVCenter
QGCRadioButton { QGCRadioButton {
id: cameraOrientationLandscape id: cameraOrientationLandscape
width: _editFieldWidth
text: "Landscape" text: "Landscape"
checked: true checked: true
exclusiveGroup: cameraOrientationGroup exclusiveGroup: cameraOrientationGroup
...@@ -147,15 +285,10 @@ Rectangle { ...@@ -147,15 +285,10 @@ Rectangle {
text: "Portrait" text: "Portrait"
exclusiveGroup: cameraOrientationGroup exclusiveGroup: cameraOrientationGroup
} }
}
Grid {
columns: 2
spacing: ScreenTools.defaultFontPixelWidth
verticalItemAlignment: Grid.AlignVCenter
QGCCheckBox { QGCCheckBox {
id: cameraTrigger id: cameraTrigger
width: _editFieldWidth
text: qsTr("Trigger:") text: qsTr("Trigger:")
checked: missionItem.cameraTrigger checked: missionItem.cameraTrigger
onClicked: missionItem.cameraTrigger = checked onClicked: missionItem.cameraTrigger = checked
...@@ -166,49 +299,188 @@ Rectangle { ...@@ -166,49 +299,188 @@ Rectangle {
showUnits: true showUnits: true
fact: missionItem.cameraTriggerDistance fact: missionItem.cameraTriggerDistance
enabled: missionItem.cameraTrigger enabled: missionItem.cameraTrigger
onEditingFinished: recalcFromMissionValues()
validator: DoubleValidator{bottom:0.0; decimals:2}
} }
}
QGCLabel { text: qsTr("Focal length:") } Component {
QGCTextField { id: cameraFields
id: focalLengthField
unitsLabel: "mm"
showUnits: true
text: _cameraInfoCanonSX260.focalLength.toString()
onEditingFinished: recalcFromCameraValues() QGCViewDialog {
}
QGCLabel { text: qsTr("Sensor Width:") } Column {
QGCTextField { id: dialogColumn
id: sensorWidthField anchors.margins: _margin
unitsLabel: "mm" anchors.top: parent.top
showUnits: true anchors.left: parent.left
text: _cameraInfoCanonSX260.sensorWidth.toString() anchors.right: parent.right
spacing: _margin * 5
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
id: selectCameraModelText
text: qsTr("Select Camera Model:")
}
QGCComboBox {
id: cameraModelCombo
model: cameraModelList
width: dialogColumn.width - selectCameraModelText.width - ScreenTools.defaultFontPixelWidth
onActivated: {
cameraIndex = index
}
Component.onCompleted: {
var index = cameraIndex
if (index === -1) {
console.warn("Active camera model name not in combo", cameraIndex)
} else {
cameraModelCombo.currentIndex = index
}
}
}
}
onEditingFinished: recalcFromCameraValues() Grid {
columns: 2
spacing: ScreenTools.defaultFontPixelWidth
verticalItemAlignment: Grid.AlignVCenter
QGCLabel { text: qsTr("Sensor Width:") }
QGCTextField {
id: sensorWidthField
unitsLabel: "mm"
showUnits: true
text: cameraModelList.get(cameraIndex).sensorWidth.toFixed(2)
readOnly: cameraIndex != 0
enabled: cameraIndex == 0
validator: DoubleValidator{bottom:0.0; decimals:2}
onEditingFinished: {
if (cameraIndex == 0) {
cameraModelList.setProperty(cameraIndex, "sensorWidth", Number(text))
}
}
}
QGCLabel { text: qsTr("Sensor Height:") }
QGCTextField {
id: sensorHeightField
unitsLabel: "mm"
showUnits: true
text: cameraModelList.get(cameraIndex).sensorHeight.toFixed(2)
readOnly: cameraIndex != 0
enabled: cameraIndex == 0
validator: DoubleValidator{bottom:0.0; decimals:2}
onEditingFinished: {
if (cameraIndex == 0) {
cameraModelList.setProperty(cameraIndex, "sensorHeight", Number(text))
}
}
}
QGCLabel { text: qsTr("Image Width:") }
QGCTextField {
id: imageWidthField
unitsLabel: "px"
showUnits: true
text: cameraModelList.get(cameraIndex).imageWidth.toFixed(0)
readOnly: cameraIndex != 0
enabled: cameraIndex == 0
validator: IntValidator {bottom:0}
onEditingFinished: {
if (cameraIndex == 0) {
cameraModelList.setProperty(cameraIndex, "imageWidth", Number(text))
}
}
}
QGCLabel { text: qsTr("Image Height:") }
QGCTextField {
id: imageHeightField
unitsLabel: "px"
showUnits: true
text: cameraModelList.get(cameraIndex).imageHeight.toFixed(0)
readOnly: cameraIndex != 0
enabled: cameraIndex == 0
validator: IntValidator {bottom:0}
onEditingFinished: {
if (cameraIndex == 0) {
cameraModelList.setProperty(cameraIndex, "imageHeight", Number(text))
}
}
}
QGCLabel { text: qsTr("Focal Length:") }
QGCTextField {
id: focalLengthField
unitsLabel: "mm"
showUnits: true
text: cameraModelList.get(cameraIndex).focalLength.toFixed(2)
readOnly: cameraIndex != 0
enabled: cameraIndex == 0
validator: DoubleValidator{bottom:0.0; decimals:2}
onEditingFinished: {
if (cameraIndex == 0) {
cameraModelList.setProperty(cameraIndex, "focalLength", Number(text))
}
}
}
}
}
function accept() {
hideDialog()
recalcFromCameraValues()
}
}//QGCViewDialog
}//Component
Column {
spacing: ScreenTools.defaultFontPixelHeight*0.01
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel { text: qsTr("Model:") }
QGCLabel { text: cameraModelList.get(cameraIndex).text }
} }
QGCLabel { text: qsTr("Sensor height:") } Grid {
QGCTextField { columns: 2
id: sensorHeightField columnSpacing: ScreenTools.defaultFontPixelWidth
unitsLabel: "mm" rowSpacing: ScreenTools.defaultFontPixelHeight*0.01
showUnits: true
text: _cameraInfoCanonSX260.sensorHeight.toString()
onEditingFinished: recalcFromCameraValues() QGCLabel {
text: qsTr("Sensor Size:")
width: _editFieldWidth
}
QGCLabel {
text: cameraModelList.get(cameraIndex).sensorWidth.toFixed(2) + qsTr(" x ") + cameraModelList.get(cameraIndex).sensorHeight.toFixed(2)
width: _editFieldWidth
}
QGCLabel { text: qsTr("Image Size:") }
QGCLabel { text: cameraModelList.get(cameraIndex).imageWidth.toFixed(0) + qsTr(" x ") + cameraModelList.get(cameraIndex).imageHeight.toFixed(0) }
QGCLabel { text: qsTr("Focal length:") }
QGCLabel { text: cameraModelList.get(cameraIndex).focalLength.toFixed(2) }
} }
}
QGCLabel { text: qsTr("Image overlap:") } QGCButton {
QGCTextField { id: cameraModelChange
id: imageOverlapField text: qsTr("Change")
unitsLabel: "%"
showUnits: true
text: "0"
onEditingFinished: recalcFromCameraValues() onClicked: {
qgcView.showDialog(cameraFields, qsTr("Set Camera Model"), qgcView.showDialogDefaultWidth, StandardButton.Save)
} }
} }
QGCLabel { text: qsTr("Polygon:") } QGCLabel { text: qsTr("Polygon:") }
Rectangle { Rectangle {
......
...@@ -53,7 +53,7 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) ...@@ -53,7 +53,7 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent)
, _turnaroundDistMetaData (FactMetaData::valueTypeDouble) , _turnaroundDistMetaData (FactMetaData::valueTypeDouble)
, _cameraTriggerDistanceMetaData(FactMetaData::valueTypeDouble) , _cameraTriggerDistanceMetaData(FactMetaData::valueTypeDouble)
{ {
_gridAltitudeFact.setRawValue(25); _gridAltitudeFact.setRawValue(50);
_gridSpacingFact.setRawValue(10); _gridSpacingFact.setRawValue(10);
_turnaroundDistFact.setRawValue(60); _turnaroundDistFact.setRawValue(60);
_cameraTriggerDistanceFact.setRawValue(25); _cameraTriggerDistanceFact.setRawValue(25);
...@@ -344,7 +344,7 @@ void SurveyMissionItem::_clearGrid(void) ...@@ -344,7 +344,7 @@ void SurveyMissionItem::_clearGrid(void)
void SurveyMissionItem::_generateGrid(void) void SurveyMissionItem::_generateGrid(void)
{ {
if (_polygonPath.count() < 3) { if (_polygonPath.count() < 3 || _gridSpacingFact.rawValue().toDouble() <= 0) {
_clearGrid(); _clearGrid();
return; return;
} }
...@@ -391,7 +391,11 @@ void SurveyMissionItem::_generateGrid(void) ...@@ -391,7 +391,11 @@ void SurveyMissionItem::_generateGrid(void)
_gridPoints += QVariant::fromValue(geoCoord); _gridPoints += QVariant::fromValue(geoCoord);
} }
_setSurveyDistance(surveyDistance); _setSurveyDistance(surveyDistance);
_setCameraShots((int)floor(surveyDistance / _cameraTriggerDistanceFact.rawValue().toDouble())); if (_cameraTriggerDistanceFact.rawValue().toDouble() > 0) {
_setCameraShots((int)floor(surveyDistance / _cameraTriggerDistanceFact.rawValue().toDouble()));
} else {
_setCameraShots(0);
}
emit gridPointsChanged(); emit gridPointsChanged();
emit lastSequenceNumberChanged(lastSequenceNumber()); emit lastSequenceNumberChanged(lastSequenceNumber());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment