【问题标题】:OpenCV detecting a single symbol with multiple bounding boxesOpenCV 检测具有多个边界框的单个符号
【发布时间】:2019-09-30 00:15:19
【问题描述】:

我正在使用 OpenCV 在手写数学方程输入上放置边界框。目前,我的代码有时会在单个图像的不同部分周围放置多个较小的边界框,而不是在图像周围创建一个大框。我不确定为什么会这样。我当前过滤图像并找到轮廓以绘制边界框的代码如下:

    img = cv2.imread(imgpath)

    morph = img.copy()
    morph = cv2.fastNlMeansDenoising(img)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1))
    morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
    morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 15))

    # take morphological gradient
    gradient_image = cv2.morphologyEx(morph, cv2.MORPH_GRADIENT, kernel)

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

    img_grey = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, kernel)

    blur = cv2.medianBlur(img_grey,3)


    ret, thing = cv2.threshold(blur, 0.0, 255.0, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    img_dilation = cv2.dilate(thing, kernel, iterations=3)


    conturs_lst = cv2.findContours(img_dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]

实际结果示例如下:

OG 图片:

【问题讨论】:

  • 由于某种原因,增加内核大小会使情况变得更糟..
  • 您可以添加您的原始图像吗?
  • 刚刚添加到帖子中!

标签: python image opencv image-processing contour


【解决方案1】:

您的想法是正确的,但我认为您过度使用 cv2.morphologyEx 来不断侵蚀和扩大图像。你提到你的问题:

目前,我的代码有时会在单个图像的不同部分周围放置多个较小的边界框,而不是在图像周围创建一个大框。

当您使用cv2.findContours 时,它可以正常工作,但由于您的轮廓实际上是斑点而不是一个相互连接的单一图像,它会创建多个边界框。要解决此问题,您可以扩大图像以将 blob 连接在一起。

我已经重写了您的代码,没有额外的 cv2.morphologyEx 重复。主要思路如下:

  • 将图像转换为灰度
  • 模糊图像
  • 将背景与所需对象分开的阈值图像
  • 扩张图像以连接斑点以形成奇异图像
  • 使用阈值最小/最大区域查找轮廓和过滤轮廓

用于隔离所需部分的阈值图像。请注意,某些轮廓的连接已断开。为了解决这个问题,我们扩大图像以连接 blob。

扩张图像以形成奇异的物体。现在请注意,我们在底部有不需要的水平部分,我们可以找到轮廓,然后使用区域过滤以删除该部分。

结果

import numpy as np
import cv2

original_image = cv2.imread("1.jpg")
image = original_image.copy()

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
thresh = cv2.threshold(blurred, 160, 255, cv2.THRESH_BINARY_INV)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
dilate = cv2.dilate(thresh, kernel , iterations=4)

cv2.imshow("thresh", thresh)
cv2.imshow("dilate", dilate)

# Find contours in the image
cnts = cv2.findContours(dilate.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

contours = []

threshold_min_area = 400
threshold_max_area = 3000

for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > threshold_min_area and area < threshold_max_area:
        # cv2.drawContours(original_image,[c], 0, (0,255,0), 3)
        cv2.rectangle(original_image, (x,y), (x+w, y+h), (0,255,0),1)
        contours.append(c)

cv2.imshow("detected", original_image) 
print('contours detected: {}'.format(len(contours)))
cv2.waitKey(0)

【讨论】:

  • 嗯,这似乎可以解决问题,但是,我认为我添加的一些内容是允许它还将“=”符号检测为一个边界框。不知道如何将其添加到此实现中。我可以只增加内核尺寸吗?
  • 您可以尝试内核维度,也可以通过检测紧密连接的组件来尝试将其合并到一个边界框中
  • 好的,谢谢!除此之外,一切看起来都很好,但有时我在我的其他一些图片中会出现不需要的大水平部分,它实际上覆盖了我想要检测的一些符号。出现这种情况的原因是什么
  • 其中一个预处理步骤是cv2.threshold,它有助于对像素值进行分类。根据不同图像上的照明,此值可能会或多或少地覆盖,因此您可能必须更改此值。目前,此全局值设置为 160,并且对于各种图像可能会有所不同。对于动态方法,您可以查看cv2.adaptiveThreshold,这在图像在不同区域具有不同光照条件的情况下可能会更好。
猜你喜欢
  • 2013-12-09
  • 2021-05-23
  • 2022-01-07
  • 2018-08-23
  • 1970-01-01
  • 2017-02-28
  • 1970-01-01
  • 2020-07-22
  • 1970-01-01
相关资源
最近更新 更多