如何用Python3的OpenCV检测图像轮廓?聚类算法优化咨询
基于聚类算法的矩形检测自动化方案
一、图像数据向量化:适配聚类算法的样本转换
不管用K-means还是DBSCAN,核心是把图像中的像素或候选轮廓转换成带特征的样本点,两种适配方案如下:
- 像素级颜色聚类:针对不同灰度区域的区分需求,将灰度图像的所有像素展开为
(总像素数, 1)的数组,每个像素的特征就是其0-255的灰度值。 - 候选轮廓特征聚类:先通过低阈值Canny提取所有轮廓(含干扰条纹),再为每个轮廓提取组合特征:比如轮廓包围区域的平均灰度值、轮廓面积、外接矩形宽高比,将这些特征拼接成向量作为单个样本。
二、K-means实现自动化区域分割+矩形检测
适合已知目标颜色类别数(这里是白、灰、黑三类)的场景:
步骤1:像素级聚类分割
import cv2 import numpy as np from sklearn.cluster import KMeans # 读取灰度图像 img = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE) h, w = img.shape # 转换像素为聚类样本数组 pixels = img.reshape(-1, 1) # K-means聚类为3类(对应白、灰、黑区域) kmeans = KMeans(n_clusters=3, random_state=42) kmeans.fit(pixels) # 生成聚类掩码图像 labels = kmeans.labels_.reshape(h, w)
步骤2:从聚类区域提取矩形轮廓
# 遍历每个聚类类别 for cluster_idx in range(3): # 生成当前类别的二值掩码 mask = np.where(labels == cluster_idx, 255, 0).astype(np.uint8) # 提取轮廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 筛选矩形轮廓 for cnt in contours: # 轮廓多边形逼近 epsilon = 0.02 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) # 判断是否为矩形(4个顶点+面积阈值过滤小区域) if len(approx) == 4 and cv2.contourArea(cnt) > 1000: cv2.drawContours(img, [approx], 0, (0, 255, 0), 2) cv2.imshow('Detected Rectangles', img) cv2.waitKey(0) cv2.destroyAllWindows()
三、DBSCAN筛选目标轮廓(针对小干扰条纹)
适合干扰条纹面积小、特征与目标差异明显的场景,无需提前指定聚类数:
步骤1:提取候选轮廓及特征
import cv2 import numpy as np from sklearn.cluster import DBSCAN from sklearn.preprocessing import StandardScaler img = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE) # 低阈值Canny提取所有轮廓(含干扰) edges = cv2.Canny(img, 50, 150) contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 提取每个轮廓的特征向量 features = [] for cnt in contours: area = cv2.contourArea(cnt) # 计算轮廓区域的平均灰度 x, y, w_cnt, h_cnt = cv2.boundingRect(cnt) roi = img[y:y+h_cnt, x:x+w_cnt] avg_gray = np.mean(roi) # 计算外接矩形宽高比 aspect_ratio = w_cnt / h_cnt if h_cnt != 0 else 0 features.append([avg_gray, area, aspect_ratio]) # 标准化特征(DBSCAN对特征尺度敏感) scaler = StandardScaler() scaled_features = scaler.fit_transform(features)
步骤2:DBSCAN聚类筛选目标矩形
# 初始化DBSCAN,eps和min_samples需根据图像实际调整 dbscan = DBSCAN(eps=0.5, min_samples=1) labels = dbscan.fit_predict(scaled_features) # 定位目标聚类:通常目标矩形的平均面积远大于干扰条纹 unique_labels = np.unique(labels) target_label = None max_avg_area = 0 for label in unique_labels: if label == -1: # 跳过噪声点 continue # 计算当前聚类的平均轮廓面积 class_areas = [features[i][1] for i in range(len(features)) if labels[i] == label] avg_area = np.mean(class_areas) if avg_area > max_avg_area: max_avg_area = avg_area target_label = label # 绘制筛选后的目标矩形 for i in range(len(contours)): if labels[i] == target_label: cnt = contours[i] epsilon = 0.02 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) if len(approx) == 4: cv2.drawContours(img, [approx], 0, (0, 255, 0), 2) cv2.imshow('Filtered Rectangles', img) cv2.waitKey(0) cv2.destroyAllWindows()
关键注意事项
- K-means适合颜色类别固定的场景,需提前指定聚类数;DBSCAN无需指定聚类数,但需根据图像调整
eps(样本邻域半径)和min_samples(聚类最小样本数)。 - 特征选择要贴合干扰类型:若干扰是小面积条纹,优先用面积+灰度特征;若干扰是灰度接近但形状不同的区域,优先用像素级灰度聚类。
- 聚类后必须通过
approxPolyDP做轮廓逼近,过滤非矩形的无效区域。
内容的提问来源于stack exchange,提问作者lksj
相关产品推荐
相关产品推荐

