开源项目分享:UAVGeoMeasure------基于 DJI 无人机单图像的地面距离与面积测量工具
1. 项目简介
在无人机巡检、遥感图像分析、目标检测与图像分割任务中,我们经常不仅需要"识别目标在哪里",还希望进一步知道目标的实际尺度,例如道路宽度、车道宽度、草地面积、裸地面积、施工区域面积、地面设施长度等。
传统测绘流程通常依赖正射影像、空三处理、地面控制点或完整三维重建流程。虽然这些方法精度更高,但在很多工程验证、外业巡检和算法演示场景中,流程相对较重。
因此,我整理并开源了一个轻量级摄影测量工具:UAVGeoMeasure。
项目地址:
text
https://github.com/Thamkench/UAVGeoMeasure
UAVGeoMeasure 面向 DJI 无人机原始图像,利用图像中的 GPS、相对高度、云台姿态和视场角等元数据,通过针孔相机模型和射线-平面求交,实现单张图像上的地面距离测量和多边形面积估算。
整体流程可以概括为:
text
单张 DJI 图像
-> 鼠标点选目标边界
-> 像素点反投影为空间射线
-> 根据云台姿态转到 ENU 地面坐标系
-> 与目标平面求交
-> 计算真实尺度下的距离或面积
2. 项目效果
下面是部分测量效果展示。

图 1:草地多边形面积测量


图 2:车道宽度测量

图 3:太阳能板区域面积测量

图 4:输电管线距离测量
从实际结果可以看到,即使不是严格正射视角,只要图像保留 DJI 元数据,并且目标区域满足近似平面假设,也可以得到较为稳定的地面尺度估计结果。
3. 核心功能
UAVGeoMeasure 当前支持以下功能:
text
1. 读取 DJI 图像 EXIF/XMP 元数据
2. 根据图像尺寸和 FOV 构建近似相机内参
3. 将图像像素点转换为相机坐标系下的空间射线
4. 根据云台 yaw / pitch / roll 将射线转换到局部 ENU 坐标系
5. 将射线与目标平面求交
6. 两点测距
7. 三点及以上多边形面积计算
8. 交互式点选
9. 测量结果可视化与保存
当前项目适合用于:
- 道路宽度或车道宽度测量;
- 草地、裸地、施工区域面积估算;
- 非正射视角下的地面区域快速测量;
- 无人机巡检中的目标几何尺寸估计;
- 目标检测框或分割掩膜后处理的几何尺度补充;
- 摄影测量与计算机视觉结合的学习示例。
4. 工程结构
项目采用模块化设计,将元数据读取、相机建模、几何计算、交互操作和结果可视化拆分为独立模块。
text
UAVGeoMeasure/
├── README.md
├── README_zh-CN.md
├── requirements.txt
├── .gitignore
├── assets/
│ ├── car_test_distance_result_1.jpg
│ ├── car_test_distance_result_2.jpg
│ ├── grass_test_area_result.jpg
│ └── ...
├── demo_data/
│ └── solar_panel_test.jpg
├── examples/
│ └── run_interactive.py
└── uav_geo_measure/
├── __init__.py
├── config.py # 测量与可视化配置
├── metadata.py # DJI EXIF/XMP 元数据读取
├── camera.py # 相机内参与像素射线生成
├── geometry.py # 几何计算、ENU 转换、距离与面积计算
├── interaction.py # 鼠标与键盘交互
├── visualization.py # 结果绘制与图像保存
└── measure.py # 高层测量流程
这种结构的好处是后续扩展比较方便。例如:
- 接入 YOLO 检测框后,可以自动测量目标长度或宽度;
- 接入分割 mask 后,可以自动计算目标区域面积;
- 接入 DEM / DSM 后,可以替代简单水平平面假设;
- 接入 GeoJSON / Shapefile 导出后,可以进一步和 GIS 流程结合。
5. 使用方式
5.1 安装依赖
Python 依赖较少:
bash
pip install -r requirements.txt
requirements.txt 内容如下:
text
numpy
opencv-python
此外,项目使用 ExifTool 读取 DJI 图像元数据,需要保证终端中可以调用 exiftool。
推荐使用 Conda 跨平台安装:
bash
conda install -c conda-forge exiftool
Ubuntu / Debian:
bash
sudo apt update
sudo apt install libimage-exiftool-perl
macOS:
bash
brew install exiftool
Windows 可以使用 Conda 或 Chocolatey:
bash
conda install -c conda-forge exiftool
或者:
bash
choco install exiftool
安装后检查:
bash
exiftool -ver
5.2 运行交互式测量
在 examples/run_interactive.py 中修改图像路径:
python
from pathlib import Path
import sys
# Add project root to Python path, so this example script can import uav_geo_measure
PROJECT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT_ROOT))
from uav_geo_measure import run_interactive_measurement
from uav_geo_measure.config import MeasurementConfig
IMAGE_PATH = PROJECT_ROOT / "demo_data" / "solar_panel_test.jpg"
if __name__ == "__main__":
config = MeasurementConfig(
max_display_width=2200,
save_result=True,
target_z_m=0.0,
)
run_interactive_measurement(
image_path=str(IMAGE_PATH),
config=config,
)
运行:
bash
python examples/run_interactive.py
如果需要使用自己的图片,也可以直接写绝对路径:
python
IMAGE_PATH = Path("/path/to/your/dji_image.jpg")
Windows 路径示例:
python
IMAGE_PATH = Path(r"D:\dataset\uav\dji_image.jpg")
6. 交互操作
程序启动后,会显示图像窗口。用户可以通过鼠标点选需要测量的位置。
| 操作 | 功能 |
|---|---|
| 鼠标左键 | 添加测量点 |
| 鼠标右键 | 撤销上一个测量点 |
| Enter / Space | 完成测量 |
| C | 清空全部已选点 |
| Q / Esc | 退出 |
测量规则:
text
选择 2 个点:计算地面距离
选择 3 个及以上点:计算多边形面积
7. 方法原理
7.1 从像素点到相机射线
在针孔相机模型中,图像中的每一个像素点都对应一条从相机光心出发的空间射线。像素点本身不能直接确定真实三维点的位置,但可以确定一个视线方向。
相机内参矩阵为:
K=fx0cx 0fycy 001 \mathbf K = \begin{bmatrix} f_x & 0 & c_x \ 0 & f_y & c_y \ 0 & 0 & 1 \end{bmatrix} K=fx0cx 0fycy 001
其中,fxf_xfx 和 fyf_yfy 表示以像素为单位的焦距,cxc_xcx 和 cyc_ycy 表示主点坐标。
当前项目在没有相机标定文件的情况下,使用图像宽度和水平视场角近似计算焦距:
fx=W2tan(θh/2) f_x = \frac{W}{2\tan(\theta_h/2)} fx=2tan(θh/2)W
其中,WWW 为图像宽度,θh\theta_hθh 为水平视场角。当前轻量实现中进一步假设像素近似为正方形,因此有:
fy≈fx,cx≈W2,cy≈H2 f_y \approx f_x,\qquad c_x \approx \frac{W}{2},\qquad c_y \approx \frac{H}{2} fy≈fx,cx≈2W,cy≈2H
给定图像点 (u,v)(u,v)(u,v),可以得到相机坐标系下的射线方向:
r~c=K−1u v 1(u−cx)/fx (v−cy)/fy 1 \tilde{\mathbf r}_c = \mathbf K^{-1} \begin{bmatrix} u \ v \ 1 \end{bmatrix} \begin{bmatrix} (u-c_x)/f_x \ (v-c_y)/f_y \ 1 \end{bmatrix} r~c=K−1u v 1(u−cx)/fx (v−cy)/fy 1
进一步归一化后得到单位射线:
rc=r~c∣r~c∣2 \mathbf r_c = \frac{\tilde{\mathbf r}_c} {\left|\tilde{\mathbf r}_c\right|_2} rc=∣r~c∣2r~c
这里的 rc\mathbf r_crc 表示被点击像素在相机坐标系中的视线方向。
7.2 从相机坐标系到 ENU 坐标系
为了将射线投影到真实地面,需要知道相机当前的姿态。DJI 图像通常会记录云台的 GimbalYawDegree、GimbalPitchDegree 和 GimbalRollDegree。
项目利用这些角度构建相机坐标系到局部 ENU 坐标系的旋转矩阵:
$$
\mathbf R_{e \leftarrow c}
\mathbf R(\psi,\theta,\phi)
其中,ψ\\psiψ、θ\\thetaθ、ϕ\\phiϕ 分别表示 yaw、pitch 和 roll。相机坐标系中的射线可以转换为 ENU 坐标系中的射线: re=Re←crc \\mathbf r_e = \\mathbf R_{e \\leftarrow c}\\mathbf r_c re=Re←crc ENU 坐标系定义为: E 表示向东,N 表示向北,U 表示向上 E \\text{ 表示向东},\\qquad N \\text{ 表示向北},\\qquad U \\text{ 表示向上} E 表示向东,N 表示向北,U 表示向上 相机中心在局部 ENU 坐标系中表示为: C=\[0 0 h\] \\mathbf C = \\begin{bmatrix} 0 \\ 0 \\ h \\end{bmatrix} C=\[0 0 h\] 其中,hhh 来自 DJI 图像中的 `RelativeAltitude`。 *** ** * ** *** #### 7.3 射线与目标平面求交 将相机射线转换到 ENU 坐标系后,可以写成参数方程: ##
\mathbf P(\lambda)
\mathbf C + \lambda \mathbf r_e
其中,C\\mathbf CC 为相机中心,re\\mathbf r_ere 为 ENU 坐标系下的射线方向,λ\\lambdaλ 为射线尺度参数。 默认目标平面为: U=zt U = z_t U=zt 其中,ztz_tzt 由 `target_z_m` 控制。默认情况下: zt=0 z_t = 0 zt=0 通过射线和平面求交,即可得到每个点击像素对应的目标平面点坐标。 对于水平目标平面,射线与平面的交点尺度可以写为: λ=zt−CUrU \\lambda = \\frac{z_t - C_U}{r_U} λ=rUzt−CU 其中,CUC_UCU 是相机中心的竖直分量,rUr_UrU 是射线方向在竖直方向上的分量。 最终得到的目标平面投影点为: ##
\mathbf P_g =
\mathbf C + \lambda \mathbf r_e
\begin{bmatrix}
E \ N \ U
\end{bmatrix}
$$
随后就可以在 (E,N)(E,N)(E,N) 平面内计算距离和面积。
8. 距离与面积计算
8.1 两点距离
两个点投影到 ENU 平面后,分别记为:
P1=E1 N1 zt,P2=E2 N2 zt \mathbf P_1 = \begin{bmatrix} E_1 \ N_1 \ z_t \end{bmatrix}, \qquad \mathbf P_2 = \begin{bmatrix} E_2 \ N_2 \ z_t \end{bmatrix} P1=E1 N1 zt,P2=E2 N2 zt
其水平地面距离为:
d=(E2−E1)2+(N2−N1)2 d = \sqrt{(E_2-E_1)^2 + (N_2-N_1)^2} d=(E2−E1)2+(N2−N1)2
这个距离是目标平面上的米制距离,不是图像中的像素距离,也不是三维斜距。
8.2 多边形面积
当用户选择三个或更多点时,程序会将这些点视为多边形顶点,并使用鞋带公式计算面积:
A=12∣∑i=1m(EiNi+1Ei+1Ni)∣ A = \frac{1}{2} \left| \sum_{i=1}^{m} \left( E_iN_{i+1} E_{i+1}N_i \right) \right| A=21 i=1∑m(EiNi+1Ei+1Ni)
其中,mmm 为顶点数量,最后一个点需要与第一个点相连:
(Em+1,Nm+1)=(E1,N1) (E_{m+1},N_{m+1}) = (E_1,N_1) (Em+1,Nm+1)=(E1,N1)
实际使用时,建议按照目标边界顺时针或逆时针顺序点选。如果点选顺序混乱,或者多边形自相交,面积结果会不可靠。
9. 必需元数据
当前实现依赖以下 DJI 元数据字段:
text
XMP-drone-dji:GPSLatitude
XMP-drone-dji:GPSLongitude
XMP-drone-dji:RelativeAltitude
XMP-drone-dji:GimbalYawDegree
XMP-drone-dji:GimbalPitchDegree
XMP-drone-dji:GimbalRollDegree
Composite:FOV
可以用下面命令检查图像是否包含这些信息:
bash
exiftool -a -G1 -s image.jpg | grep -Ei "GPS|Altitude|Gimbal|Yaw|Pitch|Roll|FOV"
需要注意的是,很多公开视频抽帧、数据集图片、截图、网页图片可能已经去除了 EXIF/XMP 元数据。这类图片可以用于视觉展示,但如果没有额外相机参数和姿态信息,通常无法直接用于几何测量。
推荐使用:
- 原始 DJI JPEG 图像;
- 未被清除 EXIF/XMP 元数据的图像;
- 具有有效相对高度和云台姿态的图像;
- 目标区域近似位于水平面的图像。
10. 配置说明
MeasurementConfig 用于控制显示、测量和输出行为:
python
MeasurementConfig(
max_display_width=2200,
save_result=True,
target_z_m=0.0,
)
主要参数说明:
| 参数 | 说明 |
|---|---|
max_display_width |
图像显示窗口最大宽度。显示图像可以缩放,但点选坐标会自动映射回原图。 |
save_result |
是否保存绘制后的测量结果图。 |
target_z_m |
局部 ENU 坐标系中的目标平面位置,默认为 0。 |
line_color_bgr |
测量线或多边形颜色,OpenCV BGR 格式。 |
line_thickness |
测量线或多边形线宽。 |
text_scale |
结果文本字号比例。 |
text_thickness |
结果文本线宽。 |
11. 精度影响因素
UAVGeoMeasure 的误差主要来自图像到目标平面的几何投影过程,而不是距离或面积公式本身。
主要影响因素包括:
| 误差来源 | 影响 |
|---|---|
| 相对高度误差 | 影响射线与目标平面的求交尺度。 |
| 云台 pitch / roll 误差 | 会改变射线与目标平面的交点,倾斜视角下更明显。 |
| FOV 内参近似 | 可能引入尺度误差。 |
| 镜头畸变 | 图像边缘区域影响更明显。 |
| 目标表面非平面 | 违反平面假设,会造成系统偏差。 |
| 元数据缺失或不同步 | 会导致投影几何关系不成立。 |
| 人工点选误差 | 边界模糊或图像分辨率较低时影响更大。 |
对于单张图像中的相对距离和面积测量,水平 GPS 位置误差通常主要影响绝对经纬度位置,而不一定显著影响相对距离和面积。真正影响尺度的通常是相机内参、云台姿态、相对高度、目标平面假设以及人工选点精度。
为了提高结果可靠性,建议优先使用原始 DJI 图像,避免在图像极边缘选点,选择边界清晰且近似平面的目标,并在可能的情况下使用已知尺寸目标、外部测量结果或正射影像进行对比验证。
12. 输出结果
距离测量输出:
text
original_name_distance_result.jpg
面积测量输出:
text
original_name_area_result.jpg
终端会同步打印:
text
选取的像素坐标
投影后的 ENU 坐标
近似纬度和经度
测量得到的距离或面积
其中,经纬度是由局部 ENU 偏移量和图像 GPS 位置近似换算得到的,应理解为辅助地理参考,而不是测绘级大地坐标成果。
13. 局限性
UAVGeoMeasure 基于以下假设:
- 所选目标位于近似平面表面;
- 目标平面可以用一个局部平面近似表示;
- DJI 元数据可用且与图像同步;
- 基于 FOV 的内参近似满足当前测量需求;
- 镜头畸变可以忽略,或者对近似测量影响较小;
- 当前云台姿态定义与代码中的旋转模型一致。
该工具适合地面目标的近似测量。如果需要精确测量建筑立面、楼顶、树冠、桥梁、料堆或复杂三维表面,需要引入 DEM/DSM、LiDAR 或多视图三维重建结果。
14. 后续计划
后续可以继续扩展:
text
1. 支持相机标定内参
2. 镜头畸变校正
3. DJI 相机型号数据库
4. DEM / DSM 辅助射线求交
5. GCP 辅助校正
6. 批量测量模式
7. YOLO 检测框尺寸测量
8. 分割 mask 面积计算
9. GeoJSON / Shapefile 导出
10. 不确定性估计与误差传播
11. 与正射影像或 GIS 流程集成
15. 项目地址
GitHub:
text
https://github.com/Thamkench/UAVGeoMeasure
如果你也在做无人机视觉、遥感图像处理、摄影测量、目标检测或图像分割相关方向,欢迎查看项目。如果觉得有帮助,也欢迎点一个 Star 支持一下。