Executor 、future学习(一)

本文详细解析了Java的Executor框架,包括其组成部分:任务、任务执行和异步计算结果。介绍了Executor、ExecutorService、ScheduledExecutorService等接口的功能和使用,以及ThreadPoolExecutor的核心参数和自定义线程池的方法。

Executor 

executor包括三个部分

1.任务,即工作单元,包括被执行的任务需要实现的接口:runnable货callabe接口

2.任务的执行,把任务分配给多个线程执行的机制,包括Executor接口及继承自Executor接口的ExecutorService接口

3.异步计算的结果,包括Future及实现了Future接口的FutureTask类

Executor

一个接口,定义了一个接受runnable对象的方法execute(Runnable command),接受一个runnable实例,用来执行一个任务,任务为一个实现了Runnable接口的类,在Executor中,可以不显示的创建线程executor.execute(new MyRunnable)来异步执行

ExecutorService

是一个比Executor更广泛的子类接口,继承Executor接口,提供生命周期管理方法:

shutDown():关闭当前service,释放Executor的所有资源,触发的动作是取消消息队列中任务的执行,不再接受新的任务提交,同时把已经提交到队列的任务执行完成

shutDownNow:它将会把尚未执行的任务不再执行,正在执行的任务,通过“线程中断”(thread.interrupt),如果线程无法响应“中断”,那么将不会通过此方式被立即结束,该方法具有返回值,返回等待执行的任务列表List<Runnable>

isShutDown:程序是否关闭

isTerminated:是否已经结束,如果关闭后,所有的任务都执行完成,返回true,其他情况均为false

awaitTermination(timeout):等待一段之间直到“任务全部结束”,如果超时就返回false

Future submit(callable/runnale):向Executor提交任务,并返回一个结果未定的Future

List<Future> invokeAll(Collection<Callable>):同步的方法,执行所有的任务列表,当所有任务都执行完成后,返回Future列表

T invokeAny(Collection<Callable>): 任务集合中,任何一个任务完成就返回

ScheduledExecutorService

设计为了支持时间可控的任务执行:固定延时执行,周期延时执行,核心方法:

schedule(Callable<V> callable, long delay, TimeUnit unit)

schedule(Runnable command, long delay, TimeUnit unit)

这两个方法是延时执行,只是传入的参数不同

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

周期执行

scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

当前一个任务执行完成之后,延时delay,再开始执行第二个任务

 

框架示意图

ScheduledThreadPool

创建一个大小无线的线程池,支持定时及周期性执行任务的需求

创建线程池要用到ThreadPoolExecutor

 

自定义线程池:ThreadPoolExecutor

 

corePoolSize:线程池中所保存的核心线程数,包括空闲线程

maximumPoolSize:池中允许最大的线程数

keepAliveTime:线程池中空闲线程能保存的最长时间

unit:持续时间的单位

workQueue:任务执行前保存任务的队列,仅保存由execute方法提交的runnable任务,有四种阻塞队列

(a)ArrayBlockingQueue

(b)LinkedBlockingQueue

(c)SynchronousQueue

(d)PriorityBlockingQueue

newFixedThreadPool使用了LinkedBlockingQueue,newCachedThreadPool使用了SynchronousQueue

threadFactory:新线程使用ThreadFactory创建,如果没有指定,则使用Executors.defaultThreadFactory默认工厂

handler:饱和策略的句柄,当线程池包满了,任务无法得到处理,需要使用饱和策略来处理无法完成的任务,有四种策略

(a)AbortPolicy:默认策略,直接抛出异常

(b)CallerRunPolicy:调用者所在线程来运行任务

(c)DiscardOldestPolicy:丢弃队列中最老的任务,并执行当前任务

(d)DiscardPolicy:不处理,直接丢弃当前任务

可以自定义饱和策略,需要实现RejectExecutionHandler接口

 

向线程提交任务有两种方法:execute和submit

execute用于提交没有返回值的任务,所以无法判断任务是否被执行过

 

 

submit用于提交需要返回值的对象,返回一个future对象,可以通过future对象判断任务是否被线程执行,future.get()获取返回的值,get方法会阻塞当前线程直道future对象被返回,也可以使用get(long timeout,TimeUnit unit)来实现由等待时间的获取返回值,如果超市仍没有返回值,那么立刻返回,这时任务可能仍没有执行

关闭线程池

遍历线程池中的线程,逐个调用线程的interrupt来中断线程,不响应中断的线程可能无法停止

(a)shutDown:把线程的状态置为shutdown,然后中断所有没有正在执行任务的线程,已经在执行任务的线程继续执行到任务完成

(b)shutDownNow:把当前线程池状态置为stop,尝试停止所有的正在执行或暂停的线程,并返回等待执行任务的列表

调用之后,调用isShutDown返回true,当所有任务都关闭后,调用isTerminaed返回true

 

当通过execute方法将一个runnable任务添加到线程池中,处理顺序:

1.如果线程池中的数量小于corePoolSize,即使线程池中有空闲的线程,也会创建一个新线程来执行新添加的任务

2.如果线程池中的数量大于等于corePoolSize,但是workQueue缓冲队列未满,则将新添加的任务放到workQueue中,线程池中有线程空闲出来后,依次将workQueue中的任务交给空闲的线程执行

3.如果线程池中的数量大于等于corePoolSize,而且workQueue缓冲队列也满了,但线程池中的数量小于maximunPoolSize,则会创建新的线程来处理被添加的任务

4.如果线程池中的数量等于maximumuPoolSize,使用RejectedExecutionHandler处理溢出线程

当有新的任务处理,先看线程池中的线程数量是否大于corePoolSize,再看缓冲队列workQueue是否满了,最后看线程池中的数量是否大于maximumuPoolSize

当线程池中的线程数量大于corePoolSize,如果里面有线程的空闲时间超过了keepAliveTime,就将其移除线程池。

使用核心线程池+阻塞队列+线程池的设计思路的优点

创建新的线程需要获取全局锁,为了避免频繁的获取全局锁,使用一个阻塞队列来存储任务,如果核心线程池中的线程执行任务的周期短,那么执行完毕后立即从阻塞队列中获取任务,避免了重新创建线程,使线程的重复利用率提高,如果核心线程池中的执行周期长,使得阻塞队列满了还不断的提交任务,这时再创建线程,使用一个核心线程池,一个线程池,是因为核心线程池容量不能太大,不然阻塞队列的缓存作用就降低了,但是如果线程池的数量太小,就限制了并发的线程数,降低了CPU的利用率

我现在做的是个轻量化的题目, (1)题目说明       点云的轻量化广泛应用于三维建模、质量检测、逆向工程等工业领域,主要解决大规模点云数据所带来的数据量庞大和噪声干扰等问题。为了降低计算资源的消耗并提高系统响应速度,点云轻量化技术显得尤为重要。该技术通过对点云数据进行下采样和去噪处理,减少数据的存储量和计算量,从而提升后续处理的效率。下采样通过降低点云的密度,保留关键信息,去除冗余数据,确保尽量减少信息损失,而去噪处理则去除离群点和噪声点,改善数据质量。点云轻量化技术在提高数据处理速度、减少存储需求、优化计算资源、提升工业测量与检测的精度和效率方面具有显著作用。       (2)评价方法       (i)轻量化精度:通过比较轻量化后点云与原始点云之间重构的几何形状差异(如最大误差和均方根误差),衡量点云数据的保真度。       (ii)计算效率:比较算法的计算复杂度(占用内容量)和计算时间。import numpy as np import os import time import open3d as o3d import trimesh from sklearn.cluster import DBSCAN from scipy.spatial import cKDTree from concurrent.futures import ThreadPoolExecutor # def read_pointcloud(input_file): # ext = os.path.splitext(input_file)[1].lower() # if ext == '.ply': # pcd = o3d.io.read_point_cloud(input_file) # elif ext == '.stl': # with open(input_file, 'rb') as f: # mesh = o3d.io.read_triangle_mesh_from_buffer(f.read()) # pcd = mesh.sample_points_uniformly(number_of_points=1000000) # elif ext == '.obj': # with open(input_file, 'rb') as f: # mesh = o3d.io.read_triangle_mesh_from_buffer(f.read()) # pcd = mesh.sample_points_uniformly(number_of_points=1000000) # else: # raise ValueError(f'不支持的文件格式:{input_file}') # return pcd def read_pointcloud(input_file): ext = os.path.splitext(input_file)[1].lower() if ext == '.ply': pcd = o3d.io.read_point_cloud(input_file) elif ext == '.stl': # stl_mesh = mesh.Mesh.from_file(input_file) with open(input_file, 'rb') as f: stl_content = f.read().decode('gbk', errors='ignore') mesh_data = trimesh.load(input_file, encoding='gbk') vertices = mesh_data.vectors.reshape(-1, 3) mesh_data = trimesh.Trimesh(vertices=vertices, faces=np.arange(len(vertices)).reshape(-1, 3)) points = mesh_data.vertices pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(points) pcd = pcd.remove_duplicated_points() print(f'stl点数有{len(pcd.points)}个') elif ext == '.obj': mesh_data = trimesh.load(input_file) points = mesh_data.vertices pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(points) pcd = pcd.remove_duplicated_points() print(f'obj点数有{len(pcd.points)}个') else: raise ValueError(f'不支持的文件格式:{input_file}') return pcd def run_DBSCAN(pcd, min_samples=10): points = np.asarray(pcd.points) # 计算点云的平均密度 tree = cKDTree(points) distances, _ = tree.query(points, k=20) eps = np.percentile(distances[:, -1], 95) # 使用第k近邻距离的统计量作为eps的初始值 # 执行DBSCAN聚类 db = DBSCAN(eps=eps, min_samples=min_samples).fit(points) labels = db.labels_ # 移除噪声点(标签为-1的点) valid_indices = np.where(labels != -1)[0] denoised_pcd = o3d.geometry.PointCloud() denoised_pcd.points = o3d.utility.Vector3dVector(points[valid_indices]) clean_attributes = {} if pcd.has_colors(): colors = np.asarray(pcd.colors) clean_attributes['colors'] = colors[valid_indices] if pcd.has_normals(): normals = np.asarray(pcd.normals) clean_attributes['normals'] = normals[valid_indices] clusters = [] unique_labels = np.unique(labels[labels != -1]) for label in unique_labels: cluster_indices = np.where(labels == label)[0] cluster_pcd = o3d.geometry.PointCloud() cluster_pcd.points = o3d.utility.Vector3dVector(points[cluster_indices]) clusters.append(cluster_pcd) return denoised_pcd, clean_attributes, clusters # 对单个点执行导向滤波 def guided_filter_point(idx, points, neighbor_indices, epsilon): center_point = points[idx] indices = neighbor_indices[idx] # 获取邻域点索引 # 确保有足够的邻域点 if len(indices) < 3: return idx, center_point neighbors = points[indices] # 获取邻域点 mean_neighbor = np.mean(neighbors, axis=0) # 计算邻域中心点 # 计算协方差矩阵 diffs = neighbors - mean_neighbor cov = np.dot(diffs.T, diffs) / len(neighbors) cov += epsilon * np.eye(3) # 添加正则化项 # 计算导向滤波系数 a = np.dot(np.linalg.inv(cov), (center_point - mean_neighbor)) b = mean_neighbor - np.dot(a, mean_neighbor) filtered_point = np.dot(a, center_point) + b # 应用滤波 return idx, filtered_point # 导向滤波 radius(0.1):导向滤波搜索半径;epsilon(0.01): 导向滤波正则化参数;max_workers(8): 并行处理的最大线程数 def guided_filtering(pcd, max_workers=16, radius=0.1, epsilon=0.01): if not pcd.has_normals(): pcd.estimate_normals( search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=radius, max_nn=30)) points = np.asarray(pcd.points) filtered_points = np.copy(points) # 创建用于存储滤波结果的数组 # 计算每个点的邻域信息 tree = cKDTree(points) neighbor_indices = [] for i in range(len(points)): indices = tree.query_ball_point(points[i], radius) neighbor_indices.append(indices) # 并行处理每个点 with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = [] for i in range(len(points)): futures.append(executor.submit( guided_filter_point, i, points, neighbor_indices, epsilon )) for future in futures: idx, filtered_point = future.result() filtered_points[idx] = filtered_point pcd.points = o3d.utility.Vector3dVector(filtered_points) # 更新点云 return pcd # 并行处理DBSCAN生成的簇 def parallel_guided_filtering(clusters, max_workers): with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = [] for i, cluster in enumerate(clusters): futures.append(executor.submit(guided_filtering, cluster)) return [future.result() for future in futures] def process_file(input_file, output_folder, min_samples, max_workers, voxel_size): try: filename = os.path.basename(input_file) relative_path = os.path.relpath(os.path.dirname(input_file), INPUT_FOLDER) output_subfolder = os.path.join(output_folder, relative_path) os.makedirs(output_subfolder, exist_ok=True) output_path = os.path.join(output_subfolder, f"processed_{filename}") start_time = time.time() pcd = read_pointcloud(input_file) n_original = len(pcd.points) denoised_pcd, _, clusters = run_DBSCAN(pcd, min_samples=min_samples) filtered_clusters = parallel_guided_filtering(clusters, max_workers=max_workers) filtered_pcd = o3d.geometry.PointCloud() all_points = [] for cluster in filtered_clusters: all_points.extend(np.asarray(cluster.points)) filtered_pcd.points = o3d.utility.Vector3dVector(np.array(all_points)) sampled_pcd = pcd.voxel_down_sample(voxel_size=voxel_size) o3d.io.write_point_cloud(output_path, sampled_pcd) n_noise = n_original - len(sampled_pcd.points) end_time = time.time() - start_time print( f'{input_file} | 原始点数: {n_original} | 移除噪声: {n_noise}({n_noise / n_original * 100:.1f}%) | 保留点数: {len(sampled_pcd.points)} | 耗时: {end_time:.2f}s') return n_original, len(sampled_pcd.points), n_noise, end_time except Exception as e: print(f"处理文件 {input_file} 时出错: {str(e)}") return 0, 0, 0, 0 def process_folder(input_folder, output_folder, min_samples, max_workers, voxel_size): os.makedirs(output_folder, exist_ok=True) subfolder = {} for root, _, files in os.walk(input_folder): # 处理统计 total_points_original = 0 total_points_clean = 0 total_noise_removed = 0 total_time = 0 for file in files: if file.lower().endswith(('.ply', '.stl', '.obj')): input_file = os.path.join(root, file) original, processed, noise, time_taken = process_file(input_file, output_folder, min_samples=min_samples, max_workers=max_workers, voxel_size=voxel_size) total_points_original += original total_points_clean += processed total_noise_removed += noise total_time += time_taken if total_points_original > 0: subfolder_relative = os.path.relpath(root, input_folder) subfolder[subfolder_relative] = (total_points_original, total_points_clean, total_noise_removed, total_time) print(f'\n子文件夹[{subfolder_relative}]:') print(f' 总原始点数: {total_points_original}') print(f' 总移除噪声: {total_noise_removed} ({total_noise_removed / total_points_original * 100:.1f}%)') print(f' 总保留点数: {total_points_clean}') print(f' 处理完成! 用时{total_time:.2f}s') print('-'*140) if __name__ == "__main__": INPUT_FOLDER = 'D:/桌面/point/data/part1' OUTPUT_FOLDER = 'D:/桌面/point/output/1' MIN_SAMPLES = 30 MAX_WORKERS = 16 VOXEL_SIZE = 1 process_folder(INPUT_FOLDER, OUTPUT_FOLDER, MIN_SAMPLES, MAX_WORKERS, VOXEL_SIZE)这段代码我是只用了去噪和下采样进行轻量化。如果我想再使用像论文里的模型进行处理,那么在我现在的代码的基础上,我应该如何修改
最新发布
07-23
你当前的代码已经实现了点云轻量化中的 **去噪(DBSCAN)和下采样(Voxel Downsample)**,这是传统方法中最常用的两种轻量化手段。 现在你想在此基础上,**结合论文中的深度学习模型**(例如 HM3D 或 BallMamba)进步优化轻量化效果。我们可以将你的项目划分为两个阶段: --- ## ✅ 、当前已完成的功能(传统轻量化) | 模块 | 功能 | |------|------| | `read_pointcloud` | 读取 `.ply`, `.stl`, `.obj` 格式点云 | | `run_DBSCAN` | 使用 DBSCAN 去噪,聚类并移除噪声点 | | `guided_filtering` | 使用导向滤波平滑点云 | | `process_file` / `process_folder` | 批量处理文件,执行去噪、滤波、下采样、输出结果 | --- ## ✅ 二、现在想实现的目标(引入深度学习模型) 你想在现有基础上: - **使用像 HM3D 或 BallMamba 这样的模型** 来辅助轻量化; - **提升轻量化精度(减少信息损失)**; - **保持轻量化效率(计算时间、内存占用)**; - **可能用于后续任务**:如分类、分割、重建等。 --- ## 🧠 三、你的代码如何与模型结合?(建议方案) 你现在的代码是个 **预处理流程**,处理完的点云可以作为模型的输入,也可以让模型在训练时学习如何在下采样后恢复原始点云信息。 ### 🔧 选项:模型用于后处理(重构原始点云) 你可以将你的轻量化点云作为输入,输入到 HM3D/BallMamba 模型中,**模型输出个更接近原始点云的重构结果**。 #### 🔁 流程如下: ``` 原始点云 ↓ [你的脚本] → 轻量化点云(下采样 + 去噪) ↓ [HM3D/BallMamba] → 重构点云(更接近原始) ↓ 比较重构误差(RMSE、最大误差)→ 评价轻量化精度 ``` #### 📌 代码修改建议: 在 `process_file` 中添加如下逻辑: ```python # 假设你已加载 HM3D_BallMamba 模型 model.eval() # 轻量化后的点云 sampled_points = np.asarray(sampled_pcd.points) # 转为 Tensor 并扩展 batch 维度 input_tensor = torch.tensor(sampled_points, dtype=torch.float32).unsqueeze(0).to(device) # 使用模型进行重构 with torch.no_grad(): reconstructed_points = model(input_tensor) # shape: (1, N_reconstructed, 3) # 转回 Open3D 点云 reconstructed_pcd = o3d.geometry.PointCloud() reconstructed_pcd.points = o3d.utility.Vector3dVector(reconstructed_points.squeeze(0).cpu().numpy()) # 保存重构点云 o3d.io.write_point_cloud(os.path.join(output_subfolder, f"reconstructed_{filename}"), reconstructed_pcd) ``` > ⚠️ 注意:你需要先训练模型或加载预训练模型,使模型具备重构能力。 --- ### 🔧 选项二:模型用于轻量化过程(学习下采样策略) 你可以用模型替代传统的 Voxel 下采样,让模型自己决定保留哪些关键点,以达到更高的保真度。 #### 📌 举例: 使用 **FPS(最远点采样) + BallMamba** 的方式选择关键点: ```python from utils.pointcloud_utils import farthest_point_sample # FPS 采样 def fps(points, num_samples): indices = farthest_point_sample(points, num_samples) return points[indices] # 使用 FPS + 模型增强 def smart_downsample(pcd, model, num_points=1024): points = np.asarray(pcd.points) if len(points) < num_points: return pcd # 最远点采样 sampled_points = fps(points, num_points) # 使用模型增强保留特征(可选) with torch.no_grad(): input_tensor = torch.tensor(sampled_points, dtype=torch.float32).unsqueeze(0).to(device) enhanced_points = model(input_tensor).squeeze(0).cpu().numpy() enhanced_pcd = o3d.geometry.PointCloud() enhanced_pcd.points = o3d.utility.Vector3dVector(enhanced_points) return enhanced_pcd ``` 然后在 `process_file` 中替换: ```python # sampled_pcd = pcd.voxel_down_sample(voxel_size=voxel_size) sampled_pcd = smart_downsample(pcd, model, num_points=1024) ``` --- ## 🧪 四、推荐评价指标(用于模型训练和评估) 你已经提到了两个核心评价标准: | 指标 | 计算方式 | 实现建议 | |------|----------|-----------| | **轻量化精度** | RMSE、最大误差(重构点云与原始点云之间的差异) | 使用 Chamfer Distance 或 Earth Mover's Distance | | **计算效率** | 内存占用、处理时间 | 使用 `time.time()` 和 `psutil` 获取内存信息 | #### 🔢 示例代码(Chamfer Distance) ```python def chamfer_distance(pcd1, pcd2): points1 = np.asarray(pcd1.points) points2 = np.asarray(pcd2.points) tree1 = cKDTree(points1) tree2 = cKDTree(points2) dist1, _ = tree1.query(points2, k=1) dist2, _ = tree2.query(points1, k=1) return (np.mean(dist1) + np.mean(dist2)) / 2 ``` --- ## 📦 五、最终建议:你的项目可以这样扩展 | 功能 | 当前代码 | 模型结合建议 | |------|----------|--------------| | 数据读取 | ✅ 支持多种格式 | 无需改动 | | DBSCAN 去噪 | ✅ 实现 | 可保留作为预处理 | | 导向滤波 | ✅ 实现 | 可保留作为平滑处理 | | Voxel 下采样 | ✅ 实现 | 可替换为模型选择关键点 | | 轻量化模型 | ❌ 未实现 | 引入 BallMamba/HM3D 进行重构或智能采样 | | 精度评价 | ✅ 基本统计 | 增加 Chamfer Distance、EMD | | 效率评价 | ✅ 时间统计 | 可增加内存监控 | | 输出格式 | ✅ PLY | 可增加 `.npy` 用于模型训练 | --- ## 📝 总结 你现在有了个 **完善的点云轻量化预处理流程**,接下来可以考虑: 1. **构建个 HM3D/BallMamba 模型,用于重构或智能采样** 2. **将模型集成进你的处理流程中** 3. **使用 Chamfer Distance 等指标评估轻量化效果** --- 如果你希望我继续帮你: - ✅ 生成完整的 BallMamba/HM3D 模型代码; - ✅ 写个训练脚本,训练模型重构点云; - ✅ 把模型嵌入你的处理脚本中; - ✅ 添加轻量化精度和效率的完整评估模块; 请告诉我你更关注以下哪部分: - [ ] 轻量化模型训练(我需要数据集和模型代码) - [ ] 轻量化模型推理(我已经有了模型) - [ ] 完整脚本集成(预处理 + 模型 + 评价) - [ ] 精度/效率评价模块实现 我可以继续为你生成完整项目内容。欢迎继续提问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值