【问题标题】:How to remove small contours attached to another big one如何去除附着在另一个大轮廓上的小轮廓
【发布时间】:2019-12-24 22:21:24
【问题描述】:

我正在做单元格分割,所以我正在尝试编写一个函数来删除主要轮廓周围的所有次要轮廓,以便做一个遮罩。 发生这种情况是因为我加载了带有一些颜色标记的图像:

问题是当我做阈值时,它假定颜色标记之间的“框”是主轮廓的一部分。

正如您在我的代码中看到的那样,我不直接将彩色图像传递给灰色,因为红色会变成黑色,但也有其他颜色,至少 8 种,并且每张图像总是不同的。我有成千上万张这样的图像,其中只显示一个单元格,但在大多数情况下,总是附加外部轮廓。我的目标是找到一个函数,它为每个图像输入提供单个单元格的二进制图像,就像这样。所以我从这段代码开始:

import cv2 as cv
cell1 = cv.imread(image_cell, 0)
imgray = cv.cvtColor(cell1,cv.COLOR_BGR2HSV)
imgray = cv.cvtColor(imgray,cv.COLOR_BGR2GRAY)
ret,thresh_binary = cv.threshold(imgray,107,255,cv.THRESH_BINARY)
cnts= cv.findContours(image =cv.convertScaleAbs(thresh_binary) , mode = 
cv.RETR_TREE,method = cv.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
   cv.drawContours(thresh_binary,[c], 0, (255,255,255), -1)    
kernel = cv.getStructuringElement(cv.MORPH_RECT, (3,3))
opening = cv.morphologyEx(thresh_binary, cv.MORPH_OPEN, kernel, 
iterations=2) # erosion followed by dilation

总结一下,我如何从图像 1 中获得红色轮廓?

【问题讨论】:

    标签: python image opencv computer-vision scikit-image


    【解决方案1】:

    实际上,在您的代码中,“框”是一个合法的额外轮廓。然后在最终图像上绘制所有轮廓,包括“盒子”。如果任何其他颜色的单元格完全在图像中,这可能会导致问题。

    更好的方法是分离出你想要的颜色。下面的代码创建了一个二进制掩码,它只显示定义的红色范围内的像素。您可以将此掩码与findContours 一起使用。

    结果:

    代码:

        import cv2
        # load image
        img = cv2.imread("PjMQR.png")
        # Convert HSV
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
        # define range of red color in HSV
        lower_val = np.array([0,20,0])
        upper_val = np.array([15,255,255])
    
        # Threshold the HSV image to get only red colors
        mask = cv2.inRange(hsv, lower_val, upper_val)
    
        # display image
        cv2.imshow("Mask", mask)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    

    This code 可以帮助您了解此过程中的不同值(HSV 与 inRange)是如何工作的。 inRange docs

    【讨论】:

    • 如果我只有一种颜色可以分离,但我在其他单元格图像中有其他颜色标记,每个都做会非常昂贵。
    • 把它放在问题中会很好。多么昂贵'?你有数百万张图片吗?几十种颜色?相邻单元格颜色相同?一个图像中可以有多个完整的单元格吗?您打算如何区分当前工作流程中的颜色?
    • 您也可以使用ContourArea 删除小于阈值的轮廓。
    • 我已经尝试过 ContourArea 并且无法识别那个小的,事实上,我在 morph 函数中提出了迭代并解决了这个特殊问题,但它不会纠正问题的颜色。跨度>
    【解决方案2】:

    所以另一种方法,没有颜色范围。

    我认为您的代码中有几件事不正确。首先,您在thresh_binary 上绘制轮廓,但它也已经具有其他单元格的外线 - 您试图摆脱的线。我认为这就是为什么你使用opening(?) 而在这种情况下你不应该使用。

    要解决问题,首先要了解有关 findContours 工作原理的一些信息。 findContours 开始在黑色背景上查找白色形状,然后在该白色轮廓内查找黑色形状,依此类推。这意味着thresh_binary 中单元格的白色轮廓被检测为轮廓。里面是其他轮廓,包括你想要的轮廓。 docs with examples

    您应该首先只查找其中没有轮廓的轮廓。 findContours 还返回轮廓的层次结构。它指示轮廓是否有“childeren”。如果它没有(值:-1),那么您查看轮廓的大小并忽略那些太小的轮廓。您也可以只寻找最大的,因为这可能是您想要的。最后在黑色蒙版上绘制轮廓。

    结果:

    代码:

        import cv2 as cv
        import numpy as np
        # load image as grayscale
        cell1 = cv.imread("PjMQR.png",0)
        # threshold image
        ret,thresh_binary = cv.threshold(cell1,107,255,cv.THRESH_BINARY)
        # findcontours
        contours, hierarchy = cv.findContours(image =thresh_binary , mode = cv.RETR_TREE,method = cv.CHAIN_APPROX_SIMPLE)
    
        # create an empty mask
        mask = np.zeros(cell1.shape[:2],dtype=np.uint8)
    
        # loop through the contours
        for i,cnt in enumerate(contours):
                # if the contour has no other contours inside of it
                if hierarchy[0][i][2] == -1 :
                        # if the size of the contour is greater than a threshold
                        if  cv2.contourArea(cnt) > 10000:
                                cv.drawContours(mask,[cnt], 0, (255), -1)    
        # display result
        cv2.imshow("Mask", mask)
        cv2.imshow("Img", cell1)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    

    注意:我使用了您上传的图像,您的图像可能像素少得多,因此轮廓区域更小
    注2:enumerate 循环遍历轮廓,并为每个循环返回轮廓和索引

    【讨论】:

    • 工作正常。很好的解释,现在我对这些 OpenCV 函数有了更好的了解。
    猜你喜欢
    • 2018-03-18
    • 2014-08-23
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    • 2020-11-24
    • 2018-06-16
    • 2016-09-11
    相关资源
    最近更新 更多