You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 00:06:02