前言
无人机或机载 LiDAR 常交出整测区 LAS/LAZ,甲方只要地块、红线或行政区以内的点。桌面软件框选可以交差,但批次一多就难复现、难审计。把流程写成 PDAL pipeline JSON,把「读点云 → 对齐坐标系 → 按多边形裁剪 → 写出 LAZ」固化下来,适合挂日批或质检脚本。
本文面向已有 LAS/LAZ、会一点命令行的测绘 / GIS 同学,按工程顺序讲三件事:CRS 对齐、filters.crop 裁剪、空洞与边界抽检 。思路上接近正射 DOM 的 gdalwarp -cutline,但对象是离散三维点:没有「半个像元」,坑主要在投影、边界拓扑和裁完后的密度空洞。
关键词:点云、LAS、LAZ、PDAL、shp 裁剪、坐标系、filters.crop、密度检查
环境与输入约定
建议环境:
- PDAL 2.x(
pdal --version确认) - GDAL/OGR(
ogr2ogr、ogrinfo,用来处理 shp / GeoJSON) - 范围面:Polygon 或 MultiPolygon(
.shp或 GeoJSON) - 点云:
.las/.laz,最好自带 SRS 元数据
先摸清家底,避免一上来就 crop:
bash
pdal info input.laz --metadata | head -n 80
pdal info input.laz --summary
ogrinfo -al -so aoi.shp
重点看:点云 srs / comp_spatialreference、XY 的 bounds、点数 count;shp 的 Layer SRS 与 Extent。若一边是经纬度、一边是投影米制,裁完经常是「点数直接变 0」或「偏了几十上百米还以为成功」。
先对齐坐标系,再谈裁剪
实践里裁剪失败有一半不是多边形坏了,而是 点云与 shp 不在同一 CRS。处理原则:
- 优先把矢量重投影到点云 CRS(通常比整库点云重投影便宜)。
- EPSG 号一致还不够时,核对 WKT 是否「同名不同参」(地方独立坐标尤甚)。
- 点云完全没有 SRS:先补元数据或明确约定,再裁;不要假设「数字看起来像高斯投影就是某带」。
示例(EPSG 请换成 pdal info 读到的真实值):
bash
ogr2ogr -t_srs EPSG:4547 aoi_4547.shp aoi.shp
gdalsrsinfo -o wkt aoi_4547.prj | head
若必须把点云变到甲方指定投影,再在 pipeline 里加 filters.reprojection(注意:重投影会改 XY,大文件耗时与误差传递都要评估):
json
{
"type": "filters.reprojection",
"out_srs": "EPSG:4547"
}
裁剪前用同一单位对比范围:把 pdal info 的 minx/maxx/miny/maxy 与 ogrinfo 的 Extent 画在纸上或 QGIS 里叠一次,确认 AOI 与点云外包确实相交。
最小可跑通的 PDAL pipeline
filters.crop 可接受 bounds 矩形,也可按多边形裁。红线 / 地块交付应用多边形;矩形只适合粗提速预裁。
工程上较稳的做法:先把已对齐 CRS 的 shp 转成 GeoJSON,再交给 crop(避免 shp 组件路径、编码在服务器上的差异)。
bash
ogr2ogr -f GeoJSON -t_srs EPSG:4547 aoi_4547.json aoi.shp
clip_las.json 示例:
json
{
"pipeline": [
{
"type": "readers.las",
"filename": "input.laz"
},
{
"type": "filters.crop",
"filename": "aoi_4547.json"
},
{
"type": "writers.las",
"filename": "clipped.laz",
"compression": "laszip"
}
]
}
执行与快速验收:
bash
pdal pipeline clip_las.json
pdal info clipped.laz --summary
正常时:count 应明显小于输入;输出 XY bounds 应落在 AOI 外包矩形附近(凹多边形内部镂空除外)。若 count 为 0,立刻回头查 CRS 与 Extent,而不是先怀疑 LAZ 损坏。
多地块、多测区
- 多要素 shp:默认往往是「落在任一多边形内的点都保留」。若合同是「一地块一文件」,先按 FID / 地块号拆矢量,再循环生成 pipeline。
- 超大测区 :可先按瓦片
bounds预裁,再精细 polygon crop,降低单次内存峰值。 - 只保留某分类 :在 crop 前后按需加
filters.range(例如Classification[2:2]只要地面点),但要在说明书写清,避免甲方以为丢了建筑点。
边界、维度与性能上的常见坑
| 现象 | 常见原因 | 处理方向 |
|---|---|---|
| 裁完点数为 0 | CRS 错位、度/米混用、AOI 与点云不相交 | 同单位对比 bounds;重投影矢量后再裁 |
| 边界缺一圈点 | 面自交、未做缓冲、点落在边界外判定 | 拓扑修复;必要时对 AOI 微缓冲半个点间距 |
| 分类 / RGB / GPSTime 丢了 | reader/writer 维度未保留、中间 filter 过严 | 对照 pdal info 维度列表;少加无关 filter |
| 很慢或临时盘暴涨 | 未压缩写出、一次读入过大 | writer 开 laszip;分块 crop 再合并 |
| 叠 DOM 对不齐 | 点云与影像未用同一最终 AOI / CRS | 统一 AOI 版本与 EPSG,再分别裁影像与点云 |
仅做矩形预裁时,可用 crop 的 bounds 形式(形如 ([xmin,xmax],[ymin,ymax]))。最终按合同红线交付时,仍应以多边形结果为准。
空洞与覆盖抽检:裁成功 ≠ 密度合格
pipeline 跑通只说明「范围内还有点」,不说明「测区采全了」。交付前建议固定三步抽检。
1)点数与平均密度
用 AOI 面积(平方米)做粗算:
平均密度 ≈ count / area
对照设计航线或合同指标(例如 ≥ 16 pts/m²)。整体偏低,优先查航高、航向重叠、有效回波,而不是反复改 crop 参数。
2)密度栅格看空洞
把点打成 count / 密度栅格,空洞比看三维更直观。示意 pipeline(分辨率按点间距调整,例如 1 m):
json
{
"pipeline": [
{ "type": "readers.las", "filename": "clipped.laz" },
{
"type": "writers.gdal",
"filename": "density.tif",
"resolution": 1.0,
"output_type": "count",
"data_type": "uint16"
}
]
}
在 QGIS 中叠 AOI:内部成片 0 值或 NoData,属于采集 / 匹配缺口;crop 解决不了,需要补飞或在报告里标缺口区。
3)内缩缓冲抽点
对 AOI 做小幅负缓冲(约 1~2 个点间距)后再 crop 一次。若内缩后点数骤降、外圈几乎无有效点,说明成果贴边稀释,应在元数据或说明书写明有效范围,避免验收时按「满幅均匀密度」卡死。
和 DOM 裁剪怎么分工
| 对象 | 常用工具 | 结果含义 |
|---|---|---|
| 正射 DOM(栅格) | gdalwarp -cutline |
像元保留或变 NoData/透明 |
| LiDAR 点云 | PDAL filters.crop |
点保留或丢弃,无半个点 |
同一项目里,DOM 与点云应使用同一套已对齐的 AOI,并在版本号或文件名里写清日期,避免影像已按新红线裁、点云仍按旧框,叠合时出现「边上一窄条对不上」。
批处理骨架(多 AOI)
测区拆成多个地块时,不必手改 JSON。可以用一段短脚本按目录生成 pipeline 再依次执行(示意):
python
from pathlib import Path
import json, subprocess
laz = Path("input.laz")
for aoi in Path("aoi_json").glob("*.json"):
out = Path("out") / f"{aoi.stem}.laz"
pipe = {
"pipeline": [
{"type": "readers.las", "filename": str(laz)},
{"type": "filters.crop", "filename": str(aoi)},
{"type": "writers.las", "filename": str(out), "compression": "laszip"},
]
}
p = Path("pipes") / f"{aoi.stem}.json"
p.parent.mkdir(exist_ok=True)
out.parent.mkdir(exist_ok=True)
p.write_text(json.dumps(pipe, ensure_ascii=False, indent=2), encoding="utf-8")
subprocess.check_call(["pdal", "pipeline", str(p)])
跑批后建议把每个输出的 count、bounds 写成 CSV,和地块面积一并入库,后面做密度抽检时直接关联,避免只在口头说「好像都裁过了」。
对已经裁好的结果做抽检清单也可以固化:同名 density GeoTIFF、平均密度字段、是否存在内部空洞标记。这样甲方或质检同事不必再打开三维软件逐块点选。
小结
- 先用
pdal info/ogrinfo对齐 CRS 并确认范围相交,再写filters.crop。 - pipeline JSON 便于复现与批处理;一地块一文件时先拆矢量。
- 裁完看 count/bounds,再用密度栅格区分「裁切问题」与「采集空洞」。
你在用 PDAL 按 shp 裁 LAZ 时,最近一次点数异常(变 0 或密度骤降)最终是怎样定位到原因的?