如何移除三维重建点云中的黑色背景(OpenCV Python场景)
移除三维重建点云中的黑色遮罩
问题描述
我用Python结合OpenCV做三维重建,已经完成了官网步骤里的:
- 单目和双目相机标定
- 姿态估计
- 极线几何计算
- 从双目图像生成深度图
最终得到了点云。我已经移除了原始图像的背景,但加载点云时还是能看到黑色遮罩,想只保留有球体的纸张工作区域,怎么移除点云中的黑色遮罩?
相关代码:
def get_3D_points(self, disparityMap, K1, K2, P2, t=np.array([0.0, 0.0, 0.0])): b = get_baseline(P2=P2) ( C1, C2, ) = get_centers(P2, prnt=True) print(" -- baseline {0}".format(b)) print("C1", C1) print("C2", C2) fx = K1[0, 0] fy = K2[1, 1] cx1 = K1[0, 2] cx2 = K2[0, 2] a1 = K1[0, 1] a2 = K2[0, 1] cy = K1[1, 2] Q = np.float64( [ [1, 0, 0, -cx1], [0, 1, 0, -cy], [0, 0, 0, fx], [0, 0, -1 / t[0], (cx1 - cx2) / t[0]], ] ) print("Q\n", Q) return cv2.reprojectImageTo3D(disparityMap, Q) def filter_infinity(self, depth, filter): """Filter infinite distances from ``PointCloud.``""" # filter inf. points mask = depth > filter coords = self.vertices[mask] colors = self.colors[mask] mask = coords[:, 2] > coords[:, 2].min() coords = coords[mask] colors = colors[mask] return PointCloud(coords, colors)
解决方案
黑色遮罩对应的是背景区域的无效点,这些点要么颜色为纯黑、要么深度值异常,可从以下几个方向处理:
1. 基于颜色过滤黑色点
原始图像的黑色遮罩区域,对应点云的颜色也是纯黑(RGB值均为0),直接过滤这类点即可。修改你的filter_infinity函数,加入颜色过滤逻辑:
def filter_infinity_and_black(self, depth, filter): """过滤无穷远点和黑色遮罩点""" # 过滤无穷远点 mask_depth = depth > filter # 过滤纯黑色点(RGB均为0),若遮罩有噪点可改成sum<10这类宽松条件 mask_black = ~((self.colors[:,0] == 0) & (self.colors[:,1] == 0) & (self.colors[:,2] == 0)) # 合并两个过滤条件 mask = mask_depth & mask_black coords = self.vertices[mask] colors = self.colors[mask] # 过滤异常深度最小值 mask_depth_valid = coords[:, 2] > coords[:, 2].min() coords = coords[mask_depth_valid] colors = colors[mask_depth_valid] return PointCloud(coords, colors)
2. 基于空间范围过滤
纸张工作区域的深度值通常比背景更集中,你可以先打印点云的深度统计值(比如print(coords[:,2].min(), coords[:,2].max(), coords[:,2].mean())),确定纸张的深度范围后,直接过滤超出范围的点:
# 在现有过滤逻辑后加入 min_depth = 0.5 # 示例值,根据实际情况调整 max_depth = 1.2 # 示例值,根据实际情况调整 mask_depth_range = (coords[:,2] > min_depth) & (coords[:,2] < max_depth) coords = coords[mask_depth_range] colors = colors[mask_depth_range]
3. 复用原始图像的前景掩码
如果你在移除原始图像背景时,已经得到了工作区域的二值掩码(前景为1,背景为0),可以直接把这个掩码映射到点云上:
# 假设mask_image是和原始图像同尺寸的二值前景掩码 mask_flat = mask_image.flatten() # 只保留掩码为1的点 coords = self.vertices[mask_flat == 1] colors = self.colors[mask_flat == 1]
这个方法最精准,直接复用你之前的背景分割结果。
内容的提问来源于stack exchange,提问作者I2I3
相关产品推荐
相关产品推荐

