Otsu阈值分割料堆图像误检测黑点问题及角度求解咨询
问题描述
我尝试通过Otsu阈值分割图像来求解料堆(左右两侧)的角度,原始图像如下:
对图像分割后,得到的结果如下:
视觉上分割后的图像白色背景里没有黑色像素,但用morphology.opening处理后,代码还是检测到了一些黑色像素,结果如下:
用其他图像测试时没有这个问题:
请问该如何解决这个问题?有什么思路吗?(下一步要求解料堆左右两侧的角度)
附上代码:
from skimage import io, filters, morphology, measure import numpy as np import cv2 as cv from scipy import ndimage import math # Load the image image = io.imread('mountain3.jpg', as_gray=True) # Apply Otsu's thresholding to segment the image segmented_image = image > filters.threshold_otsu(image) # Perform morphological closing to fill small gaps structuring_element = morphology.square(1) closed_image = morphology.closing(segmented_image, structuring_element) # Apply morphological opening to remove small black regions in the white background structuring_element = morphology.disk(10) # Adjust the disk size as needed opened_image = morphology.opening(closed_image, structuring_element) # Fill larger gaps using binary_fill_holes #filled_image = measure.label(opened_image) #filled_image = filled_image > 0 # Display the segmented image after filling the gaps io.imshow(opened_image) io.show() # Find the first row containing black pixels first_black_row = None for row in range(opened_image.shape[0]): if np.any(opened_image[row, :] == False): first_black_row = row break if first_black_row is not None: edge_points = [] # List to store the edge points # Iterate over the rows below the first black row for row in range(first_black_row, opened_image.shape[0]): black_pixel_indices = np.where(opened_image[row, :] == False)[0] if len(black_pixel_indices) > 0: # Store the first black pixel coordinates on the left and right sides left_x = black_pixel_indices[0] right_x = black_pixel_indices[-1] y = row # Append the edge point coordinates edge_points.append((left_x, y)) edge_points.append((right_x, y)) if len(edge_points) > 0: # Plotting the edge points import matplotlib.pyplot as plt edge_points = np.array(edge_points) plt.figure() plt.imshow(opened_image, cmap='gray') plt.scatter(edge_points[:, 0], edge_points[:, 1], color='red', s=1) plt.title('Edge Points') plt.show() else: print("No edge points found.") else: print("No black pixels found in the image.")
解决方案思路
- 问题根源:Otsu阈值分割后,背景中存在视觉难以察觉的微小噪点(孤立黑色像素),这些噪点在形态学开运算后仍未被完全清除,导致代码误检测为有效像素。
- 优化方向:
- 先对原始灰度图做高斯模糊,抑制高频噪点,提升Otsu分割的准确性。
- 调整形态学操作的结构元素尺寸,确保能彻底清除背景噪点。
- 保留图像中面积最大的连通区域(即料堆主体),直接排除背景中的微小噪点区域。
- 填充料堆区域的内部孔洞,避免边缘检测时出现断点。
修改后的代码
from skimage import io, filters, morphology, measure import numpy as np import matplotlib.pyplot as plt import math # 加载图像并预处理:高斯模糊抑制噪点 image = io.imread('mountain3.jpg', as_gray=True) blurred_image = filters.gaussian(image, sigma=1.5) # sigma值可根据图像噪点程度调整 # Otsu阈值分割 threshold = filters.threshold_otsu(blurred_image) segmented_image = blurred_image > threshold # 形态学闭运算填充料堆内部小缝隙 structuring_element_close = morphology.disk(5) closed_image = morphology.closing(segmented_image, structuring_element_close) # 保留最大连通区域(料堆),排除背景噪点 labeled_image = measure.label(closed_image) region_props = measure.regionprops(labeled_image) if region_props: largest_region = max(region_props, key=lambda x: x.area) filled_image = labeled_image == largest_region.label else: filled_image = closed_image # 形态学开运算清除背景残留微小噪点 structuring_element_open = morphology.disk(10) opened_image = morphology.opening(filled_image, structuring_element_open) # 显示处理后的图像 io.imshow(opened_image) io.show() # 检测边缘点 first_black_row = None for row in range(opened_image.shape[0]): if np.any(opened_image[row, :] == False): first_black_row = row break if first_black_row is not None: edge_points = [] for row in range(first_black_row, opened_image.shape[0]): black_pixel_indices = np.where(opened_image[row, :] == False)[0] if len(black_pixel_indices) > 0: left_x = black_pixel_indices[0] right_x = black_pixel_indices[-1] edge_points.append((left_x, row)) edge_points.append((right_x, row)) if edge_points: edge_points = np.array(edge_points) plt.figure() plt.imshow(opened_image, cmap='gray') plt.scatter(edge_points[:, 0], edge_points[:, 1], color='red', s=1) plt.title('Edge Points') plt.show() # 拟合料堆左右侧直线并计算角度 # 分离左右边缘点 left_points = edge_points[::2] right_points = edge_points[1::2] # 线性拟合左侧边缘 if len(left_points) > 1: left_coeffs = np.polyfit(left_points[:, 1], left_points[:, 0], 1) left_slope = left_coeffs[0] left_angle = math.degrees(math.atan(left_slope)) print(f"左侧料堆角度: {left_angle:.2f}°") # 线性拟合右侧边缘 if len(right_points) > 1: right_coeffs = np.polyfit(right_points[:, 1], right_points[:, 0], 1) right_slope = right_coeffs[0] right_angle = math.degrees(math.atan(right_slope)) print(f"右侧料堆角度: {right_angle:.2f}°") else: print("未检测到边缘点") else: print("图像中未检测到黑色像素")
关键优化点说明
- 高斯模糊:提前过滤图像中的微小噪点,让Otsu阈值分割更精准,减少背景误分割的情况。
- 最大连通区域保留:通过
measure.label标记所有连通区域,只保留面积最大的区域(料堆),直接排除背景中的噪点区域。 - 形态学操作调整:改用更大尺寸的圆盘结构元素做闭运算,填充料堆内部缝隙,确保区域完整性。
- 角度拟合:在代码末尾加入了线性拟合计算料堆两侧角度的逻辑,直接完成后续需求。
内容的提问来源于stack exchange,提问作者Tan Phan
相关产品推荐
相关产品推荐

