移除图像中杂散的小岛- Python OpenCV

kqqjbcuj  于 2022-12-27  发布在  Python
关注(0)|答案(2)|浏览(185)

我正在努力去除一些图像中的背景噪音。这是未过滤的图像。

为了过滤,我使用下面的代码生成一个掩码,表示图像中应该保留的内容:

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)

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

正如你所看到的,中间区域的所有小点都消失了,但是很多来自密度更大区域的小点也消失了。为了减少过滤,我尝试将getStructuringElement()的第二个参数改为(1,1),但是这样做给我的第一张图像就好像什么都没有过滤一样。
有没有什么方法可以应用介于这两个极端之间的滤波器?
另外,谁能给我解释一下getStructuringElement()到底是做什么的?什么是“结构元素”?它做什么?它的大小(第二个参数)如何影响过滤的级别?

ajsxfq5m

ajsxfq5m1#

您的许多问题都源于您不确定形态学图像处理的工作原理,但我们可以打消您的疑虑。您可以将结构元素解释为要比较的“基本形状”。结构元素中的1对应于您希望在此形状中查看的像素,而0则是您希望忽略的像素。存在不同的形状,例如矩形(正如您通过MORPH_RECT计算出)、椭圆、圆形等。
同样,cv2.getStructuringElement为你返回一个结构元素。第一个参数指定你想要的类型,第二个参数指定你想要的大小。在你的例子中,你想要一个2 x 2的“矩形”......这实际上是一个正方形,但这没关系。
在一个更低级的意义上,你使用结构元素,从左到右,从上到下扫描你的图像,然后你抓取像素邻域。每个像素邻域的中心正好在你正在看的感兴趣的像素上。每个像素邻域的大小与结构元素的大小相同。

侵 eclipse

对于腐 eclipse ,检查像素邻域中接触结构化元素的所有像素。如果每个非零像素都接触值为1的结构化元素像素,则相对于输入的相应中心位置的输出像素为1。如果至少有一个非零像素接触值为1的结构化像素,则输出为0。
就矩形结构元素而言,您需要确保结构元素中的每个像素都与图像中的非零像素接触,以形成像素邻域。如果不是,则输出为0,否则为1。这将有效地消除小的杂散噪声区域,并略微减小对象的面积。
矩形越大,执行的收缩越多的尺寸因子。结构化元素的尺寸是一个基线,任何小于该矩形结构化元素的对象都可以认为它们被过滤掉了,不会出现在输出中。基本上,选择1 × 1的矩形结构化元素与输入图像本身相同,因为该结构化元素适合其内部的所有像素作为像素是图像中可能的信息的最小表示。

扩张

膨胀与腐 eclipse 相反。如果至少有一个非零像素与结构化元素中的像素1相接触,则输出为1,否则输出为0。您可以将此视为略微扩大对象区域并使小岛变大。
这里尺寸的含义是结构化元素越大,对象的面积将越大,并且孤立的岛变得越大。
你所做的是先腐 eclipse 后膨胀。这就是所谓的开放操作。这个操作的目的是去除小的噪声岛,同时(试图)保持图像中较大对象的面积。腐 eclipse 去除这些岛,而膨胀使较大对象恢复到它们的原始大小。
由于某种原因,你接着又出现了一个侵 eclipse ,我不太明白,但没关系。
我个人会做的是先执行一个闭合操作,这是一个膨胀,然后是一个腐 eclipse 。闭合有助于将靠近的区域组合成一个单一的对象。因此,你会看到有一些更大的区域彼此靠近,可能应该在我们做其他事情之前连接起来。因此,我会先做一个闭合。然后执行打开,这样我们就可以移除孤立的噪声区域。注意,我将使结束结构化元素大小更大,因为我想确保我得到附近的像素,并且使开始结构化元素大小更小,这样我就不想错误地移除任何更大的区域。
一旦你这样做了,我会用原始图像屏蔽掉任何额外的信息,这样你就可以在小岛消失的同时,让较大的区域完好无损。
使用cv2.morphologyEx,而不是将侵 eclipse 链接到扩展,或者将扩展链接到侵 eclipse ,在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)

上面的代码非常简单。首先,我读入图像,然后将图像转换为灰度和强度为5的阈值,以创建对象像素的遮罩。这是一个相当干净的图像,因此任何大于5的值似乎都可以工作。对于形态学例程,我需要将图像转换为uint8,并将遮罩缩放到255。接下来,我们创建两个结构元素一个是用于闭合操作的5 × 5矩形,另一个是用于打开操作的2 × 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);
}

结果应该与Python版本中的结果相同。

u59ebvdq

u59ebvdq2#

也可以使用skimage中的remove_small_objects函数移除小像素簇:

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)

这将显示:

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

相关问题