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.Controls 1.2
import QtQuick.Dialogs 1.2
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
......@@ -22,40 +23,128 @@ Rectangle {
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() {
var focalLength = Number(focalLengthField.text)
var sensorWidth = Number(sensorWidthField.text)
var sensorHeight = Number(sensorHeightField.text)
var overlap = Number(imageOverlapField.text)
if (focalLength <= 0.0 || sensorWidth <= 0.0 || sensorHeight <= 0.0) {
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 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
}
var scaledFocalLengthMM = (1000.0 * missionItem.gridAltitude.rawValue) / focalLength
var imageWidthM = (sensorWidth * scaledFocalLengthMM) / 1000.0;
var imageHeightM = (sensorHeight * scaledFocalLengthMM) / 1000.0;
var altitude
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
var gridSpacing
var cameraTriggerDistance
altitude = (imageWidth * gsd * focalLength) / (sensorWidth * 100)
if (cameraOrientationLandscape.checked) {
gridSpacing = imageWidthM
cameraTriggerDistance = imageHeightM
imageSizeSideGround = (imageWidth * gsd) / 100
imageSizeFrontGround = (imageHeight * gsd) / 100
} else {
gridSpacing = imageHeightM
cameraTriggerDistance = imageWidthM
imageSizeSideGround = (imageHeight * gsd) / 100
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.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() {
missionItem.clearPolygon()
missionItem.clearPolygon()
}
function polygonCaptureFinished(coordinates) {
......@@ -75,7 +164,9 @@ Rectangle {
ExclusiveGroup {
id: cameraOrientationGroup
onCurrentChanged: recalcFromCameraValues()
onCurrentChanged: {
recalcFromMissionValues()
}
}
Column {
......@@ -112,6 +203,8 @@ Rectangle {
width: _editFieldWidth
showUnits: true
fact: modelData
onEditingFinished: recalcFromMissionValues()
validator: DoubleValidator{bottom:0.0; decimals:2}
}
}
}
......@@ -123,6 +216,48 @@ Rectangle {
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:") }
Rectangle {
......@@ -132,11 +267,14 @@ Rectangle {
color: qgcPal.text
}
Row {
Grid {
columns: 2
spacing: ScreenTools.defaultFontPixelWidth
verticalItemAlignment: Grid.AlignVCenter
QGCRadioButton {
id: cameraOrientationLandscape
width: _editFieldWidth
text: "Landscape"
checked: true
exclusiveGroup: cameraOrientationGroup
......@@ -147,15 +285,10 @@ Rectangle {
text: "Portrait"
exclusiveGroup: cameraOrientationGroup
}
}
Grid {
columns: 2
spacing: ScreenTools.defaultFontPixelWidth
verticalItemAlignment: Grid.AlignVCenter
QGCCheckBox {
id: cameraTrigger
width: _editFieldWidth
text: qsTr("Trigger:")
checked: missionItem.cameraTrigger
onClicked: missionItem.cameraTrigger = checked
......@@ -166,49 +299,188 @@ Rectangle {
showUnits: true
fact: missionItem.cameraTriggerDistance
enabled: missionItem.cameraTrigger
onEditingFinished: recalcFromMissionValues()
validator: DoubleValidator{bottom:0.0; decimals:2}
}
}
QGCLabel { text: qsTr("Focal length:") }
QGCTextField {
id: focalLengthField
unitsLabel: "mm"
showUnits: true
text: _cameraInfoCanonSX260.focalLength.toString()
Component {
id: cameraFields
onEditingFinished: recalcFromCameraValues()
}
QGCViewDialog {
QGCLabel { text: qsTr("Sensor Width:") }
QGCTextField {
id: sensorWidthField
unitsLabel: "mm"
showUnits: true
text: _cameraInfoCanonSX260.sensorWidth.toString()
Column {
id: dialogColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
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:") }
QGCTextField {
id: sensorHeightField
unitsLabel: "mm"
showUnits: true
text: _cameraInfoCanonSX260.sensorHeight.toString()
Grid {
columns: 2
columnSpacing: ScreenTools.defaultFontPixelWidth
rowSpacing: ScreenTools.defaultFontPixelHeight*0.01
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:") }
QGCTextField {
id: imageOverlapField
unitsLabel: "%"
showUnits: true
text: "0"
QGCButton {
id: cameraModelChange
text: qsTr("Change")
onEditingFinished: recalcFromCameraValues()
onClicked: {
qgcView.showDialog(cameraFields, qsTr("Set Camera Model"), qgcView.showDialogDefaultWidth, StandardButton.Save)
}
}
QGCLabel { text: qsTr("Polygon:") }
Rectangle {
......
......@@ -53,7 +53,7 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent)
, _turnaroundDistMetaData (FactMetaData::valueTypeDouble)
, _cameraTriggerDistanceMetaData(FactMetaData::valueTypeDouble)
{
_gridAltitudeFact.setRawValue(25);
_gridAltitudeFact.setRawValue(50);
_gridSpacingFact.setRawValue(10);
_turnaroundDistFact.setRawValue(60);
_cameraTriggerDistanceFact.setRawValue(25);
......@@ -344,7 +344,7 @@ void SurveyMissionItem::_clearGrid(void)
void SurveyMissionItem::_generateGrid(void)
{
if (_polygonPath.count() < 3) {
if (_polygonPath.count() < 3 || _gridSpacingFact.rawValue().toDouble() <= 0) {
_clearGrid();
return;
}
......@@ -391,7 +391,11 @@ void SurveyMissionItem::_generateGrid(void)
_gridPoints += QVariant::fromValue(geoCoord);
}
_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 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