Python实战点云并行化处理

LAS 及其压缩版本 LAZ 是用于存储点云信息的流行文件格式,通常由 LiDAR 技术生成。 LiDAR(即光探测和测距)是一种遥感技术,用于测量距离并创建物体和景观的高精度 3D 地图。存储的点云信息主要包括X、Y、Z坐标、强度、颜色、特征分类、GPS时间以及扫描仪提供的其他自定义字段。 LAS 文件包含数百万个点,可以准确描述感知的环境或物体,这使得其分析成为一项具有挑战性的任务。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割

处理和分析 3D 数据的基本步骤之一是计算法线。点云中的法线提供有关点云中每个点处的表面方向和方向的信息。这些信息对于可视化、对象识别和形状分析至关重要。

我们不会深入研究如何计算这些法线或使用哪个包的细节。相反,本文的重点是演示如何在对 LAS/LAZ 文件进行分块读取和分块写入时执行并行计算,以及 Python 如何应对并发和并行性的挑战。

要继续学习,你应该对 Python 有一定的了解,并熟悉 numpy 和 laspy。本文对 Python 中的并行性进行了高级概述。

[packages]
numpy = "==1.26.0"
laspy = {extras = ["lazrs"], version = "==2.5.1"}

[requires]
python_version = "3.10"

laspy 和 numpy 都是直接与 Python C_API 交互的包,使得它们速度极快。如果不采用直接 C 编程,速度方面没有太大的改进空间。因此,我们需要探索使用代码的新方法,以实现并行性或增强处理管道,以充分利用机器的潜力。

你可能知道也可能不知道,Python 的执行受到全局解释器锁 (GIL) 的限制。 GIL 是 CPython 解释器使用的一种机制,用于确保一次只有一个线程执行 Python 字节码。这简化了实现并使 CPython 的对象模型能够安全地防止并发访问。

虽然 GIL 为多线程程序和单核、单进程性能提供了简单性和优势,但它提出了一些问题:如果多个线程不能同时执行,为什么要使用多线程?是否可以与Python并行执行代码?

多线程是一种使 Python 非阻塞的方法,允许我们创建同时启动多个任务的代码,即使在任何给定时刻只能执行一个任务。当调用外部 API 或数据库(您大部分时间都在等待)时,这种类型的并发非常有用。然而,对于 CPU 密集型任务,这种方法有局限性。

为了并行运行 Python 代码,多处理库使用操作系统 API 调用在不同内核上生成单独的进程。

spawn 是 MacOS 和 Windows 中的默认方法。它创建子进程,继承运行对象的 run() 方法所需的资源。尽管比其他方法(如 fork)慢,但它提供了一致的执行。

fork 是除 MacOS 之外的所有 POSIX 系统中的默认方法。它使用父进程的所有上下文和资源创建子进程。它比 spawn更快,但在多进程和多线程环境中可能会遇到问题。

这种方法允许我们为每个处理器提供一个新的 Python 解释器,从而消除了多个线程争夺解释器可用性的问题。

鉴于点云处理严重依赖 CPU 性能,我们采用多处理来并行执行正在读取的点云的每个块的进程。

为了读取大型 LAS/LAZ 文件, laspy 提供了 chunk_iterator,用于读取数据块中的点云,这些数据块可以发送到不同的进程进行处理。随后,处理后的数据被组装并按块写回到另一个文件中。为了实现这一目标,我们需要两个上下文管理器:一个用于读取输入文件,另一个用于写入输出文件。

你通常会这样做:

import laspy
import numpy as np

# reading the file
with laspy.open(input_file_name, mode="r") as f:
    # creating a file
    with laspy.open(output_file_name, mode="w", header=header) as o_f:
        # iteration over the chunk iterator
        for chunk in f.chunk_iterator(chunk_size):
            # Normals calculation over each chunk
            point_record = calculate_normals(chunk)
            # writting or appending the data into the point cloud
            o_f.append_points(point_record)

为了并行化这个过程,我们创建了一个 ProcessPoolExecutor,它允许我们将函数的每次执行(计算法线的地方)发送到一个单独的进程。过程完成后,我们收集结果并将其写入新的 LAS/LAZ 文件。

由于我们在主进程中收集 future 的结果,然后将它们写入文件,因此我们避免了多个进程同时访问同一文件的问题。如果你的实现不允许这种方法,可能需要使用锁来确保数据完整性。

import laspy
import numpy as np
import concurrent.futures

# reading the file
with laspy.open(input_file_name, mode="r") as f:
    # creating an output file
    with laspy.open(output_file_name, mode="w", header=f.header) as o_f:
        # this is where we are going to collect our future objects
        futures = []
        with concurrent.futures.ProcessPoolExecutor() as executor:
            # iteration over the chunk iterator
            for chunk in f.chunk_iterator(chunk_size):
                # disecting the chunk into the points that conform it
                points: np.ndarray = np.array(
                    (
                        (chunk.x + f.header.offsets[0])/f.header.scales[0],
                        (chunk.y + f.header.offsets[1])/f.header.scales[1],
                        (chunk.z + f.header.offsets[2])/f.header.scales[2],
                    )
                ).T
                # calculate the normals  in a multi processing pool
                future = executor.submit(
                    process_points,   # function where we calculate the normals
                    points=points,
                )
                futures.append(future)
        # awaiting all the future to complete in case we needed
        for future in concurrent.futures.as_completed(futures):
            # unpacking the result from the future
            result = future.result()
            # creating a point record to store the results
            point_record = laspy.PackedPointRecord.empty(
                point_format=f.header.point_format
            )
            # appending information to that point record
            point_record.array = np.append(
                point_record.array,
                result
            )
            # appending the point record into the point cloud
            o_f.append_points(point_record)

这段代码中有很多东西需要展开解释,比如为什么我们不使用块对象本身?为什么我们要创建一个空的 PackedPointRecord

我们将从块对象开始。如果不触及原因,对象本身就无法发送到进程池中进行处理。因此,我们必须从中提取我们认为重要的信息。因为我们正在计算法线,所以我们需要的是块的 X、Y 和 Z 坐标,同时考虑到 LAS/LAZ 文件头中指定的偏移和比例。

鉴于计算返回一个值数组,该数组将表示 X、Y 和 Z 坐标、RGB 值、强度和分类,我们不能将其直接写入 LAS/LAZ 文件,我们需要创建一个 PackedPointRecord使用标头中指定的格式,我们将在其中存储返回的数组,然后将它们附加到 LAS/LAZ 文件中。

LAS/LAZ 文件有一个标头对象,我们在其中存储点云的比例、偏移和格式。这很重要,因为为了我们能够将信息发送到该文件,我们的值的格式必须与标头中指定的格式匹配。在我们的例子中,两个文件具有相同的标头格式。但是,如果你需要写入不同版本的文件,则数组格式必须与你要写入的版本匹配。

要确定能够将结果附加到 PackedPointRecord 中所需的格式,你可以运行以下命令:

>>> f.header.point_format.dtype()

在此示例中,我们使用 Point Format版本 3,其结构如下:

np.dtype([
    ('X', np.int32),
    ('Y', np.int32),
    ('Z', np.int32),
    ('intensity', np.int16),
    ('bit_fields', np.uint8),
    ('raw_classification', np.uint8),
    ('scan_angle_rank', np.uint8),
    ('user_data', np.uint8),
    ('point_source_id', np.int16),
    ('gps_time', np.float64),
    ('red', np.int16),
    ('green', np.int16),
    ('blue', np.int16),
])

因为我们无法使用此命令来将解压后的 future 的 dtype 与 header 的 dtype 进行匹配。

>>> result = result.astype(header.point_format.dtype())

我们必须按以下方式进行转换:

def process_points(
    points: np.ndarray,
) -> np.ndarray:
    # normals calculation
    normals, curvature, density = calculate_normals(points=points)
    # RGB
    red, green, blue = 255 * (np.abs(normals)).T
    dtype = np.dtype([
        ('X', np.int32),
        ('Y', np.int32),
        ('Z', np.int32),
        ('intensity', np.int16),
        ('bit_fields', np.uint8),
        ('raw_classification', np.uint8),
        ('scan_angle_rank', np.uint8),
        ('user_data', np.uint8),
        ('point_source_id', np.int16),
        ('gps_time', np.float64),
        ('red', np.int16),
        ('green', np.int16),
        ('blue', np.int16),
    ])
    array = np.zeros(len(points), dtype=dtype)
    array['X'] = points[:, 0]
    array['Y'] = points[:, 1]
    array['Z'] = points[:, 2]
    array['intensity'] = density
    array['bit_fields'] = np.zeros(len(points), dtype=np.uint8)
    array['raw_classification'] = curvature
    array['scan_angle_rank'] = np.zeros(len(points), dtype=np.uint8)
    array['user_data'] = np.zeros(len(points), dtype=np.uint8)
    array['point_source_id'] = np.zeros(len(points), dtype=np.int16)
    array['gps_time'] = np.zeros(len(points), dtype=np.float64)
    array['red'] = red
    array['green'] = green
    array['blue'] = blue

    return np.ascontiguousarray(array)

将所有这些放在一起,我们就能够使用计算机的所有资源并行处理大型点云。

尽管需要非常熟悉上述软件包才能理解和应用上面的代码,但我们的想法是解决我们在处理点云时遇到的常见问题之一,并分享我们找到的解决方案对于我们的问题。


原文链接:用Python并行化处理点云 - BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/601813.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

接入大量设备后,视频汇聚系统EasyCVR安防监控视频融合平台是如何实现负载均衡的?

一、负载均衡 随着技术的不断进步和监控需求的日益增长,企业视频监控系统的规模也在不断扩大,接入大量监控设备已成为一项常态化的挑战。为确保企业能够有效应对这一挑战,视频汇聚系统EasyCVR视频融合平台凭借其卓越的高并发处理能力&#x…

c++11 标准模板(STL)本地化库 - 平面类别(std::numpunct) - 定义数值标点规则

本地化库 本地环境设施包含字符分类和字符串校对、数值、货币及日期/时间格式化和分析&#xff0c;以及消息取得的国际化支持。本地环境设置控制流 I/O 、正则表达式库和 C 标准库的其他组件的行为。 平面类别 定义数值标点规则 std::numpunct template< class CharT >…

浅谈程序员的实用神器

作为一个程序员&#xff0c;有很多实用的工具和资源可以帮助我们提高工作效率和解决问题。以下是一些常用的程序员实用神器&#xff1a; 集成开发环境&#xff08;IDE&#xff09;&#xff1a;如Visual Studio Code、PyCharm、Eclipse等&#xff0c;提供代码编辑、调试、版本控…

APP 在华为应用市场上架 保姆级别详细流程

1、作为一名干开发的程序员&#xff0c;第一次能把自己的APP 上架&#xff0c;对自己来说是多么有意义的一项成就 2、创建一个 华为的开发者账号 根据提示填写完注册的信息https://developer.huawei.com/consumer/cn/product/华为开发者产品 | 开发者平台 | 流量变现 | 华为开…

虾皮商品详情页面的API接口与关键词搜索API接口

虾皮商品详情页面的API接口与关键词搜索API接口 在虾皮&#xff08;Shopee&#xff09;这样的电商平台中&#xff0c;API&#xff08;应用程序接口&#xff09;扮演着至关重要的角色&#xff0c;它们为开发者提供了与虾皮平台数据交互的桥梁。本文将详细介绍虾皮商品详情页面的…

GreatSQL的sp中添加新的sp_instr引入的bug解析

GreatSQL的sp中添加新的sp_instr引入的bug解析 一、问题发现 在一次开发中用到的sp需要添加新的sp_instr以满足需求&#xff0c;但是添加了数个sp_instr以后发现执行新的sp会发生core。 注&#xff1a;本次使用的GreatSQL 8.0.32-25 1、sp_head.cc的init_sp_psi_keys()代码里…

阿贝云免费服务器推荐

由于近期毕设需要&#xff0c;需要将毕设上传到云服务器&#xff0c;届时答辩方便展示&#xff0c;找了很多云服务器提供商&#xff0c;发现都是需要抢或者其他要求的&#xff0c;无意中从一个知乎帖子中发现了阿贝云&#xff0c;号称永久免费&#xff0c;兴冲冲的开始免费适用…

网络补充笔记

目录 OSI 开放式系统互联参考模型 --- 7层参考模型 UDP&#xff1a;用户数据报文协议 --- 非面向不可靠的传输协议&#xff1b;传输层基本协议&#xff0c;仅完成传输层的基本工作 --- 分段、端口号 TCP&#xff1a;传输控制协议 --- 面向连接的可靠性传输协议 出了完成传输层…

Express初体验

介绍 Express是一个基于Node.js平台的极简、灵活的Web应用开发框架&#xff0c;官方地址&#xff1a;https://www.expressjs.com.cn/&#xff0c;简单来说&#xff0c;Express是一个封装好的工具包&#xff0c;封装了很多功能&#xff0c;便于我们开发Web应用&#xff08;HTTP…

提升Go语言数学运算能力:math包使用指南

提升Go语言数学运算能力&#xff1a;math包使用指南 介绍数学函数的使用基本数学运算幂和根的计算三角函数对数计算 特殊数学常数和函数数学常数超越数学函数错误处理和精度问题 高级应用实例统计数据的标准偏差计算利用三角函数解决实际问题 性能优化技巧避免不必要的函数调用…

机器学习——4.案例: 简单线性回归求解

案例目的 寻找一个良好的函数表达式,该函数表达式能够很好的描述上面数据点的分布&#xff0c;即对上面数据点进行拟合。 求解逻辑步骤 使用Sklearn生成数据集定义线性模型定义损失函数定义优化器定义模型训练方法&#xff08;正向传播、计算损失、反向传播、梯度清空&#…

计算机系列之数据结构

19、数据结构&#xff08;重点、考点&#xff09; 1、线性结构 线性结构&#xff1a;每个元素最多只有一个出度和一个入读&#xff0c;表现为一条线状。线性表按存储方式分为顺序表和链表。 1、顺序存储和链式存储 存储结构&#xff1a; 顺序存储&#xff1a;用一组地址连续…

【功耗仪使用】

一&#xff0c;功耗仪使用 1.1&#xff0c;功耗仪外观及与手机&#xff0c;电脑连接方式 power monitor设备图 同时power monitor设备的后端有一个方形插孔通过数据线与电脑主机USB接口相连接&#xff0c;圆形插孔为电源插孔&#xff0c;用来给power monitor设备通电 pow…

图算法必备指南:《图算法:行业应用与实践》全面解读,解锁主流图算法奥秘!

《图算法&#xff1a;行业应用与实践》于近日正式与读者见面了&#xff01; 该书详解6大类20余种经典的图算法的原理、复杂度、参数及应用&#xff0c;旨在帮助读者在分析和处理各种复杂的数据关系时能更好地得其法、善其事、尽其能。 全书共分为10章&#xff1a; 第1~3章主要…

FFmpeg 音视频处理工具三剑客(ffmpeg、ffprobe、ffplay)

【导读】FFmpeg 是一个完整的跨平台音视频解决方案&#xff0c;它可以用于音频和视频的转码、转封装、转推流、录制、流化处理等应用场景。FFmpeg 在音视频领域享有盛誉&#xff0c;号称音视频界的瑞士军刀。同时&#xff0c;FFmpeg 有三大利器是我们应该清楚的&#xff0c;它们…

idea Maven 插件 项目多环境打包配置

背景 不同环境的配置文件不一样&#xff0c;打包方式也有差异 1. 准备配置文件 这里 local 为本地开发环境 可改为 dev 名称自定义 test 为测试环境 prod 为生产环境 根据项目业务自行定义 application.yml 配置&#xff1a; spring:profiles:#对应pom中的配置active: spring.…

二分图(染色法与匈牙利算法)

二分图当且仅当一个图中不含奇数环 1.染色法 简单来说&#xff0c;将顶点分成两类&#xff0c;边只存在于不同类顶点之间&#xff0c;同类顶点之间没有边。 e.g. 如果判断一个图是不是二分图&#xff1f; 开始对任意一未染色的顶点染色。 判断其相邻的顶点中&#xff0c;若未…

打造文旅客运标杆!吐鲁番国宾旅汽携苏州金龙升级国宾级出行体验

新疆&#xff0c;这片神秘的大地&#xff0c;从无垠沙漠到高耸天山&#xff0c;从古老丝路到繁华都市&#xff0c;处处都散发着独特的魅力&#xff0c;吸引着四面八方的游客。据新疆维吾尔自治区文化和旅游厅数据显示&#xff0c;刚刚过去的“五一”小长假&#xff0c;新疆全区…

5月白银现货最新行情走势

美联储5月的议息会议举行在即&#xff0c;但从联邦公开市场委员会&#xff08;FOMC&#xff09;近期透露的信息来看&#xff0c;降息似乎并没有迫切性。——美联储理事鲍曼认为通胀存在"上行风险"&#xff0c;明尼阿波利斯联邦储备银行行长卡什卡利提出了今年不降息的…

算法学习:数组 vs 链表

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f3af; 引言&#x1f6e0;️ 内存基础什么是内存❓内存的工作原理 &#x1f3af; &#x1f4e6; 数组&#xff08;Array&#xff09;&#x1f4d6; 什么是数组&#x1f300; 数组的存储&#x1f4dd; 示例代码&#…