如何使用OpenCV排除被遮挡的图形?

问题详解

我是准备参加比赛的学生,这个比赛会使用到 OpenCV 这样的库做来识别处理,我学习 OpenCV 的基础操作已经有一个多月了,目前遇到了一个非常棘手的问题:我会使用 OpenCV 来识别多边形,这种多边形有随机的五种类型及七种颜色,目前我已经把颜色和形状统计的问题解决了,但是被遮挡的图形却不知道怎么排除,具体内容在第二个栏目。

我尝试了什么?

我尝试使用安卓版本的 OpenCV 来解决多边形识别的问题,目前使用的办法是利用 inRange() 方法获得二值图,接着使用 findContours() 方法寻找轮廓,得到轮廓后进行开闭预算,紧接着使用 approxPolyDP() 方法进行多边形逼近,最后使用 convexHull() 方法检测角点来判断多边形是什么类型。这样能识别简单的多边形,但是这样也会识别被前景覆盖掉的多边形,被覆盖的多边形是无效的(应该被排除),请问我应该怎么做才能排除这些被覆盖的多边形?

示例图片

img

请问有人可以帮助我吗?非常感谢!请结合上面的图片解决问题,不要用chatGPT敷衍我,要是能做出来,酬金追加到100元。

分析如下思路:
(1)掩模图像,InRange 颜色分割,这样就消除了背景,提取到 指定颜色的图形。
(2)大致来说,再对每个颜色 InRange 分割得到的二值图像,提取轮廓可以获得边缘,还可以在该图上进行霍夫线检测和圆检测。
接下来就是 图形遮挡问题。
(1)虽然问题条件没有明确,但从图中看没有 三层遮挡情况,也就是可以简化为 二层遮挡问题。具体来说,一个图形,可以被多个其它图像遮挡;但是一个像素上最多叠放了2个图像,没有一个像素上重叠了3个或以上的图形。
(2)提取的遮挡图案的上层图形不需要分析,直接通过 InRange 分割可以求出。
(3)提取到的遮挡图案的下层图形是在原始图案基础上,被一个或多个遮挡图案切除了局部。
(4)如果是(被切除局部的)三角形/矩形/菱形/五角星的二值轮廓图像,可以对该轮廓依次做:
1)最小外接圆,cv.minEnclosingCircle,检出圆形。如果原图形是圆形,则外接圆与遮挡轮廓有一定重合(设个阈值判断);如果原图形不是圆形,则外接圆与轮廓基本不重合。
2)最小外接三角形,cv.minEnclosingTriangle,检出三角形。原理同上。
3)轮廓的最小矩形边界框,cv.minAreaRect,检出正方形。原理同上。
4)最后剩下 菱形和五角星,可以用轮廓的凸壳判断,cv.convexHull。如果原图形是菱形,则凸壳与遮挡轮廓有一定重合,而五角星的凸壳与遮挡轮廓没有重合。
这样大致就可以解决了。
但是,仍然只能说大致解决。为什么呢?因为如果菱形矩形被遮挡1/2以上,只剩下两条边,其实是无法判断原始图形是菱形矩形还是三角形的。

可以用我这个试一下

  1. 创建一个与原始图像相同大小的遮罩图像,将其初始化为全白色(255)。

  2. 在遮罩图像上绘制一个矩形或圆形,表示要排除的区域。可以使用OpenCV中的函数如cv2.rectangle()或cv2.circle()。

  3. 将遮罩图像应用于原始图像,使用cv2.bitwise_and()函数。这将使遮罩区域变为黑色,从而排除了被遮挡的图形。

  4. 可以使用cv2.imshow()函数显示结果图像。

以下是一个示例代码:

import cv2
import numpy as np

# 读取原始图像
img = cv2.imread('image.jpg')

# 创建遮罩图像
mask = np.ones(img.shape[:2], dtype=np.uint8) * 255

# 绘制矩形
cv2.rectangle(mask, (100, 100), (300, 300), 0, -1)

# 应用遮罩
masked_img = cv2.bitwise_and(img, img, mask=mask)

# 显示结果图像
cv2.imshow('Original Image', img)
cv2.imshow('Masked Image', masked_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在上面的代码中,我们创建了一个遮罩图像,绘制了一个矩形,并将其应用于原始图像。最终结果将显示原始图像和排除了矩形区域的遮罩图像。

以下内容部分参考ChatGPT模型:


可以使用轮廓之间的重叠面积来判断是否被遮挡,如果被遮挡的轮廓的重叠面积小于一定的阈值,则可以将其排除。下面是排除被遮挡的多边形的示例代码:

import cv2

# 读取图像
img = cv2.imread('image.jpg')

# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化图像
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 计算每个轮廓的重叠面积
for i, contour1 in enumerate(contours):
    for j, contour2 in enumerate(contours):
        if i == j:
            continue
        area1 = cv2.contourArea(contour1)
        area2 = cv2.contourArea(contour2)
        inter_area = cv2.contourArea(cv2.minAreaRect(contour1)[-1] & cv2.minAreaRect(contour2)[-1])
        if inter_area / min(area1, area2) > 0.5:
            # 如果重叠面积占较小轮廓的面积的比例大于阈值,则认为较小轮廓被遮挡
            if area1 < area2:
                contours[i] = None
            else:
                contours[j] = None

# 绘制轮廓
for contour in contours:
    if contour is not None:
        cv2.drawContours(img, [contour], 0, (0, 255, 0), 2)

# 显示图像
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

其中,通过计算两个轮廓的最小外接矩形的交集面积来计算轮廓间的重叠面积,如果重叠面积占较小轮廓的面积的比例大于阈值,则认为较小轮廓被遮挡,将其排除。最后绘制剩余的轮廓即可。


如果我的建议对您有帮助、请点击采纳、祝您生活愉快

可以参考 下如下实例:https://www.5axxw.com/questions/simple/169wck

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
你可以使用 OpenCV 的遮罩(mask)来解决这个问题。在遮罩中,你可以标记哪些像素属于前景,哪些像素属于背景。当你处理图像时,只会处理遮罩中被标记为前景的像素。这样,被前景覆盖掉的多边形就不会被处理到了。

下面是一个简单的实现过程:

  1. 创建一个和图像同样大小的遮罩,用全黑的像素填充。
  2. 对于每一个多边形轮廓,使用 fillPoly() 方法绘制出轮廓所占据的区域,并将该区域在遮罩中标记为白色。
  3. 使用 bitwise_and() 方法将原始图像和遮罩的前景像素相乘,得到遮罩中标记为前景的像素所组成的图像。
  4. 在得到的结果图像中,使用 findContours() 方法寻找轮廓,得到所有没有被前景覆盖的多边形区域。

下面是代码示例:

import cv2
import numpy as np

# 加载图像
img = cv2.imread('example.jpg')

# 创建遮罩
mask = np.zeros(img.shape[:2], dtype=np.uint8)

# 定义多边形的颜色和形状
colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0), (255, 255, 0), (0, 255, 255)]
shapes = {3: "三角形", 4: "四边形", 5: "五边形", 6: "六边形", 7: "七边形"}

# 绘制多边形区域并标记为前景
for color in colors:
    for sides, shape in shapes.items():
        # 根据颜色和形状筛选出多边形轮廓
        lower = np.array(color, np.uint8)
        upper = np.array(color, np.uint8)
        mask_color = cv2.inRange(img, lower, upper)
        contours, hierarchy = cv2.findContours(mask_color, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        for cnt in contours:
            # 多边形逼近
            approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt,True), True)
            if len(approx) == sides:
                # 绘制多边形区域
                cv2.fillPoly(mask, [approx], 255)

# 使用遮罩提取出没有被前景覆盖的多边形区域
result = cv2.bitwise_and(img, img, mask=mask)
gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 显示结果
for cnt in contours:
    # 多边形逼近
    approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt,True), True)
    # 绘制多边形区域
    cv2.drawContours(img, [approx], 0, (0, 0, 0), 2)
    # 检测角点并判断多边形类型
    hull = cv2.convexHull(approx, returnPoints=False)
    if len(hull) == 3:
        shape = "三角形"已黑化的小白     elif len(hull) == 4:
        shape = "四边形"已黑化的小白     elif len(hull) == 5:
        shape = "五边形"已黑化的小白     elif len(hull) == 6:
        shape = "六边形"已黑化的小白     elif len(hull) == 7:
        shape = "七边形"已黑化的小白     cv2.putText(img, shape, (approx.ravel()[0], approx.ravel()[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)

cv2.imshow('result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这个示例中,颜色和形状的筛选采用了简单的方法,仅通过 inRange() 方法来判断。如果你的图像中存在非常相似的颜色或形状,这种方法可能会出现误差,你可以尝试使用其他的颜色空间或形状特征来进行筛选。
如果我的回答解决了您的问题,请采纳!

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^