使用OpenCV删除孤立的像素 [英] Removing isolated pixels using OpenCV

查看:981
本文介绍了使用OpenCV删除孤立的像素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一种使用OpenCV从二进制图像中删除孤立的白色像素的方法.类似的问题( OpenCV摆脱孤立的像素)中有一堆答案",但似乎没有一个适合我.我也尝试过打开和关闭的各种组合,但都没有成功.

I'm looking for a way to remove isolated white pixels from a binary image using OpenCV. A similar question (OpenCV get rid of isolated pixels) has a bunch of "answers" but none seem to work for me. I've tried various combinations of opening and closing without success as well.

此处的文章:

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

建议我可以为此目的使用命中或遗失"操作:

Suggests I can use the hit-or-miss operation for exactly this purpose:

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

1 is used to locate isolated points in a binary image

其原因是0的解释与直接与侵蚀/膨胀配合使用时的解释不同(其中0解释为不在乎"而不是不是白色",这基本上就是我所追求的) ).但是,使用此内核只会渲染原始图像.

And that the reason why is that 0s are interpreted differently than when they are used with erosion/dilation directly (where 0s are interpreted as "don't care's" rather than "not white" which is basically what I'm after). However, using this kernel simply renders the original image.

我的输入图像是这样:

您会注意到我想消除的图像左侧附近有一些白色像素.

You'll notice there's a few white pixels near the left-hand side of the image which I'd like to get rid of.

代码如下:

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)

去除这些孤立像素的正确方法是什么?

What is the right way of removing isolated pixels like these?

更新:亚历山大的答案就像一个咒语,是最快的解决方案.另一个答案也提供了一种解决方案,即使用 cv2.connectedComponents 功能,但是它会占用更多的处理器资源.这是使用此方法的函数:

Update: Alexander's answer works like a charm and is the fastest solution. The other answer provides a solution too, which is to use the cv2.connectedComponents function, but it is much more processor-intensive. Here's a function that uses this approach:

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

推荐答案

我相信OpenCV实现已被破坏. OpenCV的GitHub上有一个相关的问题,该问题似乎合并了一个拉取请求以进行修复;我认为它已按照 pull请求中的引用添加到OpenCV 3.3-rc中.这应该在下次更新OpenCV时解决.我不确定问题是否是由同一件事引起的.

I believe the OpenCV implementation was broken. There was a related issue on OpenCV's GitHub which seems to have merged a pull request to fix; I think it was added to OpenCV 3.3-rc as referenced in the pull request so hopefully this should be fixed by the next time you update OpenCV. I'm not sure if the problem is caused by the same thing or not.

从所选答案中得出的创造性解决方案很棒,但我同意您的观点:尽管实施不完善,但肯定有更好的方法.

The creative solution from the selected answer is great, but I agree with you: there must be a better way, despite the broken implementation.

OpenCV失败指南中声明:

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

Therefore, the hit-or-miss operation comprises three steps:

  1. 侵蚀具有结构元素 B1 的图像 A .
  2. 用结构元素 B2 腐蚀图像 A ( A_c )的补码.
  3. AND是第1步和第2步的结果.
  1. Erode image A with structuring element B1.
  2. Erode the complement of image A (A_c) with structuring element B2.
  3. AND results from step 1 and step 2.

然后继续说,这可以通过在hit-or-miss转换中使用单个内核来完成,但是众所周知,它是坏的.因此,让我们改为执行这些步骤.

It then goes on to say that this can be accomplished with a single kernel in the hit-or-miss transform, but as we know, it is broken. So let's do those steps instead.

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()

然后将其删除,就像将hitormiss反转并与input_image用作cv2.bitwise_and()中的mask一样简单.

And then to remove, it's as simple as inverting the hitormiss and using that as a mask in cv2.bitwise_and() with the 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()

注意:如评论中所述,在这种特定情况下用kernel1进行的腐蚀与输入二进制图像相同,因此无需进行此计算,并且在此特定情况下还引入了一些其他不必要的步骤.但是,您可能有不同的内核,而中间没有一个内核,因此我将按原样保留代码,以使其对所有内核通用.

Note: as discussed in the comments, erosion with kernel1 in this specific case is identical to the input binary image, so there's no need to do this computation, and this introduces some other unnecessary steps as well in this specific case. However, you could have different kernels than just a single 1 in the middle, so I'm going to keep the code as-is to keep it general for any kernels.

这篇关于使用OpenCV删除孤立的像素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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