cv2.drawContours() - 取消填充字符内的圆圈(Python、OpenCV) [英] cv2.drawContours() - unfill circles inside characters (Python, OpenCV)

查看:87
本文介绍了cv2.drawContours() - 取消填充字符内的圆圈(Python、OpenCV)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

按照@Silencer 的建议,我使用了他发布的代码

我试图通过 cv2.RETR_EXTERNAL 但使用这个参数只考虑整个外部区域.

代码是这样的(再次感谢 Silencer.几个月来一直在寻找这个......):

将 numpy 导入为 np导入 cv2im = cv2.imread('imgs\\2.png')imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)ret, thresh = cv2.threshold(imgray, 127, 255, 0)图像,轮廓,层次结构 = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)#contours.sort(key=lambda x: int(x.split('.')[0]))对于 i, cnts in enumerate(contours):## 这个轮廓是一个 3D numpy 数组cnt = 轮廓[i]res = cv2.drawContours(im, [cnt], 0, (255, 0, 0), 1)cv2.imwrite("contours.png", res)'''## 方法一:裁剪区域x,y,w,h = cv2.boundingRect(cnt)裁剪 = res[y:y+h, x:x+w]cv2.imwrite("cnts\\croped{}.png".format(i),croped)'''## 方法二:在空白处绘制# 获取索引为 0 的坐标偏移量 = cnt.min(axis=0)cnt = cnt - cnt.min(axis=0)max_xy = cnt.max(axis=0) + 1w, h = max_xy[0][0], max_xy[0][1]# 在空白处绘制画布 = np.ones((h, w, 3), np.uint8) * 255cv2.drawContours(canvas, [cnt], -1, (0, 0, 0), -1)#如果 h >15 和 w <60:cv2.imwrite("cnts\\canvas{}.png".format(i), canvas)

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

谢谢

更新

我在下面实现了 Fiver 答案,结果如下:

导入 cv2将 numpy 导入为 npimg = 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)图像,轮廓,层次结构 = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)对于 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,倒置 = cv2.threshold(tmp_img, 127, 255, cv2.THRESH_BINARY_INV)cnt = 轮廓[i]x, y, w, h = cv2.boundingRect(cnt)裁剪 = 倒置 [y:y + h, x:x + w]cv2.imwrite("roi{}.png".format(i), 裁剪)

解决方案

要绘制 char 而不填充封闭的内部区域:

<块引用>

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

  2. 找到没有内部对象的外部轮廓(通过标志层次结构

    结合这个答案

    <小时>

    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将 numpy 导入为 np导入 cv2img = cv2.imread('img02.png')灰色 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)## 临界点ret, threshed = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)## 查找轮廓cnts, hiers = cv2.findContours(threshed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]画布 = np.zeros_like(img)n = len(cnts)hiers = hiers[0]对于范围(n)中的我:如果 hiers[i][3] != -1:##如果在里面,继续继续## 画cv2.drawContours(canvas, cnts, i, (0,255,0), -1, cv2.LINE_AA)## 找到所有内部轮廓并绘制ch = hiers[i][2]而 ch!=-1:打印(" {: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)

    使用此图像运行此代码:

    您将获得:

    <小时>

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

    <小时>

    更新:

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

    在 OpenCV 3.4 中:

    在 OpenCV 4.0 中:

    <小时>

    As suggested by @Silencer, I used the code he posted here to draw contours around the numbers in my image. At some point, working with numbers like 0,6,8,9 I saw that their inside contours (the circles) are being filled as well. How can I prevent this ? Is there a min/max area of action to set for cv2.drawContours() so I can exclude the inner area ?

    I tried to pass cv2.RETR_EXTERNAL but with this parameter only the whole external area is considered.

    The code is this (again, thanks Silencer. Was searching for this for months..):

    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)
    

    The main image on which I am working..

    Thanks

    UPDATE

    I implemented Fiver answer below and this is the result:

    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)
    

    解决方案

    To draw the char without filled the closed inner regions:

    1. find the contours on the threshed binary image with hierarchy.

    2. find the outer contours that don't have inner objects (by flag hierarchyi).

    3. for each outer contour:

      3.1 fill it(maybe need check whether needed);

      3.2 then iterate in it's inner children contours, fill then with other color(such as inversed color).

    4. combine with the crop code, crop them.

    5. maybe you need sort them, resplit them, normalize them.
    6. maybe, now you can do ocr with the trained model.


    FindContours, refill the inner closed regions.

    Combine with this answer Copy shape to blank canvas (OpenCV, Python), do more steps, maybe you can get this or better:


    The core code to refill the inner closed regions is as follow:

    #!/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)
    

    Run this code with this image:

    You will get:


    Of course, this is for two hierarchies. I haven't test for more than two. You who need can do test by yourself.


    Update:

    Notice in different OpenCVs, the cv2.findContours return different values. To keep code executable, we can just get the last two returned values use: cnts, hiers = cv2.findContours(...)[-2:]

    In OpenCV 3.4:

    In OpenCV 4.0:


    这篇关于cv2.drawContours() - 取消填充字符内的圆圈(Python、OpenCV)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆