如何从Planogram图像中识别并分割8层货架?(已试Hough线、轮廓法)
货架识别与分割方案求助
需求
- 从Planogram图像中识别并分割出全部货架,使用蓝色(或黑色、灰色等)线条进行分割,最终需得到8层货架。
已尝试方法
- 采用Hough线变换、轮廓检测法识别货架,但未实现预期目标。
参考图像

已尝试代码1(OpenCV轮廓法)
import cv2 import numpy as np # 加载图像 img = cv2.imread("display.jpg") # 转换为灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 阈值二值化 threshold, binary = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY_INV) # 查找轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 遍历轮廓并裁剪保存 for i, contour in enumerate(contours): # 获取轮廓外接矩形 x, y, w, h = cv2.boundingRect(contour) # 裁剪货架区域 shelf_img = img[y:y+h, x:x+w] # 保存裁剪后的图像 cv2.imwrite(f"shelf_{i}.jpg", shelf_img)
已尝试代码2(Scikit-image Hough线法)
#!/usr/bin/env python # coding: utf-8 from skimage.transform import (hough_line, hough_line_peaks,probabilistic_hough_line) from skimage.feature import canny from skimage import data from skimage.io import imread import numpy as np import matplotlib.pyplot as plt from skimage.filters import roberts, sobel, scharr # 加载图像 image = imread("display.jpg", as_gray=True) * 255 # Canny边缘检测+概率Hough线检测 edges = canny(image, 4, 1, 25) lines = probabilistic_hough_line(edges, threshold=2, line_length=10, line_gap=3) # 可视化检测结果 fig2, ax = plt.subplots(1, 3, figsize=(20, 8)) ax[0].imshow(image, cmap=plt.cm.gray) ax[0].set_title('输入图像') ax[0].axis('image') ax[1].imshow(edges, cmap=plt.cm.gray) ax[1].set_title('Canny边缘') ax[1].axis('image') ax[2].imshow(edges * 0) for line in lines: p0, p1 = line ax[2].plot((p0[0], p1[0]), (p0[1], p1[1])) ax[2].set_title('概率Hough线检测') ax[2].axis('image') height, width = image.shape # 定义投影计算函数 def proj_x(line): p0, p1 = line return np.abs(p0[0] - p1[0]) def proj_y(line): p0, p1 = line return np.abs(p0[1] - p1[1]) # 统计水平/垂直线投影 s_h = np.zeros(height) s_v = np.zeros(height) for line in lines: p0, p1 = line y1 = max(p0[1], p1[1]) y0 = min(p0[1], p1[1]) if proj_y(line) > 2: s_v[y0: y1] += proj_y(line) if proj_x(line) > 2: s_h[y0: y1] += proj_x(line) # 可视化投影结果 plt.figure(figsize=(20, 10)) plt.imshow(image, cmap=plt.cm.gray) plt.title('输入图像') plt.plot(s_h, np.arange(height), linewidth=2, label="水平投影") plt.plot(s_v, np.arange(height), linewidth=2, label="垂直投影") plt.ylim([height, 0]) plt.xlim([0, width]) plt.legend() # 投影特征滤波 import scipy.ndimage s_f = 20 * s_h / (s_v + 1) s_f = scipy.ndimage.gaussian_filter1d(s_f, 1) plt.figure(figsize=(20, 10)) plt.imshow(image, cmap=plt.cm.gray) plt.title('输入图像') plt.plot(s_f, np.arange(height), linewidth=2, label="滤波后投影") plt.ylim([height, 0]) plt.xlim([0, width]) # 峰值检测与候选区域提取 import scipy.signal cands = scipy.signal.find_peaks_cwt(s_f, np.arange(1, 50)) print(zip(cands, s_f[cands])) pairs = [] for c in cands: for d in cands: if (c < d) and (d -c < 100): pairs.append((c, d)) y_tagets = pairs[np.argmax([s_f[a] * s_f[b] for a, b in pairs])] y0, y1 = y_tagets # 可视化候选货架区域 import matplotlib.patches as patches fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(111, aspect='equal') ax.imshow(image, cmap=plt.cm.gray) ax.set_title('输入图像') ax.add_patch( patches.Rectangle( (0, y0), width, y1 - y0, facecolor="red", alpha=0.4 ) )
可行优化方案
1. Hough线变换针对性优化
- 筛选水平线:货架层为水平结构,计算每条检测线的斜率,只保留斜率绝对值小于0.1的线条,排除垂直/倾斜干扰线。
- 聚类合并相近线条:将筛选后的水平线按y坐标做K-means聚类(指定聚类数为8),取聚类中心作为最终的货架分割线,避免重复检测。
- 增强水平边缘:用Sobel算子单独提取水平边缘,或在Canny检测前做高斯模糊降噪,突出货架的水平结构。
2. 水平投影分析法
- 计算灰度图的水平像素和,货架区域的投影曲线会出现明显的极值点;对投影曲线做高斯平滑后,提取局部极值点作为分割线。
- 替换固定阈值为自适应阈值二值化,应对图像光照不均问题,更好保留货架结构。
3. 形态学操作辅助
- 用水平方向的结构元素(如
cv2.getStructuringElement(cv2.MORPH_RECT, (50,1)))做开运算,过滤垂直干扰,强化水平货架线。 - 对二值图做闭运算,填补货架线的间隙,让水平结构更连贯。
优化后代码示例(Hough线+K-means聚类)
import cv2 import numpy as np from sklearn.cluster import KMeans # 加载图像 img = cv2.imread("display.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊+Sobel水平边缘检测 blur = cv2.GaussianBlur(gray, (5,5), 0) sobel_x = cv2.Sobel(blur, cv2.CV_64F, 1, 0, ksize=3) abs_sobel = np.uint8(np.absolute(sobel_x)) _, binary = cv2.threshold(abs_sobel, 50, 255, cv2.THRESH_BINARY) # Hough线检测(只保留长水平线) lines = cv2.HoughLinesP(binary, 1, np.pi/180, threshold=100, minLineLength=img.shape[1]*0.8, maxLineGap=20) # 筛选水平线的y坐标 horizontal_lines = [] for line in lines: x1,y1,x2,y2 = line[0] if abs(y2-y1) < 10: # 过滤非水平线 horizontal_lines.append((y1 + y2)//2) # K-means聚类得到8个分割线 kmeans = KMeans(n_clusters=8, random_state=42) kmeans.fit(np.array(horizontal_lines).reshape(-1,1)) shelf_y = sorted(kmeans.cluster_centers_.flatten()) # 绘制蓝色分割线 result_img = img.copy() for y in shelf_y: y = int(y) cv2.line(result_img, (0, y), (img.shape[1], y), (255,0,0), 2) # 保存结果 cv2.imwrite("shelf_divided.jpg", result_img)
内容的提问来源于stack exchange,提问作者Armash Ansari
相关产品推荐
相关产品推荐

