cv2.drawContours() - 取消填充字符内的圆圈(Python,OpenCV)

2024-05-17

根据@Silencer的建议,我使用了他发布的代码here https://stackoverflow.com/questions/48244328/copy-shape-to-blank-canvas-opencv-python/48246500#48246500在我的图像中绘制数字周围的轮廓。 在某些时候,处理像这样的数字0,6,8,9我看到它们的内部轮廓(圆圈)也被填充了。 我怎样才能防止这种情况发生?是否有为 cv2.drawContours() 设置的最小/最大操作区域,以便我可以排除内部区域?

我试图通过cv2.RETR_EXTERNAL但使用此参数时,仅考虑整个外部区域。

代码是这样的(再次感谢 Silencer。我已经搜索了几个月了..):

import numpy as np
import cv2

im = cv2.imread('imgs\\2.png')
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

#contours.sort(key=lambda x: int(x.split('.')[0]))

for i, cnts in enumerate(contours):
    ## this contour is a 3D numpy array
    cnt = contours[i]
    res = cv2.drawContours(im, [cnt], 0, (255, 0, 0), 1)
    cv2.imwrite("contours.png", res)
    '''
    ## Method 1: crop the region
    x,y,w,h = cv2.boundingRect(cnt)
    croped = res[y:y+h, x:x+w]
    cv2.imwrite("cnts\\croped{}.png".format(i), croped)
    '''
    ## Method 2: draw on blank
    # get the 0-indexed coords
    offset = cnt.min(axis=0)
    cnt = cnt - cnt.min(axis=0)
    max_xy = cnt.max(axis=0) + 1
    w, h = max_xy[0][0], max_xy[0][1]
    # draw on blank
    canvas = np.ones((h, w, 3), np.uint8) * 255
    cv2.drawContours(canvas, [cnt], -1, (0, 0, 0), -1)

    #if h > 15 and w < 60:
    cv2.imwrite("cnts\\canvas{}.png".format(i), canvas)

我正在处理的主要图像..

Thanks

UPDATE

我在下面实现了 Fiver 答案,这就是结果:

import cv2
import numpy as np

img = cv2.imread('img.png')
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_v = img_hsv[:, :, 2]

ret, thresh = cv2.threshold(~img_v, 127, 255, 0)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for i, c in enumerate(contours):
    tmp_img = np.zeros(img_v.shape, dtype=np.uint8)
    res = cv2.drawContours(tmp_img, [c], -1, 255, cv2.FILLED)

    tmp_img = np.bitwise_and(tmp_img, ~img_v)

    ret, inverted = cv2.threshold(tmp_img, 127, 255, cv2.THRESH_BINARY_INV)

    cnt = contours[i]

    x, y, w, h = cv2.boundingRect(cnt)
    cropped = inverted[y:y + h, x:x + w]

    cv2.imwrite("roi{}.png".format(i), cropped)

来绘制char没有填充封闭的内部区域:

  1. 找到具有层次结构的脱粒二值图像上的轮廓。

  2. 找到没有内部对象的外部轮廓(通过标志层次结构i https://i.stack.imgur.com/rqSTd.png).

  3. 对于每个外轮廓:

    3.1 填写(可能需要检查是否需要);

    3.2 然后迭代其内部子轮廓,然后用其他颜色(例如反转颜色)填充。

  4. 与裁剪代码结合,裁剪它们。

  5. 也许你需要对它们进行排序、重新分割、标准化它们。
  6. 也许,现在您可以使用经过训练的模型进行 OCR 操作。

查找轮廓,重新填充内部封闭区域。

结合这个答案将形状复制到空白画布(OpenCV、Python) https://stackoverflow.com/questions/48244328/copy-shape-to-blank-canvas-opencv-python/48246500#48246500,做更多的步骤,也许你可以得到这个或更好的:


核心代码为refill内部封闭区域如下:

#!/usr/bin/python3
# 2018.01.14 09:48:15 CST
# 2018.01.15 17:56:32 CST
# 2018.01.15 20:52:42 CST

import numpy as np
import cv2

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

## Threshold 
ret, threshed = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)

## FindContours
cnts, hiers = cv2.findContours(threshed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]

canvas = np.zeros_like(img)
n = len(cnts)
hiers = hiers[0]

for i in range(n):
    if hiers[i][3] != -1:
        ## If is inside, the continue 
        continue
    ## draw 
    cv2.drawContours(canvas, cnts, i,  (0,255,0), -1, cv2.LINE_AA)

    ## Find all inner contours and draw 
    ch = hiers[i][2]
    while ch!=-1:
        print(" {:02} {}".format(ch, hiers[ch]))
        cv2.drawContours(canvas, cnts, ch, (255,0,255), -1, cv2.LINE_AA)
        ch = hiers[ch][0]

cv2.imwrite("001_res.png", canvas)

使用此图像运行此代码:

你会得到:


当然,这是针对两个层次结构的。我没有测试过两次以上。有需要的可以自行测试一下。


Update:

注意在不同的 OpenCV 中,cv2.findContours返回不同的值。为了保持代码可执行,我们可以使用以下方法获取最后两个返回值:cnts, hiers = cv2.findContours(...)[-2:]

在 OpenCV 3.4 中:

在 OpenCV 4.0 中:


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cv2.drawContours() - 取消填充字符内的圆圈(Python,OpenCV) 的相关文章