A. Points in Segments

本文介绍了一个算法问题:给定一系列区间,如何找出这些区间未覆盖的所有整数点。通过使用C++实现的示例代码,展示了如何初始化数组、标记区间内的点,并最终找出所有未被区间覆盖的点。

A. Points in Segments

time limit per test

1 second

memory limit per test

256 megabytes

input

standard input

output

standard output

You are given a set of nn segments on the axis OxOx, each segment has integer endpoints between 11 and mm inclusive. Segments may intersect, overlap or even coincide with each other. Each segment is characterized by two integers lili and riri (1≤li≤ri≤m1≤li≤ri≤m) — coordinates of the left and of the right endpoints.

Consider all integer points between 11 and mm inclusive. Your task is to print all such points that don't belong to any segment. The point xxbelongs to the segment [l;r][l;r] if and only if l≤x≤rl≤x≤r.

Input

The first line of the input contains two integers nn and mm (1≤n,m≤1001≤n,m≤100) — the number of segments and the upper bound for coordinates.

The next nn lines contain two integers each lili and riri (1≤li≤ri≤m1≤li≤ri≤m) — the endpoints of the ii-th segment. Segments may intersect, overlap or even coincide with each other. Note, it is possible that li=rili=ri, i.e. a segment can degenerate to a point.

Output

In the first line print one integer kk — the number of points that don't belong to any segment.

In the second line print exactly kk integers in any order — the points that don't belong to any segment. All points you print should be distinct.

If there are no such points at all, print a single integer 00 in the first line and either leave the second line empty or do not print it at all.

Examples

input

Copy

3 5
2 2
1 2
5 5

output

Copy

2
3 4 

input

Copy

1 7
1 7

output

Copy

0

Note

In the first example the point 11 belongs to the second segment, the point 22 belongs to the first and the second segments and the point 55belongs to the third segment. The points 33 and 44 do not belong to any segment.

In the second example all the points from 11 to 77 belong to the first segment.

代码:

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    int a[105];
    fill(a,a+105,0);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        fill(a+x,a+y+1,1);
    }
    int ans=0;
    for(int i=1;i<=m;++i)
    {
        if(a[i]==0) ans++;
    }
    cout<<ans<<endl;
    for(int i=1;i<=m;++i)
    {
        if(a[i]==0) cout<<i<<" ";
    }
    return 0;
}

 

import open3d as o3d import numpy as np import time import random from collections import deque import multiprocessing as mp import sys import traceback import ctypes def region_growing_segmentation(pcd, radius=0.02): """ 区域生长分割算法 :param pcd: 输入点云 :param radius: 邻域搜索半径(米) :return: 分割后的区域列表(每个区域是点索引列表) """ print("开始区域生长分割...") points = np.asarray(pcd.points) n_points = len(points) kdtree = o3d.geometry.KDTreeFlann(pcd) visited = np.zeros(n_points, dtype=bool) segments = [] for idx in range(n_points): if visited[idx]: continue # 创建新区域 segment = [] queue = deque([idx]) visited[idx] = True while queue: current_idx = queue.popleft() segment.append(current_idx) # 搜索半径内的邻域点 [_, idxs, _] = kdtree.search_radius_vector_3d(pcd.points[current_idx], radius) for n_idx in idxs: if not visited[n_idx]: visited[n_idx] = True queue.append(n_idx) segments.append(segment) print(f"分割完成,共 {len(segments)} 个区域") return segments def assign_random_colors(pcd, segments): """ 为每个区域分配随机颜色 :param pcd: 点云对象 :param segments: 分割区域列表 """ colors = np.zeros((len(pcd.points), 3)) for segment in segments: color = [random.random(), random.random(), random.random()] for idx in segment: colors[idx] = color pcd.colors = o3d.utility.Vector3dVector(colors) def process_segment(args): """ 处理单个区域的函数,用于多进程执行 :param args: 包含(区域索引, 区域点索引列表, 共享数组, 点云形状, 法向量半径, 计数半径)的元组 :return: (区域索引, 有效点标记数组, 是否移除整个区域标志, 处理时间, 错误信息) """ seg_idx, segment, shared_array, points_shape, radius_normal, radius_count = args start_time = time.time() error = None try: # 从共享数组重建点云数据 flat_array = np.frombuffer(shared_array.get_obj(), dtype=np.float64) all_points = flat_array.reshape(points_shape) # 创建点云对象 pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(all_points) # 创建KDTree kdtree = o3d.geometry.KDTreeFlann(pcd) # 创建该区域的点云 segment_pcd = pcd.select_by_index(segment) # 下采样 voxel_size = radius_normal / 2 down_pcd = segment_pcd.voxel_down_sample(voxel_size) if down_pcd is None or len(down_pcd.points) < 10: # 点数太少,跳过重建 valid_segment_points = np.ones(len(segment), dtype=bool) remove_entire_segment = len(segment) < 100 end_time = time.time() return seg_idx, segment, valid_segment_points, remove_entire_segment, end_time - start_time, error # 估计法向量 down_pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid( radius=radius_normal*2, max_nn=30)) # 泊松重建 - 使用更稳定的参数 if len(down_pcd.points) > 50: # 确保有足够点进行重建 try: with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug): mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson( down_pcd, depth=8, linear_fit=True, scale=1.1) except Exception as e: mesh = o3d.geometry.TriangleMesh() error = f"泊松重建失败: {str(e)}" else: mesh = o3d.geometry.TriangleMesh() # 计算区域平均法向量A2 segment_normals = np.asarray(down_pcd.normals) A2 = np.mean(segment_normals, axis=0) if np.linalg.norm(A2) > 1e-6: A2 /= np.linalg.norm(A2) # 步骤4: 计算每个点的平均法向量A1并过滤 valid_segment_points = np.ones(len(segment), dtype=bool) for j, orig_idx in enumerate(segment): # 找到该点在下采样后的点云中的对应点 [_, idxs, _] = kdtree.search_knn_vector_3d(pcd.points[orig_idx], 1) down_idx = idxs[0] if idxs else -1 # 如果找不到对应点,跳过 if down_idx == -1: continue # 获取以该点为顶点的所有三角形 triangles = [] if mesh.has_triangles(): for tri in mesh.triangles: if down_idx in tri: triangles.append(tri) if len(triangles) == 0: continue # 计算这些三角形的法向量平均值A1 tri_normals = [] mesh_vertices = np.asarray(mesh.vertices) for tri in triangles: # 三角形三个顶点 pts = [mesh_vertices[i] for i in tri] # 计算三角片法向量 v1 = pts[1] - pts[0] v2 = pts[2] - pts[0] normal = np.cross(v1, v2) norm_val = np.linalg.norm(normal) if norm_val > 1e-6: normal /= norm_val tri_normals.append(normal) if tri_normals: A1 = np.mean(tri_normals, axis=0) norm_val = np.linalg.norm(A1) if norm_val > 1e-6: A1 /= norm_val # 计算A1和A2的夹角(度) if np.linalg.norm(A1) > 1e-6 and np.linalg.norm(A2) > 1e-6: cos_angle = np.clip(np.dot(A1, A2), -1.0, 1.0) angle = np.degrees(np.arccos(cos_angle)) # 如果偏差大于10度,标记为无效点 if angle > 10: valid_segment_points[j] = False # 步骤5: 半径内点云计数过滤 for j, orig_idx in enumerate(segment): if not valid_segment_points[j]: continue [k, idxs, _] = kdtree.search_radius_vector_3d(pcd.points[orig_idx], radius_count) # 排除自身点 neighbor_count = k - 1 if k > 0 else 0 # 如果邻域点少于100个,标记为无效点 if neighbor_count < 100: valid_segment_points[j] = False # 步骤6: 检查是否移除整个区域 remove_entire_segment = np.sum(valid_segment_points) < 100 end_time = time.time() process_time = end_time - start_time return seg_idx, segment, valid_segment_points, remove_entire_segment, process_time, error except Exception as e: # 捕获并记录异常 error = f"区域 {seg_idx} 处理错误: {str(e)}\n{traceback.format_exc()}" valid_segment_points = np.ones(len(segment), dtype=bool) remove_entire_segment = False end_time = time.time() return seg_idx, segment, valid_segment_points, remove_entire_segment, end_time - start_time, error def process_segments(pcd, segments, radius_normal=0.1, radius_count=0.1, max_workers=4): """ 多进程处理点云区域 :param pcd: 点云对象 :param segments: 分割区域列表 :param radius_normal: 法向量计算半径(米) :param radius_count: 点云计数半径(米) :param max_workers: 最大进程数 :return: 处理后的点云 """ print(f"开始多进程处理区域,共 {len(segments)} 个区域,使用 {max_workers} 个进程...") # 准备点云数据 points = np.asarray(pcd.points, dtype=np.float64) # 创建共享数组 shared_array = mp.Array(ctypes.c_double, points.size) # 将点云数据复制到共享数组 shared_np_array = np.frombuffer(shared_array.get_obj(), dtype=np.float64) shared_np_array[:] = points.flatten() # 创建有效点标记数组 valid_points = np.ones(len(pcd.points), dtype=bool) # 准备任务参数 tasks = [] for i, segment in enumerate(segments): tasks.append(( i, segment, shared_array, points.shape, radius_normal, radius_count )) # 用于存储结果的列表 results = [None] * len(segments) # 使用进程池并行处理 with mp.Pool(processes=max_workers) as pool: # 提交所有任务 futures = [pool.apply_async(process_segment, (task,)) for task in tasks] # 处理完成的任务 for i, future in enumerate(futures): try: result = future.get() seg_idx, segment, valid_segment_points, remove_entire_segment, process_time, error = result if error: print(error) # 存储结果 results[seg_idx] = (segment, valid_segment_points, remove_entire_segment, process_time) print(f"区域 {seg_idx+1}/{len(segments)} 处理完成,用时: {process_time:.2f}秒") except Exception as e: print(f"区域 {i} 结果获取失败: {str(e)}") results[i] = (segments[i], np.ones(len(segments[i]), dtype=bool), False, 0) # 处理所有结果 for seg_idx, result in enumerate(results): if result is None: continue segment, valid_segment_points, remove_entire_segment, _ = result if remove_entire_segment: # 移除整个区域 for idx in segment: valid_points[idx] = False print(f"区域 {seg_idx+1} 点数不足,已移除") else: # 更新全局有效点标记 for j, orig_idx in enumerate(segment): if not valid_segment_points[j]: valid_points[orig_idx] = False # 创建有效点的点云 valid_indices = np.where(valid_points)[0] result_pcd = pcd.select_by_index(valid_indices) print(f"处理完成,剩余点数: {len(result_pcd.points)}") return result_pcd def main(): # 步骤1: 读取点云文件 file_path = r"D:\chenle\ms\2.pcd" print(f"加载点云文件: {file_path}") pcd = o3d.io.read_point_cloud(file_path) if not pcd.has_points(): print("错误: 无法读取点云文件") return print(f"原始点云点数: {len(pcd.points)}") # 步骤2: 分区域处理 print("开始区域分割...") segments = region_growing_segmentation(pcd, radius=0.02) assign_random_colors(pcd, segments) # 步骤7: 迭代处理三次 for i in range(3): print(f"\n=== 开始第 {i+1} 次迭代 ===") # 使用CPU核心数一半的进程数,避免内存溢出 worker_count = min(4, max(1, mp.cpu_count() // 2)) pcd = process_segments(pcd, segments, max_workers=worker_count) segments = region_growing_segmentation(pcd) # 重新分割 # 步骤9: 保存结果 output_path = r"D:\chenle\ms\processed.pcd" o3d.io.write_point_cloud(output_path, pcd) print(f"结果已保存至: {output_path}") if __name__ == "__main__": # 设置多进程启动方法 mp.set_start_method('spawn', force=True) main() 出现区域 123 结果获取失败: SynchronizedArray objects should only be shared between processes through inheritance错误,更改,并给出完整解决后的代码
最新发布
11-19
<think> 我们遇到了一个错误:区域 123 结果获取失败: SynchronizedArray objects should only be shared between processes through inheritance 这个错误是因为我们在使用`mp.Array`创建的共享数组时,在子进程中通过`apply_async`传递了共享数组对象,而`mp.Array`不支持通过参数传递(pickle)到子进程,只能通过继承(即在创建子进程时就已经存在)的方式共享。 解决方案: 1. 使用`mp.Manager`来创建一个可以在进程间传递的共享对象。 2. 或者,将共享数组作为全局变量(但多进程中全局变量不共享),这不太可行。 3. 另一种方法是使用`mp.Array`,但将其作为初始化参数传递给进程池(使用`initializer`和`initargs`),这样每个子进程都会继承这个共享数组。 这里我们选择第3种方法,因为它更高效。我们将共享数组作为全局变量传递给每个子进程,然后在子进程中直接使用这个全局变量。 具体步骤: 1. 在创建进程池之前,将共享数组设置为一个全局变量(使用模块级的全局变量)。 2. 使用进程池的`initializer`参数,在子进程启动时将共享数组赋值给一个全局变量。 3. 修改`process_segment`函数,使其从全局变量中获取共享数组。 但是,由于我们每个任务需要处理不同的区域,并且需要传递区域索引等参数,我们仍然需要传递区域索引、区域点索引列表等。而共享数组通过全局方式访问。 注意:由于每个子进程需要独立访问全局共享数组,我们使用`init_worker`初始化函数来设置全局变量。 另一种更简单的方法:由于共享数组的大小是固定的,我们可以将其创建放在主进程中,然后通过`init_worker`将其复制到子进程的全局变量中。但是,共享数组本身是继承的,所以不需要复制,只需要在子进程中访问同一个共享数组。 修改计划: 1. 定义一个全局变量`global_shared_array`和`global_points_shape`,用于在子进程中存储共享数组和点云形状。 2. 创建一个初始化函数`init_worker`,它将主进程的共享数组和点云形状赋给子进程的全局变量。 3. 修改`process_segment`函数,不再从参数中获取共享数组,而是从全局变量中获取。 4. 修改任务参数,不再传递共享数组和点云形状(但需要传递区域索引、区域点索引、法向量半径、计数半径)。 5. 在创建进程池时,使用`initializer`和`initargs`来传递共享数组和点云形状。 注意:共享数组是通过继承方式传递的,所以不会出现pickle错误。 我们重新组织代码: 在`process_segments`函数中: - 创建共享数组(和之前一样) - 定义全局变量(在模块级别不行,因为是在函数内,所以使用子进程的全局变量) - 编写初始化函数`init_worker(shared_array, points_shape)` - 将共享数组和点云形状作为`initargs`传递给进程池 同时,修改`process_segment`函数,使其参数不再包含共享数组和点云形状。 由于我们使用`initializer`,每个子进程启动时都会调用`init_worker`,将共享数组和点云形状存储在该子进程的全局变量中。 这样,在`process_segment`内部,就可以直接使用这些全局变量。 但是,由于在Windows系统中,多进程使用`spawn`方式启动,子进程会重新导入模块,因此我们需要注意全局变量的定义。 我们将在`process_segments`函数内部定义`init_worker`函数,这样它就可以访问`process_segments`的局部变量(通过闭包),但多进程中无法序列化闭包,所以我们通过参数传递。 因此,我们这样定义: ```python # 在process_segments函数内部 def init_worker(shared_array, points_shape): # 在子进程中设置全局变量 global global_shared_array, global_points_shape global_shared_array = shared_array global_points_shape = points_shape ``` 然后,在创建进程池时: ```python with mp.Pool(processes=max_workers, initializer=init_worker, initargs=(shared_array, points.shape)) as pool: # 提交任务 ``` 同时,修改任务参数,现在每个任务只包含:`(seg_idx, segment, radius_normal, radius_count)` 修改`process_segment`函数,使其参数为:`(seg_idx, segment, radius_normal, radius_count)` 在`process_segment`函数内部,从全局变量`global_shared_array`和`global_points_shape`中获取数据。 注意:由于我们使用`spawn`启动方法,子进程会导入模块,因此我们需要在模块级别定义全局变量(或者至少在子进程的全局作用域中定义)。所以我们在模块顶部定义: ```python global_shared_array = None global_points_shape = None ``` 这样,每个子进程在初始化时,将主进程传递过来的共享数组和点云形状赋值给这两个全局变量。 完整代码修改如下: </think>这个错误是由于 `multiprocessing.Array` 对象无法直接通过进程池传递导致的。我将修复这个问题,使用更健壮的进程间通信方法。以下是完整的解决方案: ```python import open3d as o3d import numpy as np import time import random from collections import deque import multiprocessing as mp import sys import traceback import ctypes import os import tempfile def region_growing_segmentation(pcd, radius=0.02): """ 区域生长分割算法 :param pcd: 输入点云 :param radius: 邻域搜索半径(米) :return: 分割后的区域列表(每个区域是点索引列表) """ print("开始区域生长分割...") points = np.asarray(pcd.points) n_points = len(points) kdtree = o3d.geometry.KDTreeFlann(pcd) visited = np.zeros(n_points, dtype=bool) segments = [] for idx in range(n_points): if visited[idx]: continue # 创建新区域 segment = [] queue = deque([idx]) visited[idx] = True while queue: current_idx = queue.popleft() segment.append(current_idx) # 搜索半径内的邻域点 [_, idxs, _] = kdtree.search_radius_vector_3d(pcd.points[current_idx], radius) for n_idx in idxs: if not visited[n_idx]: visited[n_idx] = True queue.append(n_idx) segments.append(segment) print(f"分割完成,共 {len(segments)} 个区域") return segments def assign_random_colors(pcd, segments): """ 为每个区域分配随机颜色 :param pcd: 点云对象 :param segments: 分割区域列表 """ colors = np.zeros((len(pcd.points), 3)) for segment in segments: color = [random.random(), random.random(), random.random()] for idx in segment: colors[idx] = color pcd.colors = o3d.utility.Vector3dVector(colors) def process_segment(args): """ 处理单个区域的函数,用于多进程执行 :param args: 包含(区域索引, 区域点索引列表, 点云文件路径, 法向量半径, 计数半径)的元组 :return: (区域索引, 有效点标记数组, 是否移除整个区域标志, 处理时间, 错误信息) """ seg_idx, segment, pcd_file_path, radius_normal, radius_count = args start_time = time.time() error = None try: # 从文件加载点云 pcd = o3d.io.read_point_cloud(pcd_file_path) if not pcd.has_points(): error = f"无法加载点云文件: {pcd_file_path}" valid_segment_points = np.ones(len(segment), dtype=bool) remove_entire_segment = False end_time = time.time() return seg_idx, segment, valid_segment_points, remove_entire_segment, end_time - start_time, error # 创建KDTree kdtree = o3d.geometry.KDTreeFlann(pcd) # 创建该区域的点云 segment_pcd = pcd.select_by_index(segment) # 下采样 voxel_size = radius_normal / 2 down_pcd = segment_pcd.voxel_down_sample(voxel_size) if down_pcd is None or len(down_pcd.points) < 10: # 点数太少,跳过重建 valid_segment_points = np.ones(len(segment), dtype=bool) remove_entire_segment = len(segment) < 100 end_time = time.time() return seg_idx, segment, valid_segment_points, remove_entire_segment, end_time - start_time, error # 估计法向量 down_pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid( radius=radius_normal*2, max_nn=30)) # 泊松重建 - 使用更稳定的参数 if len(down_pcd.points) > 50: # 确保有足够点进行重建 try: with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug): mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson( down_pcd, depth=8, linear_fit=True, scale=1.1) except Exception as e: mesh = o3d.geometry.TriangleMesh() error = f"泊松重建失败: {str(e)}" else: mesh = o3d.geometry.TriangleMesh() # 计算区域平均法向量A2 segment_normals = np.asarray(down_pcd.normals) A2 = np.mean(segment_normals, axis=0) if np.linalg.norm(A2) > 1e-6: A2 /= np.linalg.norm(A2) # 步骤4: 计算每个点的平均法向量A1并过滤 valid_segment_points = np.ones(len(segment), dtype=bool) for j, orig_idx in enumerate(segment): # 找到该点在下采样后的点云中的对应点 [_, idxs, _] = kdtree.search_knn_vector_3d(pcd.points[orig_idx], 1) down_idx = idxs[0] if idxs else -1 # 如果找不到对应点,跳过 if down_idx == -1: continue # 获取以该点为顶点的所有三角形 triangles = [] if mesh.has_triangles(): for tri in mesh.triangles: if down_idx in tri: triangles.append(tri) if len(triangles) == 0: continue # 计算这些三角形的法向量平均值A1 tri_normals = [] mesh_vertices = np.asarray(mesh.vertices) for tri in triangles: # 三角形三个顶点 pts = [mesh_vertices[i] for i in tri] # 计算三角片法向量 v1 = pts[1] - pts[0] v2 = pts[2] - pts[0] normal = np.cross(v1, v2) norm_val = np.linalg.norm(normal) if norm_val > 1e-6: normal /= norm_val tri_normals.append(normal) if tri_normals: A1 = np.mean(tri_normals, axis=0) norm_val = np.linalg.norm(A1) if norm_val > 1e-6: A1 /= norm_val # 计算A1和A2的夹角(度) if np.linalg.norm(A1) > 1e-6 and np.linalg.norm(A2) > 1e-6: cos_angle = np.clip(np.dot(A1, A2), -1.0, 1.0) angle = np.degrees(np.arccos(cos_angle)) # 如果偏差大于10度,标记为无效点 if angle > 10: valid_segment_points[j] = False # 步骤5: 半径内点云计数过滤 for j, orig_idx in enumerate(segment): if not valid_segment_points[j]: continue [k, idxs, _] = kdtree.search_radius_vector_3d(pcd.points[orig_idx], radius_count) # 排除自身点 neighbor_count = k - 1 if k > 0 else 0 # 如果邻域点少于100个,标记为无效点 if neighbor_count < 100: valid_segment_points[j] = False # 步骤6: 检查是否移除整个区域 remove_entire_segment = np.sum(valid_segment_points) < 100 end_time = time.time() process_time = end_time - start_time return seg_idx, segment, valid_segment_points, remove_entire_segment, process_time, error except Exception as e: # 捕获并记录异常 error = f"区域 {seg_idx} 处理错误: {str(e)}\n{traceback.format_exc()}" valid_segment_points = np.ones(len(segment), dtype=bool) remove_entire_segment = False end_time = time.time() return seg_idx, segment, valid_segment_points, remove_entire_segment, end_time - start_time, error finally: # 清理临时文件 if os.path.exists(pcd_file_path): os.remove(pcd_file_path) def process_segments(pcd, segments, radius_normal=0.1, radius_count=0.1, max_workers=4): """ 多进程处理点云区域 :param pcd: 点云对象 :param segments: 分割区域列表 :param radius_normal: 法向量计算半径(米) :param radius_count: 点云计数半径(米) :param max_workers: 最大进程数 :return: 处理后的点云 """ print(f"开始多进程处理区域,共 {len(segments)} 个区域,使用 {max_workers} 个进程...") # 创建有效点标记数组 valid_points = np.ones(len(pcd.points), dtype=bool) # 准备任务参数 tasks = [] temp_files = [] # 保存点云到临时文件 temp_dir = tempfile.mkdtemp() pcd_file_path = os.path.join(temp_dir, "temp_pcd.pcd") o3d.io.write_point_cloud(pcd_file_path, pcd) for i, segment in enumerate(segments): # 创建临时文件副本 temp_file_path = os.path.join(temp_dir, f"temp_pcd_{i}.pcd") os.link(pcd_file_path, temp_file_path) temp_files.append(temp_file_path) tasks.append(( i, segment, temp_file_path, radius_normal, radius_count )) # 用于存储结果的列表 results = [None] * len(segments) # 使用进程池并行处理 with mp.Pool(processes=max_workers) as pool: # 提交所有任务 futures = [pool.apply_async(process_segment, (task,)) for task in tasks] # 处理完成的任务 for i, future in enumerate(futures): try: result = future.get() seg_idx, segment, valid_segment_points, remove_entire_segment, process_time, error = result if error: print(error) # 存储结果 results[seg_idx] = (segment, valid_segment_points, remove_entire_segment, process_time) print(f"区域 {seg_idx+1}/{len(segments)} 处理完成,用时: {process_time:.2f}秒") except Exception as e: print(f"区域 {i} 结果获取失败: {str(e)}") results[i] = (segments[i], np.ones(len(segments[i]), dtype=bool), False, 0) # 清理临时文件 for file_path in temp_files: if os.path.exists(file_path): os.remove(file_path) if os.path.exists(pcd_file_path): os.remove(pcd_file_path) os.rmdir(temp_dir) # 处理所有结果 for seg_idx, result in enumerate(results): if result is None: continue segment, valid_segment_points, remove_entire_segment, _ = result if remove_entire_segment: # 移除整个区域 for idx in segment: valid_points[idx] = False print(f"区域 {seg_idx+1} 点数不足,已移除") else: # 更新全局有效点标记 for j, orig_idx in enumerate(segment): if not valid_segment_points[j]: valid_points[orig_idx] = False # 创建有效点的点云 valid_indices = np.where(valid_points)[0] result_pcd = pcd.select_by_index(valid_indices) print(f"处理完成,剩余点数: {len(result_pcd.points)}") return result_pcd def main(): # 步骤1: 读取点云文件 file_path = r"D:\chenle\ms\2.pcd" print(f"加载点云文件: {file_path}") pcd = o3d.io.read_point_cloud(file_path) if not pcd.has_points(): print("错误: 无法读取点云文件") return print(f"原始点云点数: {len(pcd.points)}") # 步骤2: 分区域处理 print("开始区域分割...") segments = region_growing_segmentation(pcd, radius=0.02) assign_random_colors(pcd, segments) # 步骤7: 迭代处理三次 for i in range(3): print(f"\n=== 开始第 {i+1} 次迭代 ===") # 使用CPU核心数一半的进程数,避免内存溢出 worker_count = min(4, max(1, mp.cpu_count() // 2)) pcd = process_segments(pcd, segments, max_workers=worker_count) segments = region_growing_segmentation(pcd) # 重新分割 # 步骤9: 保存结果 output_path = r"D:\chenle\ms\processed.pcd" o3d.io.write_point_cloud(output_path, pcd) print(f"结果已保存至: {output_path}") if __name__ == "__main__": # 设置多进程启动方法 mp.set_start_method('spawn', force=True) main() ``` ### 主要修复点: 1. **使用临时文件替代共享内存**: ```python # 保存点云到临时文件 temp_dir = tempfile.mkdtemp() pcd_file_path = os.path.join(temp_dir, "temp_pcd.pcd") o3d.io.write_point_cloud(pcd_file_path, pcd) # 每个任务使用独立的临时文件 temp_file_path = os.path.join(temp_dir, f"temp_pcd_{i}.pcd") os.link(pcd_file_path, temp_file_path) ``` 使用文件系统而不是共享内存,避免了进程间通信问题。 2. **进程间通信优化**: ```python tasks.append(( i, segment, temp_file_path, # 传递文件路径而不是共享内存对象 radius_normal, radius_count )) ``` 传递文件路径字符串而不是复杂的共享内存对象。 3. **在子进程中加载点云**: ```python # 从文件加载点云 pcd = o3d.io.read_point_cloud(pcd_file_path) ``` 每个子进程独立加载点云数据,避免了共享内存问题。 4. **临时文件管理**: ```python finally: # 清理临时文件 if os.path.exists(pcd_file_path): os.remove(pcd_file_path) ``` 添加了完善的临时文件清理机制,防止文件泄漏。 5. **高效文件复制**: ```python # 创建硬链接而不是复制文件内容 os.link(pcd_file_path, temp_file_path) ``` 使用硬链接创建文件副本,避免实际复制点云数据,节省时间和空间。 6. **资源清理**: ```python # 清理临时文件 for file_path in temp_files: if os.path.exists(file_path): os.remove(file_path) if os.path.exists(pcd_file_path): os.remove(pcd_file_path) os.rmdir(temp_dir) ``` 在主进程中清理所有临时资源。 ### 关键改进说明: 1. **文件系统替代共享内存**: - 使用临时文件存储点云数据 - 通过文件路径传递数据位置 - 子进程独立加载点云数据 2. **高效文件管理**: ```python # 创建主点云文件 pcd_file_path = os.path.join(temp_dir, "temp_pcd.pcd") o3d.io.write_point_cloud(pcd_file_path, pcd) # 为每个任务创建硬链接 os.link(pcd_file_path, temp_file_path) ``` 使用硬链接避免实际数据复制,节省磁盘空间和I/O时间。 3. **健壮的错误处理**: ```python try: # 处理逻辑 except Exception as e: # 错误处理 finally: # 清理资源 ``` 确保即使在出错的情况下也能清理临时文件。 4. **资源优化**: - 使用临时目录集中管理所有临时文件 - 主进程负责整体清理 - 子进程只负责清理自己的临时文件 ### 使用建议: 1. **性能考虑**: - 如果点云很大,确保临时目录所在的磁盘有足够空间 - 使用SSD磁盘可以显著提高文件读写性能 2. **错误处理**: - 如果遇到文件权限问题,检查临时目录的写入权限 - 添加日志记录文件操作,便于调试 3. **内存优化**: ```python # 处理大型点云时添加内存检查 if sys.getsizeof(pcd.points) > 1e9: # 大于1GB worker_count = min(2, worker_count) # 减少工作进程数 ``` 添加内存检查,防止处理大型点云时内存不足 这个解决方案避免了共享内存问题,使用文件系统作为进程间通信的媒介,同时保持了代码的功能和性能。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值