跳转到主要内容

点云数据处理

项目描述

PDAL Python支持允许您使用PDAL将数据处理成Numpy数组。它提供了一个PDAL扩展模块来控制Python与PDAL的交互。此外,您还可以使用它从PDAL操作中获取模式元数据

安装

注意: PDAL Python绑定需要已安装的PDAL基础库。源代码可在https://pdal.ioGitHub找到。

PyPI

PDAL Python支持可通过PyPI安装

pip install PDAL

GitHub

PDAL Python扩展的仓库可在https://github.com/PDAL/python找到

从PDAL 1.7版本开始,Python支持独立于PDAL本身发布。

用法

简单

以下是一个简单的管道示例,该管道简单地读取一个ASPRS LAS文件并按X维度对其进行排序

json = """
{
  "pipeline": [
    "1.2-with-color.las",
    {
        "type": "filters.sort",
        "dimension": "X"
    }
  ]
}"""

import pdal
pipeline = pdal.Pipeline(json)
count = pipeline.execute()
arrays = pipeline.arrays
metadata = pipeline.metadata
log = pipeline.log

程序化管道构建

上一个示例指定了管道为JSON字符串。另外,可以通过创建Stage实例并将它们管道连接起来来构建管道。例如,上一个管道可以指定为

pipeline = pdal.Reader("1.2-with-color.las") | pdal.Filter.sort(dimension="X")

Stage对象

  • 一个阶段是pdal.Readerpdal.Filterpdal.Writer的实例。

  • 可以通过传递适用于相应PDAL阶段的选项作为关键字参数来实例化一个阶段。有关PDAL阶段及其选项的更多信息,请参阅Stage对象的PDAL文档。

    • 对于ReadersWritersfilename选项以及Filterstype选项,可以作为第一个参数按位置传递。

    • inputs选项指定了一个阶段序列,这些阶段将被设置为当前阶段的输入。每个输入可以是另一个阶段的字符串标签,或者Stage实例本身。

  • ReaderFilterWriter类为所有相应的PDAL驱动程序提供了静态方法。例如,pdal.Filter.head()pdal.Filter(type="filters.head")的快捷方式。这些方法是通过检查pdal和可用的选项自动生成的,每个方法的docstring中都包含了这些选项。

>>> help(pdal.Filter.head)
Help on function head in module pdal.pipeline:

head(**kwargs)
    Return N points from beginning of the point cloud.

    user_data: User JSON
    log: Debug output filename
    option_file: File from which to read additional options
    where: Expression describing points to be passed to this filter
    where_merge='auto': If 'where' option is set, describes how skipped points should be merged with kept points in standard mode.
    count='10': Number of points to return from beginning.  If 'invert' is true, number of points to drop from the beginning.
    invert='false': If true, 'count' specifies the number of points to skip from the beginning.

管道对象

可以从以下内容创建一个pdal.Pipeline实例:

  • 一个JSON字符串:Pipeline(json_string)

  • 一系列Stage实例:Pipeline([stage1, stage2])

  • 一个单个Stage使用Stage.pipeline方法:stage.pipeline()

  • 无:Pipeline()创建一个没有任何阶段的管道。

  • 使用管道操作符(|)将Stage和/或其他Pipeline实例连接起来

    • stage1 | stage2

    • stage1 | pipeline1

    • pipeline1 | stage1

    • pipeline1 | pipeline2

每次应用管道操作符都会创建一个新的Pipeline实例。要更新现有的Pipeline,请使用相应的就地管道操作符(|=

# update pipeline in-place
pipeline = pdal.Pipeline()
pipeline |= stage
pipeline |= pipeline2

使用Numpy数组读取

以下更复杂的场景演示了PDAL和Python之间完整的循环

  • 从GitHub读取一个小测试文件到Numpy数组

  • 使用Numpy过滤数组以获取强度

  • 将过滤后的数组传递给PDAL再次进行过滤

  • 使用TileDB-PDAL集成TileDB writer插件将最终的过滤数组写入LAS文件和一个TileDB数组

import pdal

data = "https://github.com/PDAL/PDAL/blob/master/test/data/las/1.2-with-color.las?raw=true"

pipeline = pdal.Reader.las(filename=data).pipeline()
print(pipeline.execute())  # 1065 points

# Get the data from the first array
# [array([(637012.24, 849028.31, 431.66, 143, 1,
# 1, 1, 0, 1,  -9., 132, 7326, 245380.78254963,  68,  77,  88),
# dtype=[('X', '<f8'), ('Y', '<f8'), ('Z', '<f8'), ('Intensity', '<u2'),
# ('ReturnNumber', 'u1'), ('NumberOfReturns', 'u1'), ('ScanDirectionFlag', 'u1'),
# ('EdgeOfFlightLine', 'u1'), ('Classification', 'u1'), ('ScanAngleRank', '<f4'),
# ('UserData', 'u1'), ('PointSourceId', '<u2'),
# ('GpsTime', '<f8'), ('Red', '<u2'), ('Green', '<u2'), ('Blue', '<u2')])
arr = pipeline.arrays[0]

# Filter out entries that have intensity < 50
intensity = arr[arr["Intensity"] > 30]
print(len(intensity))  # 704 points

# Now use pdal to clamp points that have intensity 100 <= v < 300
pipeline = pdal.Filter.range(limits="Intensity[100:300)").pipeline(intensity)
print(pipeline.execute())  # 387 points
clamped = pipeline.arrays[0]

# Write our intensity data to a LAS file and a TileDB array. For TileDB it is
# recommended to use Hilbert ordering by default with geospatial point cloud data,
# which requires specifying a domain extent. This can be determined automatically
# from a stats filter that computes statistics about each dimension (min, max, etc.).
pipeline = pdal.Writer.las(
    filename="clamped.las",
    offset_x="auto",
    offset_y="auto",
    offset_z="auto",
    scale_x=0.01,
    scale_y=0.01,
    scale_z=0.01,
).pipeline(clamped)
pipeline |= pdal.Filter.stats() | pdal.Writer.tiledb(array_name="clamped")
print(pipeline.execute())  # 387 points

# Dump the TileDB array schema
import tiledb
with tiledb.open("clamped") as a:
    print(a.schema)

执行可流式传输的管道

可流式传输的管道(仅由可流式传输的PDAL阶段组成的管道)可以通过Pipeline.iterator()以流式模式执行。这返回一个迭代器对象,每次产生最多chunk_size大小(默认=10000)的Numpy数组。

import pdal
pipeline = pdal.Reader("test/data/autzen-utm.las") | pdal.Filter.range(limits="Intensity[80:120)")
for array in pipeline.iterator(chunk_size=500):
    print(len(array))
# or to concatenate all arrays into one
# full_array = np.concatenate(list(pipeline))

Pipeline.iterator() 还可以接受一个可选的 prefetch 参数(默认=0),允许并行预取多达这个数量的数组,并在返回给调用者之前进行缓冲。

如果您只想以流式模式执行可流式处理的流水线,并且不需要访问数据点(通常流水线包含 Writer 阶段时),可以使用 Pipeline.execute_streaming(chunk_size) 方法。这与 sum(map(len, pipeline.iterator(chunk_size))) 功能上等效,但更高效,因为它避免了在内存中分配和填充任何数组。

访问网格数据

一些 PDAL 阶段(例如 filters.delaunay)创建 TIN 类型的网格数据。

可以使用 Pipeline.meshes 属性在 Python 中访问这些数据,该属性返回一个形状为 (1,n) 的 numpy.ndarray,其中 n 是网格中的三角形数量。

如果 PointView 中不包含网格数据,则 n = 0。

每个三角形是一个元组 (A,B,C),其中 A、B 和 C 是指向 PointView 的索引,用于标识构成三角形的顶点。

Meshio 集成

meshes 属性提供了面数据,但不易作为网格使用。因此,我们提供了可选的 Meshio 库集成。

pdal.Pipeline 类提供了一个 get_meshio(idx: int) -> meshio.Mesh 方法。该方法从 PointView 数组和网格属性创建一个 Mesh 对象。

功能简单使用示例

import pdal

...
pl = pdal.Pipeline(pipeline)
pl.execute()

mesh = pl.get_meshio(0)
mesh.write('test.obj')

高级网格使用案例

USE-CASE:取一个激光雷达地图,从地面点创建网格,分割成瓦片,并将瓦片存储在 PostGIS 中。

(示例使用 1.2-with-color.las 并未进行地面分类以便清晰)

import pdal
import psycopg2
import io

pl = (
    pdal.Reader(".../python/test/data/1.2-with-color.las")
    | pdal.Filter.splitter(length=1000)
    | pdal.Filter.delaunay()
)
pl.execute()

conn = psycopg(%CONNNECTION_STRING%)
buffer = io.StringIO

for idx in range(len(pl.meshes)):
    m =  pl.get_meshio(idx)
    if m:
        m.write(buffer,  file_format = "wkt")
        with conn.cursor() as curr:
          curr.execute(
              "INSERT INTO %table-name% (mesh) VALUES (ST_GeomFromEWKT(%(ewkt)s)",
              { "ewkt": buffer.getvalue()}
          )

conn.commit()
conn.close()
buffer.close()
https://github.com/PDAL/python/workflows/Build/badge.svg

要求

  • PDAL 2.6+

  • Python >=3.9

  • Pybind11(例如 pip install pybind11[global]

  • Numpy >= 1.22(例如 pip install numpy

  • scikit-build-core(例如 pip install scikit-build-core

项目详情


下载文件

下载适用于您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源分发

pdal-3.4.5.tar.gz (89.5 kB 查看哈希值)

上传时间

由以下支持