删除图像中虚假的小噪声岛 - Python OpenCV

ann*_*ena 29 python opencv filtering image image-processing

我试图摆脱一些我的图像背景噪音.这是未经过滤的图像.

为了过滤,我使用此代码生成应保留在图像中的掩码:

 element = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
 mask = cv2.erode(mask, element, iterations = 1)
 mask = cv2.dilate(mask, element, iterations = 1)
 mask = cv2.erode(mask, element)
Run Code Online (Sandbox Code Playgroud)

使用此代码,当我从原始图像中屏蔽掉不需要的像素时,我得到的是:

正如你所看到的,中间区域的所有小点都消失了,但是来自密集区域的许多小点也消失了.为了减少过滤,我尝试将第二个参数更改getStructuringElement()为(1,1),但这样做会给我第一个图像,就像没有过滤任何东西一样.

有什么方法可以应用这两个极端之间的过滤器吗?

另外,任何人都可以向我解释到底是getStructuringElement()做什么的吗?什么是"结构元素"?它做了什么以及它的大小(第二个参数)如何影响过滤级别?

ray*_*ica 72

你的很多问题源于你不确定形态学图像处理是如何工作的,但我们可以让你的疑虑得到休息.您可以将结构元素解释为要与之比较的"基本形状".结构元素中的1对应于您要在此形状中查看的像素,0是您要忽略的像素.有不同的形状,如矩形(如你所知MORPH_RECT),椭圆形,圆形等.

因此,cv2.getStructuringElement为您返回一个结构元素.第一个参数指定所需的类型,第二个参数指定所需的大小.在你的情况下,你想要一个2 x 2"矩形"......这真的是一个正方形,但没关系.

在更加卑鄙的意义上,您使用结构元素并从图像的左侧到右侧以及从上到下进行扫描,并抓取像素邻域.每个像素邻域的中心都恰好位于您正在查看的感兴趣像素上.每个像素邻域的大小与结构元素的大小相同.

侵蚀

对于侵蚀,您将检查像素邻域中与结构元素接触的所有像素.如果每个非零像素正在触摸1的结构化元素像素,则相对于输入的相应中心位置中的输出像素是1.如果存在至少一个接触结构化像素的非零像素即为1,则输出为0.

就矩形结构元素而言,您需要确保结构元素中的每个像素都触摸图像中的非零像素,以用于像素邻域.如果不是,则输出为0,否则为1.这有效地消除了小的噪声区域,并且还略微减小了对象的面积.

尺寸因子,矩形越大,执行的收缩越多.结构元素的大小是一个基线,其中任何小于此矩形结构元素的对象,您可以将它们视为已过滤而不出现在输出中.基本上,选择1 x 1矩形结构元素与输入图像本身相同,因为该结构元素适合其中的所有像素,因为像素是图像中可能的信息的最小表示.

扩张

膨胀与侵蚀相反.如果至少有一个非零像素接触结构元素中的像素为1,则输出为1,否则输出为0.您可以将此视为略微扩大的对象区域并使小岛变大.

这里对尺寸的影响是结构元素越大,对象的面积越大,孤立的岛变得越大.


你正在做的是先侵蚀,然后是扩张.这就是所谓的开放操作.此操作的目的是在(尝试)维护图像中较大对象的区域时去除小的噪声岛.侵蚀消除了这些岛屿,而扩张使较大的物体恢复到原始大小.

你出于某种原因再次受到侵蚀,我无法理解,但那没关系.


我个人会做的是首先执行关闭操作,这是一个扩张,然后是侵蚀.关闭有助于将靠近的区域分组为单个对象.因此,您会看到在我们执行任何其他操作之前,可能会有一些较大的区域彼此接近.因此,我会先关闭,然后再打开一个开口,以便我们可以移除隔离的噪音区域.请注意,我将使结束元素的大小更大,因为我想确保我得到附近的像素和开放的结构元素大小更小,以便我不想错误地删除任何更大的区域.

一旦你这样做,我会用原始图像掩盖任何额外的信息,这样你就可以在较小的岛屿消失时保留较大的区域.

而不是链接侵蚀,然后扩张,或扩张,然后侵蚀,使用cv2.morphologyEx,你可以指定MORPH_OPENMORPH_CLOSE作为标志.

因此,假设您的图像被调用,我个人会这样做spots.png:

import cv2
import numpy as np

img = cv2.imread('spots.png')
img_bw = 255*(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) > 5).astype('uint8')

se1 = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
se2 = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
mask = cv2.morphologyEx(img_bw, cv2.MORPH_CLOSE, se1)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, se2)

mask = np.dstack([mask, mask, mask]) / 255
out = img * mask

cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('output.png', out)
Run Code Online (Sandbox Code Playgroud)

上面的代码非常明显.首先,我读入图像,然后将图像转换为灰度和阈值,强度为5,以创建被视为对象像素的蒙版.这是一个相当干净的图像,因此任何大于5的图像似乎都有效.对于形态学例程,我需要将图像转换为uint8并将蒙版缩放到255.接下来,我们创建两个结构元素 - 一个是关闭操作的5 x 5矩形,另一个是打开操作的2 x 2 .我cv2.morphologyEx在阈值图像上分别运行两次打开和关闭操作.

一旦我这样做,我堆叠掩码,使其成为一个3D矩阵并除以255,使其成为掩码,[0,1]然后我们将此掩码与原始图像相乘,以便我们可以抓取图像的原始像素和从掩码输出中维护被认为是真实对象的东西.

剩下的只是为了说明.我在窗口中显示图像,并且还将图像保存到一个名为的文件中output.png,其目的是向您显示此帖子中图像的外观.

我明白了:

在此输入图像描述

请记住,它并不完美,但它比以前更好.你必须使用结构元素大小来获得你认为是一个好输出的东西,但这肯定足以让你开始.祝好运!


C++版

有些请求将我上面编写的代码翻译成使用OpenCV的C++版本.我终于开始编写代码的C++版本,并且已经在OpenCV 3.1.0上进行了测试.这个代码如下.如您所见,代码与Python版本中的代码非常相似.但是,我cv::Mat::setTo在原始图像的副本上使用并将不是最终掩码的一部分设置为0.这与在Python中执行元素乘法相同.

#include <opencv2/opencv.hpp>

using namespace cv;

int main(int argc, char *argv[])
{
    // Read in the image
    Mat img = imread("spots.png", CV_LOAD_IMAGE_COLOR);

    // Convert to black and white
    Mat img_bw;
    cvtColor(img, img_bw, COLOR_BGR2GRAY);
    img_bw = img_bw > 5;

    // Define the structuring elements
    Mat se1 = getStructuringElement(MORPH_RECT, Size(5, 5));
    Mat se2 = getStructuringElement(MORPH_RECT, Size(2, 2));

    // Perform closing then opening
    Mat mask;
    morphologyEx(img_bw, mask, MORPH_CLOSE, se1);
    morphologyEx(mask, mask, MORPH_OPEN, se2);

    // Filter the output
    Mat out = img.clone();
    out.setTo(Scalar(0), mask == 0);

    // Show image and save
    namedWindow("Output", WINDOW_NORMAL);
    imshow("Output", out);
    waitKey(0);
    destroyWindow("Output");
    imwrite("output.png", out);
}
Run Code Online (Sandbox Code Playgroud)

结果应与Python版本中的结果相同.


duh*_*ime 8

还可以使用remove_small_objectsskimage 中的函数删除小像素簇:

import matplotlib.pyplot as plt
from skimage import morphology
import numpy as np
import skimage

# read the image, grayscale it, binarize it, then remove small pixel clusters
im = plt.imread('spots.png')
grayscale = skimage.color.rgb2gray(im)
binarized = np.where(grayscale>0.1, 1, 0)
processed = morphology.remove_small_objects(binarized.astype(bool), min_size=2, connectivity=2).astype(int)

# black out pixels
mask_x, mask_y = np.where(processed == 0)
im[mask_x, mask_y, :3] = 0

# plot the result
plt.figure(figsize=(10,10))
plt.imshow(im)
Run Code Online (Sandbox Code Playgroud)

这显示:

在此处输入图片说明

要仅保留较大的簇,请尝试增加min_size(保留簇的最小大小)和减小connectivity(形成簇时像素邻域的大小)。仅使用这两个参数,就可以仅保留适当大小的像素簇。