OpenCV 中的图像处理 004_平滑图像

本文主要内容来自于 OpenCV-Python 教程OpenCV 中的图像处理 部分,这部分的全部主要内容如下:

目标

学习:

  • 使用各种低通滤波器模糊图像
  • 将定制过滤器应用于图像(2D 卷积)

2D卷积(图像过滤)

与一维信号一样,图像也可以使用各种低通滤波器 (LPF)、高通滤波器 (HPF) 等进行滤波。LPF 有助于消除噪声、模糊图像等。HPF 过滤器有助于在图像中找到边缘。

OpenCV 提供了一个函数 **cv.filter2D()**来将内核与图像进行卷积。例如,我们将在图像上尝试平均滤波器。一个 5x5 平均滤波器内核如下所示:

K = 1 25 [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ] K = \frac{1}{25} \begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \end{bmatrix} K=2511111111111111111111111111

操作是这样的:保持这个内核高于一个像素,将所有低于这个内核的 25 个像素相加,取平均值,然后用新的平均值替换中心像素。对图像中的所有像素继续该操作。试试这段代码并检查结果:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def image_filtering():
    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    img = cv.imread(cv.samples.findFile('opencv-logo.png'))

    kernel = np.ones((5, 5), np.float32) / 25
    dst = cv.filter2D(img, -1, kernel)

    row, col, _ = img.shape

    edge = np.full((row, 10, 3), (255, 255, 255), np.uint8);
    images = [img, edge, dst]
    dst = cv.hconcat(images)

    cv.imshow("Image", dst)

    cv.waitKey(-1)
    cv.destroyAllWindows()


if __name__ == "__main__":
    image_filtering()

结果如下:

Image

图像模糊(图像平滑)

图像模糊是通过将图像与低通滤波器内核进行卷积来实现的。它对于去除噪声很有用。它实际上从图像中去除了高频内容(比如噪声,边缘)。所以在这个操作中边缘有点模糊(也有不模糊边缘的模糊技术)。OpenCV 提供了四种主要的模糊技术类型。

1. 平均

这是通过将图像与归一化框滤波器进行卷积来完成的。它只是取内核区域下所有像素的平均值并替换中心元素。这通过函数 cv.blur() 或 cv.boxFilter() 完成。检查关于内核的文档来了解更多细节。我们应该指定内核的宽度和高度。一个 3x3 归一化框滤波器将看起来像下面这样:

K = 1 9 [ 1 1 1 1 1 1 1 1 1 ] K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix} K=91111111111

注意
如果不想使用归一化的框滤波器,则使用 cv.boxFilter()。给函数传递一个参数 normalize=False

看一下下面的示例演示,它使用一个 5x5 大小的内核:

def averaging_filter():
    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    img = cv.imread(cv.samples.findFile('opencv-logo-white.png'))

    blur = cv.blur(img, (5, 5))

    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
    plt.xticks([]), plt.yticks([])
    plt.show()


if __name__ == "__main__":
    averaging_filter()

结果如下:
Image

高斯模糊

在这个方法中,不是使用框滤波器,而是使用一个高斯内核。它通过函数 cv.GaussianBlur() 完成。我们应该指定内核的宽度和高度,它们应该是正奇数。我们还应该指定 X 和 Y 方向的标准差,sigmaX 和 sigmaY。如果只指定了 sigmaX,sigmaY 将取与 sigmaX 相同的值。如果两者都为零,则它们根据内核大小计算得出。高斯模糊在从图像中移除高斯噪声非常有效。

如果你想,你可以使用函数 cv.getGaussianKernel() 创建一个高斯内核。

上面的代码可以修改以用于高斯模糊:

    blur = cv.GaussianBlur(img, (5, 5), 0)

如果使用函数 cv.getGaussianKernel(),则需要如下的代码:

    kernel = cv.getGaussianKernel(5, 0)
    kernel_2D = kernel @ kernel.transpose()
    blur = cv.filter2D(img, -1, kernel_2D)

结果如下:
Image

3. 中值模糊

这里,函数 cv.medianBlur() 取内核区域下所有像素的中值,并将中心元素替换为该中值。这对图像中的椒盐噪声非常有效。有趣的是,在上述滤波器中,中心元素是新计算的值,可能是图像中的像素值或新值。但是在中值模糊中,中心元素总是被图像中的某个像素值替换。它有效地降低了噪音。它的内核大小应该是一个正奇数。

在这个演示中,我为原始图像添加了 50% 的噪点并应用了中值模糊。添加噪点,通过如下的 salt_and_pepper(image, n) 函数完成。检查结果:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
import random

def salt_and_pepper(image, n):
    print(image.shape)
    for i in range(int(n / 2)):
        row = random.randint(0, image.shape[0] - 1)
        col = random.randint(0, image.shape[1] - 1)

        write_black = random.randint(0, 2)
        if write_black == 0:
            image[row][col] = (255, 255, 255)
        else:
            image[row][col] = (0, 0, 0)
    return image


def median_blurring():
    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    img = cv.imread(cv.samples.findFile('opencv-logo-white.png'))

    img = salt_and_pepper(img, img.shape[0] * img.shape[1])

    median = cv.medianBlur(img, 5)

    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(median), plt.title('Blurred')
    plt.xticks([]), plt.yticks([])
    plt.show()


if __name__ == "__main__":
    median_blurring()

结果如下:

Image

4. 双边滤波

cv.bilateralFilter() 函数在保持边缘锐利的同时去除噪声非常有效。但与其它滤波器相比,这个操作速度较慢。我们已经看到高斯滤波器采用像素周围的邻域,并找到其高斯加权平均值。这个高斯滤波器是一个单独的空间函数,即在滤波时考虑附近的像素。它不考虑像素是否具有几乎相同的强度。它不考虑像素是否是边缘像素。所以它也模糊了边缘,这是我们不想做的。

双边滤波在空间上也采用了高斯滤波器,但多了一个高斯滤波器,它是像素差的函数。空间的高斯函数确保只考虑附近的像素进行模糊处理,而强度差异的高斯函数确保只考虑那些与中心像素具有相似强度的像素进行模糊处理。所以它保留了边缘,因为边缘处的像素会有很大的强度变化。

下面的示例显示了双边滤波器的使用(有关参数的详细信息,请访问文档)。

def bilateral_blurring():
    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    img = cv.imread(cv.samples.findFile('bilateral.jpg'))

    img = img[0:img.shape[0], 0:int(img.shape[1] / 2)]

    blur = cv.bilateralFilter(img, 9, 75, 75)

    images = [img, blur]
    dest = cv.hconcat(images)

    cv.imshow("Image", dest)
    cv.waitKey(-1)
    cv.destroyAllWindows()


if __name__ == "__main__":
    bilateral_blurring()

结果如下:
Image

看,表面上的纹理消失了,但边缘依然存在。

其它资源

  1. 关于 双边滤波 的详细信息

练习

参考文档

Smoothing Images

使用带有高斯核的cv2.GaussianBlur和cv2.filter2D的不同结果?

利用OpenCV给彩色图像添加椒盐噪声的方法

使用Python-OpenCV向图片添加噪声的实现(高斯噪声、椒盐噪声)

Python 随机数生成

Done.

猜你喜欢

转载自blog.csdn.net/tq08g2z/article/details/123976069