使用OpenCV删除孤立的像素

时间:2017-09-10 17:52:10

标签: python opencv computer-vision

我正在寻找一种使用OpenCV从二进制图像中删除孤立白色像素的方法。一个类似的问题(OpenCV get rid of isolated pixels)有一堆“答案”,但似乎没有一个对我有用。我已经尝试过各种开合方式,但也没有成功。

这篇文章:

https://homepages.inf.ed.ac.uk/rbf/HIPR2/hitmiss.htm

建议我可以使用命中或未命中操作来实现此目的:

Structuring elements

  

1 用于定位二进制图像中的孤立点

原因是0s的解释与直接使用侵蚀/扩张时的解释不同(其中0被解释为“不关心”而不是“不是白色”,这基本上就是我所追求的)。但是,使用此内核只需渲染原始图像。

我的输入图片是:

Calc input image

您会注意到图像左侧附近有一些白色像素,我想摆脱它。

以下是代码:

kernel = np.array([ [0, 0, 0],
                    [0, 1, 0],
                    [0, 0, 0]],np.uint8)

hitormiss = cv2.morphologyEx(input_image, cv2.MORPH_HITMISS, kernel)

cv2.imshow('hitormiss', hitormiss)

删除像这样的孤立像素的正确方法是什么?

更新:亚历山大的答案就像一个魅力,是最快的解决方案。另一个答案也提供了一个解决方案,即使用cv2.connectedComponents函数,但它的处理器密集程度要高得多。这是一个使用这种方法的函数:

def remove_isolated_pixels(self, image):
    connectivity = 8

    output = cv2.connectedComponentsWithStats(image, connectivity, cv2.CV_32S)

    num_stats = output[0]
    labels = output[1]
    stats = output[2]

    new_image = image.copy()

    for label in range(num_stats):
        if stats[label,cv2.CC_STAT_AREA] == 1:
            new_image[labels == label] = 0

    return new_image

2 个答案:

答案 0 :(得分:4)

我相信OpenCV的实施已被打破。有一个相关的issue on OpenCV's GitHub似乎合并了一个拉取请求来修复;我认为它被添加到pull request中引用的OpenCV 3.3-rc中,所以希望下次更新OpenCV时应该修复它。我不确定问题是否是由同一件事引起的。

所选答案中的创意解决方案非常棒,但我同意您的观点:必须有更好的方式,尽管实施方法有所不足。

在他们所说的OpenCV Hit-or-miss tutorial上:

  

因此,命中或未命中操作包括三个步骤:

     
      
  1. 使用结构元素 B1 侵蚀图像 A
  2.   
  3. 使用结构元素 B2 侵蚀图像 A A_c )的补充。
  4.   
  5. AND步骤1和步骤2的结果。
  6.   

然后继续说这可以通过命中或未命中转换中的单个内核来完成,但是正如我们所知,它已被破坏。那么让我们改为做这些步骤。

import cv2
import numpy as np

# load image, ensure binary, remove bar on the left
input_image = cv2.imread('calc.png', 0)
input_image = cv2.threshold(input_image, 254, 255, cv2.THRESH_BINARY)[1]
input_image_comp = cv2.bitwise_not(input_image)  # could just use 255-img

kernel1 = np.array([[0, 0, 0],
                    [0, 1, 0],
                    [0, 0, 0]], np.uint8)
kernel2 = np.array([[1, 1, 1],
                    [1, 0, 1],
                    [1, 1, 1]], np.uint8)

hitormiss1 = cv2.morphologyEx(input_image, cv2.MORPH_ERODE, kernel1)
hitormiss2 = cv2.morphologyEx(input_image_comp, cv2.MORPH_ERODE, kernel2)
hitormiss = cv2.bitwise_and(hitormiss1, hitormiss2)

cv2.imshow('isolated.png', hitormiss)
cv2.waitKey()

Isolated pixels

然后删除,就像反转hitormiss一样简单,并将其maskcv2.bitwise_and() input_image一起使用。

hitormiss_comp = cv2.bitwise_not(hitormiss)  # could just use 255-img
del_isolated = cv2.bitwise_and(input_image, input_image, mask=hitormiss_comp)
cv2.imshow('removed.png', del_isolated)
cv2.waitKey()

Removed isolated pixels

注意:正如评论中所讨论的,在这种特定情况下kernel1的侵蚀与输入二进制图像相同,因此不需要进行此计算,这也引入了一些其他不必要的步骤。具体案例。但是,你可能有不同的内核,而不是中间的一个1,所以我将保持代码原样,以保持它对任何内核的通用。

答案 1 :(得分:2)

  1. 运行连接组件标签
  2. 计算每个组件的像素数
  3. 对于每个小于一分钟像素数的分量,将所有分量像素转换为零。