基于Python OpenCV的角点检测、带孔及指定类型形状计数问题
问题原因
你的现有代码直接对整张灰度图执行Harris角点检测,没有限定检测范围,所以形状内部纹理、孔洞边缘的拐点都会被识别为角点,同时也无法和颜色、形状类型做关联,没法满足你提出的三个统计需求。
调整思路
先通过轮廓提取+层级判断+颜色识别,把每个形状单独拆分出来,再针对单个形状的有效区域做角点检测,完全排除内部无效点的干扰。
调整后的完整实现代码如下:
import cv2 import numpy as np import matplotlib.pyplot as plt # 读取图像 img = cv2.imread('/content/shapes.png') img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 定义常见颜色的HSV阈值(可根据你的实际图像调整) color_ranges = { '红色': [(0, 120, 70), (10, 255, 255), (170, 120, 70), (180, 255, 255)], '蓝色': [(90, 120, 70), (128, 255, 255)], '绿色': [(40, 40, 40), (70, 255, 255)], '黄色': [(20, 100, 100), (30, 255, 255)] } # 1. 提取所有轮廓和层级关系 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 存储每个形状的信息 shapes_info = [] for i, cnt in enumerate(contours): # 过滤太小的噪声轮廓 area = cv2.contourArea(cnt) if area < 100: continue # 判断是否是外层轮廓(父轮廓为-1),排除孔洞本身的轮廓 if hierarchy[0][i][3] == -1: # 判断是否带孔洞:是否存在子轮廓 has_hole = hierarchy[0][i][2] != -1 # 识别形状类型 epsilon = 0.04 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) vertex_count = len(approx) if vertex_count == 3: shape_type = '三角形' elif vertex_count == 4: # 可加长宽比判断区分正方形/矩形,这里简化统一为矩形 shape_type = '矩形' elif vertex_count > 6: shape_type = '圆形' else: shape_type = '其他' # 识别颜色:取轮廓中心的HSV值匹配阈值 M = cv2.moments(cnt) cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) pixel_hsv = hsv[cy, cx] shape_color = '未知' for color, ranges in color_ranges.items(): # 红色有两个阈值区间 if color == '红色': lower1, upper1, lower2, upper2 = ranges if (lower1[0] <= pixel_hsv[0] <= upper1[0] and lower1[1] <= pixel_hsv[1] <= upper1[1] and lower1[2] <= pixel_hsv[2] <= upper1[2]) or \ (lower2[0] <= pixel_hsv[0] <= upper2[0] and lower2[1] <= pixel_hsv[1] <= upper2[1] and lower2[2] <= pixel_hsv[2] <= upper2[2]): shape_color = color break else: lower, upper = ranges if lower[0] <= pixel_hsv[0] <= upper[0] and lower[1] <= pixel_hsv[1] <= upper[1] and lower[2] <= pixel_hsv[2] <= upper[2]: shape_color = color break # 统计当前形状的角点:只在当前形状的掩码区域做Harris检测 mask = np.zeros(gray.shape, dtype=np.uint8) cv2.drawContours(mask, [cnt], -1, 255, -1) masked_gray = cv2.bitwise_and(np.float32(gray), np.float32(gray), mask=mask) dst = cv2.cornerHarris(masked_gray, 5, 3, 0.04) ret, dst = cv2.threshold(dst, 0.1*dst.max(), 255, 0) dst = np.uint8(dst) ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001) corners = cv2.cornerSubPix(gray, np.float32(centroids), (5,5), (-1,-1), criteria) # 排除背景的centroid,角点数量是len(corners)-1 corner_count = len(corners) - 1 # 存储形状信息 shapes_info.append({ 'color': shape_color, 'type': shape_type, 'has_hole': has_hole, 'corner_count': corner_count }) # 三个需求的统计示例 # 1. 统计指定颜色(比如蓝色)对应形状的角点总数 target_color = '蓝色' total_corner = sum([s['corner_count'] for s in shapes_info if s['color'] == target_color]) print(f"{target_color}形状的总角点数量:{total_corner}") # 2. 统计带孔洞的形状总数 total_hole_shape = sum([1 for s in shapes_info if s['has_hole']]) print(f"带孔洞的形状总数:{total_hole_shape}") # 3. 统计指定类型(比如三角形)的形状数量 target_type = '三角形' total_type = sum([1 for s in shapes_info if s['type'] == target_type]) print(f"{target_type}的总数量:{total_type}") # 可视化角点检测结果 dst = cv2.cornerHarris(gray,5,3,0.04) img[dst>0.1*dst.max()]=[255,0,255] plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) plt.show()
关键修改说明
- 新增轮廓层级判断,只处理外层的形状轮廓,孔洞的内层轮廓不会被单独统计,同时可以直接判断形状是否带孔洞
- 对每个形状单独生成掩码,仅在形状的有效区域内做Harris角点检测,完全排除了内部点、其他形状、孔洞边缘的点干扰,角点计数精准
- 新增颜色识别、形状类型识别逻辑,直接可以满足你提出的三个统计需求,只需修改统计部分的目标颜色/类型即可
内容的提问来源于stack exchange,提问作者Med FutureXAI
相关产品推荐
相关产品推荐

