1. 引言
將無人機拍攝拍攝的影像作為底圖可以實現快速甚至實時的更新,這對於應急指揮(如,森林防火)有著很大的意義
常規的做法是使用無人機拍攝一組照片,然後將這一組照片放入生產軟體(如,ContextCapture)生產正射影像,然後地圖切片釋出
這裡記錄的是另一種做法,直接將無人機照片貼地,這種方式速度很快,幾乎無需後臺處理,照片細節完整保留,當然這種方式存在著誤差,尤其地勢起伏較大的地方可能並不適用
2. 實現過程
這裡先有以下設定:
- 無人機拍攝的影像都是豎直向下的類似正射的照片(不是類正射的照片貼地視角會很奇怪)
- 無人機的朝向是正北方向
- 地面是接近於平面
無人機照片的位置關係如下圖所示,可以根據這些資料快速簡單地算出照片的貼地範圍
筆者這裡使用的無人機是大疆無人機型號H20T,參考大疆官網給出的H20T的資料:技術引數_禪思 H20 系列無人機負載_DJI大疆行業應用
這裡的DFOV是82.9°(廣角相機)
上圖中的width
和height
是圖片的寬高(單位:px),用以計算與斜邊的角度
上圖中的relative altitude
是相對高度,表示地面與無人機(相機)的距離
width
、height
和relative altitude
都可以直接從照片中讀取
要進行計算還有一點需要做的,就是把經緯度座標轉換為投影座標系下的座標,計算後再將投影座標資料轉換為經緯度,這樣就完成了計算
詳細的程式碼實現如下:
/**
* 根據照片的資料資訊,將照片貼上到地圖上(最好是正射照片,無人機指向正北方)
* @param imgWidth 圖片寬度(畫素)
* @param imgHeight 圖片高度(畫素)
* @param distance 相機與地面的距離(米)
* @param url 圖片的URL
* @param latitude 照片中心點的緯度
* @param longitude 照片中心點的經度
* @param dfov 相機的視場角(預設為82.9)
*/
function pasteImageByHeight(imgWidth, imgHeight, distance, url, latitude, longitude, dfov = 82.9) {
console.log(imgWidth, imgHeight, distance, url, latitude, longitude, dfov)
const thisDistance = parseFloat(distance)
if (isNaN(thisDistance) || thisDistance <= 0) {
globalAlert("照片與地面的距離資訊有誤")
return
}
const thisLatitude = parseFloat(latitude)
const thisLongitude = parseFloat(longitude)
if (isNaN(thisLatitude) || isNaN(thisLongitude)) {
globalAlert("照片的經緯度資訊有誤")
return
}
const hypotenuse = distance * Math.tan(dfov / 2 * Math.PI / 180)
const angle = Math.atan(imgHeight / imgWidth)
const halfWidth = hypotenuse * Math.sin(angle)
const halfHeight = hypotenuse * Math.cos(angle)
const projCrs = "+proj=utm +zone=51 +datum=WGS84 +units=m +no_defs"
const projectedPos = Proj4("EPSG:4326", projCrs, [thisLongitude, thisLatitude])
const [x, y] = projectedPos
const leftTop = Proj4(projCrs, "EPSG:4326", [x + halfWidth, y + halfHeight])
const rightBottom = Proj4(projCrs, "EPSG:4326", [x - halfWidth, y - halfHeight])
console.log(leftTop, rightBottom)
console.log({ xmin: rightBottom[0], xmax: leftTop[0], ymin: rightBottom[1], ymax: leftTop[1] })
const imageLayer = new mars3d.layer.ImageLayer({
name: "無人機航拍影像",
url: url,
rectangle: { xmin: rightBottom[0], xmax: leftTop[0], ymin: rightBottom[1], ymax: leftTop[1] },
zIndex: 20,
opacity: 0.6
})
map.addLayer(imageLayer)
map.flyToExtent({
xmin: longitude - 0.001,
xmax: longitude + 0.001,
ymin: latitude - 0.001,
ymax: latitude + 0.001
}, {
duration: 1
})
return imageLayer.id as string
}
最後實現的效果:
下面是可以最佳化的一些地方:
- 將圖片儲存到Rectangle Entity中,然後統一儲存到一個Layer裡,減少圖片過多時的效能下降
- 將圖片儲存到Rectangle Entity中,然後可以Rectangle Entity進行旋轉,這樣就無需讓無人機保持正北朝向
3. 參考資料
[1] 功能示例(Vue版) | Mars3D三維視覺化平臺 | 火星科技
[2] 功能示例(Vue版) | Mars3D三維視覺化平臺 | 火星科技