無人機照片貼地實現

当时明月在曾照彩云归發表於2024-03-26

1. 引言

將無人機拍攝拍攝的影像作為底圖可以實現快速甚至實時的更新,這對於應急指揮(如,森林防火)有著很大的意義

常規的做法是使用無人機拍攝一組照片,然後將這一組照片放入生產軟體(如,ContextCapture)生產正射影像,然後地圖切片釋出

這裡記錄的是另一種做法,直接將無人機照片貼地,這種方式速度很快,幾乎無需後臺處理,照片細節完整保留,當然這種方式存在著誤差,尤其地勢起伏較大的地方可能並不適用

2. 實現過程

這裡先有以下設定:

  • 無人機拍攝的影像都是豎直向下的類似正射的照片(不是類正射的照片貼地視角會很奇怪)
  • 無人機的朝向是正北方向
  • 地面是接近於平面

無人機照片的位置關係如下圖所示,可以根據這些資料快速簡單地算出照片的貼地範圍

image-20240326185312010

筆者這裡使用的無人機是大疆無人機型號H20T,參考大疆官網給出的H20T的資料:技術引數_禪思 H20 系列無人機負載_DJI大疆行業應用

這裡的DFOV是82.9°(廣角相機)

上圖中的widthheight是圖片的寬高(單位:px),用以計算與斜邊的角度

上圖中的relative altitude是相對高度,表示地面與無人機(相機)的距離

widthheightrelative 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
}

最後實現的效果:

image-20240326200031524

下面是可以最佳化的一些地方:

  • 將圖片儲存到Rectangle Entity中,然後統一儲存到一個Layer裡,減少圖片過多時的效能下降
  • 將圖片儲存到Rectangle Entity中,然後可以Rectangle Entity進行旋轉,這樣就無需讓無人機保持正北朝向

3. 參考資料

[1] 功能示例(Vue版) | Mars3D三維視覺化平臺 | 火星科技

[2] 功能示例(Vue版) | Mars3D三維視覺化平臺 | 火星科技

相關文章