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

基于Python的重叠图形检测计数问题求助(含OpenCV代码)

问题概述

需要开发工具检测并计数图片中的正方形、三角形与圆形,图形存在重叠情况(示例图为几何图形组成的火车)。当前用OpenCV编写的代码存在两个问题:

  • 轮子实际是10个圆(5个大圆各含1个小圆),但代码检测出19个圆
  • 被轮子遮挡的四边形未被正确识别

当前代码:

import cv2

img = cv2.imread('trem.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

_, threshold = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

contours, _ = cv2.findContours(
    threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

i = 0

shapes = dict()
triangle = quadrilateral = circle = 0

for contour in contours:

    if i == 0:
        i = 1
        continue

    approx = cv2.approxPolyDP(
        contour, 0.01 * cv2.arcLength(contour, True), True)

    cv2.drawContours(img, [contour], 0, (0, 0, 255), 5)

    M = cv2.moments(contour)
    if M['m00'] != 0.0:
        x = int(M['m10'] / M['m00'])
        y = int(M['m01'] / M['m00'])

    if len(approx) == 3:
        triangle += 1
        shapes.update({'Triangle': triangle})

    elif len(approx) == 4:
        quadrilateral += 1
        shapes.update({'Quadrilateral': quadrilateral})
    else:
        circle += 1
        shapes.update({'Circle': circle})

cv2.imshow('shapes', img)
print(shapes)
cv2.waitKey(0)
cv2.destroyAllWindows()

当前输出:{'Circle': 19, 'Quadrilateral': 5, 'Triangle': 2}
正确结果:{'Circle': 10, 'Quadrilateral': 6, 'Triangle': 2}

OpenCV代码修正方案

1. 解决圆计数错误问题

原代码靠“非3/4边的轮廓都算圆”的逻辑太粗糙,加上RETR_TREE会提取所有层级的小轮廓(比如圆的内部毛刺、噪点),导致计数虚高。修正方法:

  • 过滤极小面积的噪点轮廓(比如面积小于50像素的直接跳过)
  • 用圆形度判断圆:公式为 4π×面积÷(周长²),值越接近1越接近标准圆形,一般取阈值>0.8
  • 用RETR_CCOMP提取内外层轮廓,确保能抓到大圆内部的小圆,同时避免冗余轮廓

2. 解决被遮挡四边形未识别问题

原代码的简单阈值会把被遮挡的边缘断开,加上approxPolyDP的epsilon参数(0.01)太严格,导致被遮挡的四边形轮廓被拆成多个小轮廓。修正方法:

  • 用Canny边缘检测替代简单阈值,保留更多边缘信息
  • 加膨胀操作,把被遮挡断开的边缘重新连接
  • 调整approxPolyDP的epsilon到0.03,让被遮挡的四边形轮廓能被近似成4条边

修改后的代码

import cv2
import numpy as np

img = cv2.imread('trem.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 用Canny边缘检测+膨胀替代简单阈值,修复被遮挡的边缘
edges = cv2.Canny(gray, 50, 150)
kernel = np.ones((3, 3), np.uint8)
edges = cv2.dilate(edges, kernel, iterations=1)

# 提取所有内外轮廓,层级结构用CCOMP更清晰
contours, hierarchy = cv2.findContours(edges, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

shapes = {'Triangle': 0, 'Quadrilateral': 0, 'Circle': 0}

for idx, contour in enumerate(contours):
    # 过滤极小噪点
    area = cv2.contourArea(contour)
    if area < 50:
        continue
    
    perimeter = cv2.arcLength(contour, True)
    # 调整近似多边形的精度,适配被遮挡的轮廓
    approx = cv2.approxPolyDP(contour, 0.03 * perimeter, True)
    
    # 计算圆形度,排除非圆的不规则轮廓
    circularity = 4 * np.pi * (area / (perimeter ** 2)) if perimeter != 0 else 0
    
    # 判断图形类型
    if len(approx) == 3:
        shapes['Triangle'] += 1
    elif len(approx) == 4:
        shapes['Quadrilateral'] += 1
    elif circularity > 0.8:
        shapes['Circle'] += 1

print(shapes)
cv2.imshow('processed edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
不使用OpenCV的实现思路

假设已经用PIL+Numpy提取了轮廓(比如通过二值化、连通区域分析得到边界点),判断图形类型的核心逻辑如下:

  • 三角形:实现Douglas-Peucker近似多边形算法,得到的顶点数为3;或者计算轮廓的凸包,凸包顶点数为3
  • 四边形:近似多边形顶点数为4;若需区分正方形,可额外判断四条边长度接近、内角接近90度
  • 圆形:计算圆形度(公式同前),值大于0.8则判定为圆

核心步骤示例(伪代码)

from PIL import Image
import numpy as np
from scipy.ndimage import label

# 1. 加载图片并二值化
img = Image.open('trem.png').convert('L')
img_np = np.array(img)
# 手动实现Otsu阈值二值化(替代OpenCV的自动阈值)
hist, bins = np.histogram(img_np, bins=256)
bin_centers = (bins[:-1] + bins[1:]) / 2
weight1 = np.cumsum(hist)
weight2 = np.cumsum(hist[::-1])[::-1]
mean1 = np.cumsum(hist * bin_centers) / weight1
mean2 = (np.cumsum((hist * bin_centers)[::-1]) / weight2[::-1])[::-1]
variance12 = weight1[:-1] * weight2[1:] * (mean1[:-1] - mean2[1:]) ** 2
idx = np.argmax(variance12)
threshold = bin_centers[idx]
binary = (img_np < threshold).astype(np.uint8) * 255

# 2. 找连通区域
labeled, num_features = label(binary == 0)  # 假设黑色是图形区域

# 3. 对每个连通区域提取轮廓并判断类型
for i in range(1, num_features+1):
    # 提取区域边界点
    coords = np.argwhere(labeled == i)
    # 计算面积(像素数)
    area = len(coords)
    # 提取边缘点(简化版:取区域的外围像素)
    # 计算周长(边缘点的欧氏距离求和)
    # 实现Douglas-Peucker算法得到近似多边形顶点数
    # 计算圆形度
    # 根据顶点数和圆形度判断图形类型

内容的提问来源于stack exchange,提问作者SolaDeCoro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 15:33:17