如何提升Python中物体中心延伸臂的图像直线检测精度?
识别物体中心向外延伸的投影线问题
目标需求
我正尝试识别物体中心向外延伸的投影线,示例目标效果如下:


当前尝试方案及效果
目前已尝试步骤:找到物体轮廓后填充物体,提取填充后图像的骨架,再应用Hough Line Transform统计直线数量,效果如下:


但该方法未达到预期效果,当前使用代码如下:
import cv2 import numpy as np from skimage.transform import hough_line, hough_line_peaks from skimage import io import matplotlib.pyplot as plt from matplotlib import cm from skimage import color from imutils import paths from skimage.morphology import skeletonize # Load the image img = cv2.imread("image.PNG") plt.imshow(img[:,:,::-1]) plt.show() thickness = 5 # Get image dimensions height, width = img.shape[:2] # Define border dimensions top = bottom = thickness left = right = thickness # Create border mask border_mask = np.zeros((height, width, 3), np.uint8) # Ensure the mask has the same number of channels as the image border_mask[top:height-bottom, left:width-right] = 255 # Add thickness to edges of image result = cv2.bitwise_and(img, border_mask) # Add black border result = cv2.copyMakeBorder(result, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(0,0,0)) # Convert the image to grayscale (if it's not already) gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY) # Apply thresholding _, thresholded = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # Find contours in the thresholded image contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # Draw the contours on a copy of the original image contour_image = img.copy() cv2.drawContours(contour_image, contours, -1, (255, 255, 255), 40) # Taking a matrix of size 5 as the kernel kernel = np.ones((5, 5), np.uint8) img_dilation = cv2.dilate(contour_image, kernel, iterations=10) # Bitwise AND operation to fill the image result = cv2.bitwise_and(img, img_dilation) plt.imshow(result) plt.show() skeleton = skeletonize(result) image = cv2.cvtColor(skeleton, cv2.COLOR_BGR2GRAY) # Classic straight-line Hough transform # Set a precision of 0.05 degree. tested_angles = np.linspace(-np.pi / 2, np.pi / 2, 180) h, theta, d = hough_line(image, theta=tested_angles) hpeaks = hough_line_peaks(h, theta, d, threshold=0.2 * h.max()) print(len(hpeaks[0])) plt.imshow(mask,cmap='gray') # display the skeletonized image fig, axes = plt.subplots() axes.imshow(skeleton, cmap=plt.cm.gray) axes.set_title('Skeleton of the image') plt.show() skeleton_copy = skeleton.copy() for _, angle, dist in zip(*hpeaks): a = np.cos(angle) b = np.sin(angle) x0 = a * dist y0 = b * dist x1 = int(x0 + 1000 * (-b)) y1 = int(y0 + 1000 * (a)) x2 = int(x0 - 1000 * (-b)) y2 = int(y0 - 1000 * (a)) cv2.line(skeleton_copy, (x1, y1), (x2, y2), (0, 0, 255), 2) plt.imshow(skeleton_copy) plt.show()
优化建议
1. 简化预处理流程,聚焦核心区域
当前边框处理和膨胀步骤冗余,建议直接针对目标物体做精准处理:
- 灰度化后直接二值化,提取最大轮廓(目标物体)
- 使用
cv2.drawContours填充轮廓,得到纯净的物体掩码,替代复杂的膨胀+位运算操作,避免引入额外噪声
2. 修正骨架提取逻辑
skimage.morphology.skeletonize要求输入为单通道二值图(0/1),当前传入彩色图会导致错误,需先转灰度再归一化到0-1范围。
3. 优化Hough直线检测参数
- 缩小角度步长,比如用
np.linspace(-np.pi/2, np.pi/2, 360)提升角度检测精度 - 调整
hough_line_peaks的threshold、min_distance参数,过滤重复或噪声直线 - 增加
num_peaks限制,只保留最显著的直线
4. 基于物体中心筛选直线
先计算物体重心坐标,只保留经过中心附近的直线,过滤无关边缘噪声:
- 通过轮廓矩计算中心:
M = cv2.moments(max_contour),cx = int(M["m10"]/M["m00"]),cy = int(M["m01"]/M["m00"]) - 计算直线到中心的距离,筛选距离小于阈值的直线
优化后示例代码
import cv2 import numpy as np from skimage.morphology import skeletonize from skimage.transform import hough_line, hough_line_peaks import matplotlib.pyplot as plt # 加载图像并预处理 img = cv2.imread("image.PNG") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 根据图像明暗自动选择二值化方向 _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV if np.mean(gray) > 127 else cv2.THRESH_BINARY) # 提取最大轮廓并填充 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) max_contour = max(contours, key=cv2.contourArea) mask = np.zeros_like(gray) cv2.drawContours(mask, [max_contour], -1, 255, thickness=cv2.FILLED) # 计算物体中心 M = cv2.moments(max_contour) cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) # 骨架化(转成0-1二值图) skeleton = skeletonize(mask / 255) # Hough直线检测,优化参数 tested_angles = np.linspace(-np.pi/2, np.pi/2, 360) h, theta, d = hough_line(skeleton, theta=tested_angles) # 调整阈值、最小距离,限制峰值数量 hpeaks = hough_line_peaks(h, theta, d, threshold=0.3*h.max(), min_distance=20, num_peaks=8) # 绘制结果 skeleton_copy = (skeleton * 255).astype(np.uint8) skeleton_copy = cv2.cvtColor(skeleton_copy, cv2.COLOR_GRAY2BGR) cv2.circle(skeleton_copy, (cx, cy), 5, (0,255,0), -1) # 标记中心 for _, angle, dist in zip(*hpeaks): a = np.cos(angle) b = np.sin(angle) x0 = a * dist y0 = b * dist # 延长直线至图像边界 x1 = int(x0 + img.shape[1] * (-b)) y1 = int(y0 + img.shape[0] * (a)) x2 = int(x0 - img.shape[1] * (-b)) y2 = int(y0 - img.shape[0] * (a)) # 筛选经过中心附近的直线 if abs((a*cx + b*cy) - dist) < 10: cv2.line(skeleton_copy, (x1, y1), (x2, y2), (0,0,255), 2) plt.imshow(cv2.cvtColor(skeleton_copy, cv2.COLOR_BGR2RGB)) plt.title("优化后的中心投影线检测") plt.show()
其他补充建议
- 若图像噪声大,二值化前可增加
cv2.GaussianBlur或cv2.medianBlur去噪 - 多物体场景下,可通过轮廓面积过滤,只处理感兴趣的物体
- 考虑使用概率霍夫变换(
cv2.HoughLinesP),直接检测线段,更适合提取短投影线
内容的提问来源于stack exchange,提问作者mojojojo
相关产品推荐
相关产品推荐

