简介:面向无人机航测、摄影测量与三维重建方向的初学者和从业者,这份 PDF 以 Agisoft Photoscan 为工具,梳理从原始照片到 DEM 及正射影像的完整生产链路,重点解决坐标系统设定、像控点布设、空三优化与成果导出中的操作疑问。资源包仅含 1 个 PDF 文件,体积约 512KB,属于轻量型流程手册,便于查阅和打印。内容按参数预设、添加照片、装载相机 POS、检查相机校准、对齐照片、标记、优化照片对齐、设置边界框、建立密集点云等环节展开,并补充构建网格、编辑几何、构建纹理、Build DEM 与 Orthomosaic 等后续步骤;对精度、配对预选、关键点数量、深度过滤、纹理大小等参数给出推荐值,同时说明 GCP 均匀分布、标记精度与坐标系设置要点。目前已有 1357 人学习下载,适合希望快速掌握 Photoscan 航测处理流程、对照实操查漏补缺的读者。
1. 从一组航拍照片到DEM和正射影像,Photoscan这条链路真正难在哪
一块200米见方的土方场地,飞手带回来八百多张照片,甲方要的是两样东西:能算填挖方量的 DEM,和能当底图量距离的正射影像。很多人以为把照片丢进 Photoscan、点两下工作流就完事,实际结果是点云拉花、地表鼓包、正射接边错位,一套问题接着一套。
难点不在软件会不会用,而在参数之间是有依赖关系的:照片对齐没做稳,后面深度图质量拉到 Ultra 也救不回来;坐标系统选错,DEM 的平面位置和高程都对不上真实世界;导出时 nodata 没设,进 GIS 一做统计全图最大值直接爆掉。
这篇按实际生产顺序拆开讲:控制点布设与坐标系统、对齐与深度图的参数取舍、密集点云栅格化成 DEM、正射影像镶嵌与分块导出,以及 DSM 转 DEM 之后用 GDAL 做的后处理和精度验证。做航测、土方、矿山、工程测量的可以直接照着参数改。
2. Photoscan生成DEM的参数链:从控制点到栅格化
2.1 控制点与坐标系统:DEM能不能落到真实世界
Photoscan 的相机对齐只负责把照片摆到互相自洽的位置上,它给出的位置是相对的。要让 DEM 能和其他数据叠、能量测,必须靠外部约束把它拽到真实坐标系里,这件事有两个来源:影像自带的 POS 数据(无人机 RTK/PPK 记录的中心点坐标和姿态),以及地面控制点(GCP)。
POS 精度够的时候(RTK 固定解、平面高程都在厘米级),可以直接用 POS 做参考约束,不布 GCP 也能出厘米到分米级的成果。POS 是单点 GPS 记录的,就必须布 GCP,通常按区域边缘加中心布设,一块 200 米见方的场地布 5 到 9 个足够,用油漆十字或对空标志,RTK 打点。
投影这块要特别注意两点。一是平面坐标系统,国内工程常落在 CGCS2000 的 3 度带高斯投影上,在 Photoscan 的参考设置里选对应的 EPSG 代码,别用经纬度去出图,否则面积和距离量算会有形变。二是高程基准,RTK 打出来的通常是正常高(1985 国家高程基准),而 GNSS 原始记录的是椭球高,两者差一个高程异常值,如果不做改正,DEM 的高程会整体偏移几米,而且这个偏移在同一个项目里是常量,很容易被忽略。稳妥做法是控制点导入时就把正常高写进去,检查点也做同样处理。
导入后要先在参考面板里看误差列,超过阈值的点先别急着优化,回头看是不是打错了点号或者照片刺点偏了。
2.2 对齐照片的精度与关键点上限怎么设
对齐是整条链路的地基。参数只有几个,但组合起来差别很大。
| 精度档位 | 关键点上限建议 | 连接点上限建议 | 典型场景 | 相对耗时 |
|---|---|---|---|---|
| Highest | 60000 | 10000 | 小范围、纹理弱(裸地、水面边) | 最高 |
| High | 40000 | 4000 | 常规航测生产默认 | 高 |
| Medium | 20000 | 2000 | 大面积快速预览、查重叠 | 中 |
| Low | 8000 | 1000 | 只做定位排查 | 低 |
关键点上限决定每张影像提取多少特征点,连接点上限决定最终参与光束法平差的同名点数量。把关键点拉到 60000、连接点还是 4000,多出来的特征点大部分会被淘汰,白耗内存。反过来,纹理弱的区域(比如大面积水泥地、水面边缘)把连接点压到 1000 以下,平差很容易发散。
通用预选(generic preselection)一定要开,参考预选(reference preselection)在 POS 可靠的时候也开,能大幅缩短匹配时间。对齐完先看两个指标:一是每张照片的投影误差,二是相机估计参数里的焦距残差。有明显离群的照片直接禁用。
2.3 深度图质量与滤波:密集点云的成色在这一步决定
比较新的版本把密集点云拆成了两步:先算深度图,再从深度图重建点云。质量档位本质上是对原始影像的降采样倍数。
| 质量档位 | 影像降采样 | 内存占用 | 适用场景 |
|---|---|---|---|
| Ultra | 原图 | 极高 | 小面积、精细建模 |
| High | 1/4 | 高 | 常规 DEM 生产 |
| Medium | 1/16 | 中 | 大范围快速出图 |
| Low | 1/64 | 低 | 只做预览 |
滤波参数决定深度图里那些低置信度的匹配点要不要留下。Aggressive 最干净,会把植被边缘、屋檐、电线这类飞点抹掉,代价是细节也少一点;Moderate 是常用折中;Mild 保留更多细节但噪声多;Disabled 基本等于不滤,只在特殊需求下用。
import PhotoScan # 较新版本改为 import Metashape as PhotoScan doc = PhotoScan.app.document chunk = doc.addChunk() # 导入照片,指定坐标系,控制点从参考文件读入 chunk.addPhotos(photo_list) chunk.crs = PhotoScan.CoordinateSystem("EPSG::4547") # CGCS2000 3度带 chunk.importReference(gcp_path, format=PhotoScan.ReferenceFormatCSV, columns="nxyz", delimiter=",") # 对齐:High 精度,开启两种预选,关键点 40000,连接点 4000 chunk.matchPhotos(accuracy=PhotoScan.HighAccuracy, generic_preselection=True, reference_preselection=True, keypoint_limit=40000, tiepoint_limit=4000) chunk.alignCameras() # 用控制点优化一次,把残差大的点剔掉后再优化 chunk.optimizeCameras(fit_f=True, fit_cx=True, fit_cy=True, fit_k1=True, fit_k2=True, fit_k3=True, fit_p1=True) # 深度图 + 密集点云 chunk.buildDepthMaps(quality=PhotoScan.HighQuality, filter=PhotoScan.ModerateFiltering, reuse_depth=False) chunk.buildDenseCloud(point_colors=True, keep_depth=True)buildDepthMaps里的quality和上面表格对应,filter建议从 Moderate 起步;reuse_depth=False表示每次重新算深度图,改了对齐结果之后必须重算,否则用的还是旧深度。buildDenseCloud的keep_depth=True会保留深度图,好处是后面改 DEM 参数不用重算点云,代价是项目文件体积翻倍。
2.4 从密集点云生成DEM:源、插值与边界
从点云到 DEM 是一次栅格化,参数里最影响成果的是插值方式和源数据选择。
# 用密集点云直接生成 DEM,插值只填内部小空洞 chunk.buildDem(source=PhotoScan.DenseCloudData, interpolation=PhotoScan.EnabledInterpolation, projection=chunk.crs, region=chunk.region, resolution=0.05) # 单位与投影一致,这里是 5cm chunk.exportDem("dem_raw.tif", format="TIFF", projection=chunk.crs, nodata=-9999, write_world=True, tiff_big=True)source有三个常见选项:DenseCloudData 直接用点云插值,成果最贴近地表细节;ModelData 从网格模型采样,速度快但受网格分辨率限制;ElevationData 是引用上一版 DEM,一般不用。interpolation的 Disabled 完全不填洞,出来的 DEM 会有大片 nodata,适合要做严格质量控制的项目;Enabled 填内部空洞但保留外边界,是最常用的;Extrapolated 会把边界也外推一段,不推荐用于面积量算。resolution不要设得比点云平均间距还小,否则插值出来的栅格看着密,实际全是假的。
3. Photoscan正射影像生产与DOM镶嵌的落地参数
3.1 surface 用网格还是用DEM:正射投影的两种底座
正射影像的本质是按每一个像素的平面位置,反算它在哪张照片上,再把颜色取回来。反算需要高程,这个高程从一个表面模型上读。Photoscan 提供两种表面:网格模型(Mesh)和 DEM(ElevationData)。
用网格的好处是能表达竖直面,桥梁、挡墙、建筑立面不会出现投影错位;代价是网格本身可能包含悬空错误面,正射上会出现莫名其妙的色块。用 DEM 做底,平面地形最干净,山区或高差大的地方要注意 DEM 分辨率要够,否则陡坡上每个像素的高程都偏一点,正射会糊。
经验判断:面积量算、平地土方、矿山平面图,用 DEM 做底就够;要出建筑立面专题或者城区高密度建模,用网格。选择在buildOrthomosaic的surface参数里指定。
3.2 颜色校正与镶嵌模式:让接边看不出来
大面积正射最难看的不是精度,是块与块之间的色差。Photoscan 有三个层次的校正:影像级别的颜色平衡(color_correction)、单张调的调色(调色板里的曝光/白平衡)、以及镶嵌时的混合方式。
# 先建网格(用 DEM 做正射的话这一步可以跳过) chunk.buildModel(surface=PhotoScan.HeightField, source=PhotoScan.DenseCloudData, interpolation=PhotoScan.EnabledInterpolation, face_count=PhotoScan.HighFaceCount) # 生成正射,底面用 DEM,镶嵌用 Mosaic chunk.buildOrthomosaic(surface=PhotoScan.ElevationData, blending=PhotoScan.MosaicBlending, color_correction=True, seam_marker=False, fill_holes=True, projection=chunk.crs)color_correction=True会在整个数据集上做一次全局色调统一,处理前必须保证每张影像的曝光参数没有大起大落,否则校正结果会偏。blending有三个值:Mosaic 是默认的按拼接线硬拼接,边界清晰、速度快;Average 在重叠区做加权平均,色差更小但边缘容易发虚;Natural 是较新的混合策略,过渡更自然,耗时也更高。城市正射建议试 Average,地形正射用 Mosaic 就够。
seam_marker=True会把拼接线画成可见的标记,用来查找接边错位的位置,正式出图时关掉。
3.3 导出GeoTIFF分块与nodata设置
正射导出最常见的翻车是文件过大打不开,或者坐标信息丢了。Photoscan 导出 GeoTIFF 有两种方式:不带 world file(地理信息内嵌在 TIFF 的 GeoTIFF tag 里),和带 world file(额外生成 .tfw 和 .prj)。主流的 GIS 两种都认,内嵌的更省事。
chunk.exportOrthomosaic( "dom.tif", format="TIF", projection=chunk.crs, tiled=True, # 分块存储,便于 GIS 逐块读取 blockw=4096, # 块宽像素 blockh=4096, # 块高像素 compression="LZW", # 无损压缩,一般能压到 40%~60% bigtiff=True, # 超过 4GB 时启用 write_world=True, # 同时输出 .tfw / .prj raster_transform=PhotoScan.RasterTransformNone )LZW 是无损压缩,正射影像用它是安全选择,别为了体积用 JPEG 压缩,边界会出现块状伪影。bigtiff=True在单文件超过 4GB 时必开,否则写到一半会报错。分块大小 4096 是个平衡点,太小产生大量冗余,太大某些 GIS 加载时会卡。
如果正射里有空洞(被遮挡、水域、超出重叠区),导出时可以通过PhotoScan.RasterTransformNone和后续 GDAL 处理来统一填 -9999,别让空洞变成 0,因为 0 在影像里是纯黑,进 GIS 还会被当成有效值参与统计。
3.4 大区域分批处理与接边控制
一个测区超过 3000 张照片时,一次性跑完整链路对内存要求非常高。常见做法是按航带或者按网格分块,每块单独建 chunk 跑出 DEM 和正射,再用 GDAL 拼接。分块的接边位置尽量选在重叠充分、地形平缓的地方,沿着道路或地类边界切,视觉上更自然。
分块处理有一个坑:块与块的坐标系统、分辨率必须完全一致,否则拼接后会有错位和重采样痕迹。做法是把每一块的resolution参数写成同一个固定值,导出时投影只写 EPSG 代码不带任何基准变换,拼接时用 VRT 而不要逐块重采样。
4. 从DSM到DEM:Photoscan成果的后处理与精度验证
4.1 密集点云分类与DSM转DEM
Photoscan 直接从密集点云生成的其实是最佳地表模型(DSM),它包含树冠、房屋、车辆这些地表附着物。要拿到真正意义上只含裸地的 DEM(数字高程模型,有时也叫 DTM),必须做点云分类或者滤波。
工程上两条路。一条是在 Photscan 里用「分类地面点」功能,基于几何和颜色做粗略分类,再生成 DEM,速度快但分类精度一般,适合植被稀疏的区域。另一条是把密集点云导成 LAS,用 PDAL 或 CloudCompare 做精细滤波,尤其是 Progressive Morphological Filter 和 Cloth Simulation Filter(CSF),在有林地的区域效果明显好于软件内置分类。
# 导出密集点云为 LAS # chunk.exportPoints("dense.las", format=PhotoScan.PointsFormatLAS) # 用 CSF 布料模拟滤波提取地面点(需 PDAL 加载 CSF 插件) pdal translate dense.las ground_csf.las \ --filter.csf \ --filters.csf.resolution=2.0 \ --filters.csf.threshold=0.5 \ --filters.csf.rigidness=false # 或者用渐进形态学滤波 pdal translate dense.las ground_smrf.las \ --filter.smrf \ --filters.smrf.scalar=1.25 \ --filters.smrf.slope=0.15 \ --filters.smrf.window=18filters.csf.resolution是布料网格间距,跟地形起伏幅度相关,平地取 0.5 到 1,丘陵取 2 到 5。threshold是点到布料的距离阈值,超过就判为非地面,通常 0.3 到 0.8。filters.smrf.window是形态学运算的窗口尺寸,必须大于最大建筑物的尺寸,否则楼房会被误判成地形。
分类完的地面点再栅格化成 DEM,插值用 IDW 或 TIN 线性插值都行,跟之前一样注意分辨率不要小于点间距。
4.2 GDAL后处理:填洞、重采样、分割与镶嵌
DEM 出来之后,真正花时间的往往是后处理。常见需求有四个:补空洞、统一分辨率、按范围分割、多块拼接。
# 1. 填洞:对 nodata 区域做插值填补,-md 是最大填补距离,-si 是迭代次数 gdal_fillnodata.py -md 30 -si 3 -o dem_fill.tif dem_raw.tif # 2. 重采样到统一网格,同时统一 nodata 值 gdalwarp -tr 0.1 0.1 -r bilinear -dstnodata -9999 \ -t_srs EPSG:4547 dem_fill.tif dem_01m.tif # 3. 按矢量范围分割:GIS 里做 DEM 分割最稳的方式就是 cutline gdalwarp -cutline boundary.shp -crop_to_cutline \ -dstnodata -9999 -co COMPRESS=DEFLATE -co TILED=YES \ dem_01m.tif dem_clip.tif # 4. 多块拼接:先建虚拟栅格,再一次性写出 gdalbuildvrt mosaic.vrt tile_*.tif gdal_translate -co COMPRESS=DEFLATE -co TILED=YES -co BIGTIFF=IF_SAFER \ mosaic.vrt dem_mosaic.tifgdal_fillnodata.py的填洞是插值,不是真实观测,用之前先确认这些空洞是边缘裁剪导致还是数据缺失。范围裁剪时用矢量边界切,比在 QGIS 里按范围出图更稳,因为 gdalwarp 会统一处理坐标变换和像素对齐。拼接时先建 VRT 再转实体文件,比直接 gdal_merge 省内存,也不容易在块数多的时候报错。
如果要做水文分析,DEM 里的洼地会导致流向计算失败,需要单独做填洼处理,那是另一套流程,不要和上面的 nodata 填补混用。
4.3 用检查点做精度验证:RMSE怎么算、多少算合格
精度验证的核心概念是:用于平差的控制点不能再用来评估精度,必须留一批没参与解算的点作为检查点。通常按控制点总数的 1/3 到 1/2 留作检查点,分布要覆盖测区四角和中心。
验证分平面和高程两部分。平面用检查点的 X、Y 残差算 RMSE,高程用 Z 残差算。计算方法:
# 用检查点的高程和 DEM 采样高程做差 gdal_calc.py -A dem_final.tif -B check_dem.tif \ --calc="A-B" --outfile=diff.tif --NoDataValue=-9999 # 统计差值的均值和标准差 gdalinfo -stats diff.tif再看统计结果里的 Mean 和 StdDev。Mean 反映系统偏差,如果明显不为零,多半是高程基准没统一或者控制点本身有问题;StdDev 反映随机误差,是精度评估的主角。
合格标准没有统一答案,看项目要求。1:500 地形图对高程精度的要求一般在 0.15 米以内,1:2000 可以放宽到 0.5 米左右。实际生产中,RTK 控制的航测项目在裸地条件下高程 RMSE 做到 5 到 10 厘米是常见的;有植被覆盖的区域会明显变差,因为点云返回的是树冠高度。
| 检查内容 | 数据来源 | 判断依据 | 超限时的排查方向 |
|---|---|---|---|
| 平面精度 | 检查点 X/Y 残差 | 与成图比例尺对应 | 坐标系统、GCP 刺点 |
| 高程系统偏差 | 差值 Mean | 应接近零 | 高程基准、高程异常改正 |
| 高程随机误差 | 差值 StdDev | 项目规范要求 | 对齐质量、深度图质量 |
| 点云完整性 | 空洞比例 | 无大面积缺失 | 重叠率、拍摄角度 |
5. 把Photoscan一次成功的参数固化成批处理脚本
跑通一个项目之后,最有价值的动作是把参数固化成脚本,而不是靠记忆重复点菜单。Photoscan 的 Python API 在这里比界面好用得多,因为参数可以被审查、被版本管理、被批量套用。
import Metashape as ms # 老版本写 import PhotoScan as ms import glob, os PARAMS = { "match_accuracy": ms.HighAccuracy, "keypoint_limit": 40000, "tiepoint_limit": 4000, "depth_quality": ms.HighQuality, "depth_filter": ms.ModerateFiltering, "dem_resolution": 0.05, "crs": "EPSG::4547", } def run_one(photo_dir, out_dir): doc = ms.Document() chunk = doc.addChunk() chunk.addPhotos(sorted(glob.glob(os.path.join(photo_dir, "*.JPG")))) chunk.crs = ms.CoordinateSystem(PARAMS["crs"]) chunk.matchPhotos(accuracy=PARAMS["match_accuracy"], generic_preselection=True, reference_preselection=True, keypoint_limit=PARAMS["keypoint_limit"], tiepoint_limit=PARAMS["tiepoint_limit"]) chunk.alignCameras() chunk.buildDepthMaps(quality=PARAMS["depth_quality"], filter=PARAMS["depth_filter"]) chunk.buildDenseCloud(point_colors=True) chunk.buildDem(source=ms.DenseCloudData, interpolation=ms.EnabledInterpolation, projection=chunk.crs, resolution=PARAMS["dem_resolution"]) chunk.buildOrthomosaic(surface=ms.ElevationData, blending=ms.MosaicBlending, color_correction=True, projection=chunk.crs) name = os.path.basename(photo_dir.rstrip("/")) chunk.exportDem(os.path.join(out_dir, name + "_dem.tif"), projection=chunk.crs, nodata=-9999, write_world=True) chunk.exportOrthomosaic(os.path.join(out_dir, name + "_dom.tif"), format="TIF", tiled=True, blockw=4096, blockh=4096, compression="LZW", bigtiff=True, write_world=True) doc.save(os.path.join(out_dir, name + ".psx")) doc.clear()把参数抽成字典的好处是:同一批项目里,只有dem_resolution和crs需要按项目改,其余保持常量,成果的可比性就有了保证。注意doc.clear()要放在最后,否则内存会一直累积,跑第三个项目就开始吃交换分区。
5.1 三个容易漏掉的复核动作
一是导出后用gdalinfo看一遍坐标和 nodata。gdalinfo dem_final.tif输出里的Origin、Pixel Size、NoData Value三项,只要有一项不对,后面所有分析都是白做。这一步比在 GIS 里打开看要快得多,也更容易批量执行。
二是把检查点残差和 DEM 差值图叠在一起看。别只看 RMSE 一个数字,残差的空间分布能告诉你问题在哪:如果误差集中在测区边缘,多半是重叠率不够或者边缘控制点太少;如果集中在某个方向呈线性变化,大概率是坐标系统或者高程基准的问题。
三是正射影像和 DEM 做一次配准自检。把正射叠加到 DEM 生成的晕渲图上,看建筑物轮廓、道路边线有没有明显错位。这个动作能同时暴露正射投影用错表面、DEM 空洞、以及分块接边位置三类问题,比单独检查每一项快。接边处如果出现固定宽度的条带差异,优先去查分块时的投影和分辨率参数是否完全一致。
本文还有配套的精品资源,点击获取