计算机视觉5:图像平滑

图像平滑主要是消除噪音。一些噪音干扰使图像退化,质量下降,表现为图像模糊、特征淹没、对图像分析不利。去除噪音、恢复原始图像是图像处理中一个重要的内容。

1. 图像平滑基础

图像平滑是一种实用的数字图像处理技术。

图像平滑分为空间域图像平滑技术和频率域图像平滑技术。空间域图像平滑技术有邻域平均法、空间低通滤波、多图像平均、中值滤波。在频率域,可以采用各种形式的低通滤波器的方法减少噪声。

图像平滑属于邻域增强的一种方式。平滑的主要目的有两个:消除噪声以及改善图像质量和抽出对象特征。

平滑处理时需要用到一个滤波器。

滤波的意思就是对源图像的每个像素周围一定范围内的像素进行运算,运算的范围就称为淹膜或领域。

如果运算只是对各像素灰度值进行简单处理(如乘一个权值)最后求和,就称为线性滤波;如果对像素灰度值的运算比较复杂,而不是最后求和的简单运算,则是非线性滤波

最常用的滤波器时线性滤波器,线性滤波处理的输出像素值g(i , j)是输入像素值f( i + k, j + l )的加权和,如下所示:

g(i, j) = \sum_{k,j}f(i+k,j+l)h(k,l)

h(k,l)称为核,仅仅是一个加权系数。

非线性平滑可消除这些孤立的噪声点,对图像的细节影响不大,但是对物体的边缘会带来一定的失真。

2. 线性滤波

2.1 归一化方框滤波器

归一化方框滤波器(Normalized Box Filter)是最简单的滤波器,输出像素值是核窗口内像素值的均值(所有像素加权系数相等),如下图:

方框滤波和均值滤波核基本上是一致的,主要区别是要不要归一化处理,如果使用归一化处理,方框滤波就是均值滤波。实际上,均值滤波是方框滤波归一化后的特殊情况。

均值滤波不能很好的保护细节。OpenCV提供了blur函数实现均值滤波操作,函数声明如下:

cv.blur(src, ksize[, dst[, anchor[, borderType]]]) -> dst

  • src:表示输入图像,图像深度是CV_8U、CV_16U、CV_16S、CV_32F以及CV_64F其中的一个;
  • ksize:表示滤波模板kernel的尺寸,一般使用Size(w, h)来指定,如Size(3, 3);
  • dst:表示输出图像,深度和类型与输入图像一致;
  • anchor:表示锚点,也就是处理的像素位于kernel的什么位置,默认值为(-1, -1),即位于kernel中心;
  • border:用于推断图像外部像素的某种边界模式,默认值是BORDER_DEFAULT。

代码实例:实现均值滤波

import cv2 as cv 
 
g_nTrackbarMaxValue = 9;             #定义轨迹条最大值
g_nTrackbarValue=0                   #定义轨迹条初始值
g_nKernelValue=0                     #定义kernel尺寸
g_srcImage = cv.imread("cat.png")
g_dstImage = cv.imread("cat.png")
windowName='Mean filtering'          # 定义窗口的名称。

def on_kernelTrackbar(x):    # 定义回调函数 x:轨迹条的位置值
	global g_nKernelValue
	# 根据输入值重新计算kernel尺寸
	# 使用cv.getTrackbarPos获取轨迹条的当前位置值,并计算核心的尺寸。核心尺寸是轨迹条位置值的两倍加1。
	g_nTrackbarValue = cv.getTrackbarPos('res',windowName)
	g_nKernelValue = g_nTrackbarValue * 2 + 1
	#均值滤波函数  根据计算结果创建一个元组,表示滤波器核心的大小。
	ksize = (g_nKernelValue,g_nKernelValue)
	# 应用均值滤波,模糊原始图像g_srcImage,并将结果存储在g_dstImage。
	cv.blur(g_srcImage,ksize, g_dstImage,)

cv.namedWindow('src', cv.WINDOW_AUTOSIZE)     #定义窗口显示属性
cv.imshow('src',g_srcImage)
cv.namedWindow(windowName)
# 在指定的窗口中创建一个轨迹条,轨迹条的名称为"res",范围从0到9,初始位置为0,
# 当轨迹条的值改变时调用on_kernelTrackbar函数。
cv.createTrackbar('res',windowName, 0, 9, on_kernelTrackbar)
 
while (True):
    cv.imshow(windowName, g_dstImage)   # 在指定窗口中显示滤波后的图像。
	# 如果按下了'q'键,则退出循环。
    if cv.waitKey(1) == ord('q'):
        break
cv.destroyAllWindows()

运行效果:

2.2 高斯滤波器

高斯滤波(Gaussian Filter)是一种线性平滑滤波,对于除去高斯噪声有很好的效果。

高斯滤波,是通过对输入数组的每个点与输入的高斯滤波模板执行卷积计算后,将这些结果一块组成滤波后的输出数组,通常的讲就是高斯滤波是对整幅图像进行加权平均的过程,每一个像素点的值都是由其本身和领域内的其他像素值经过加权平均后得到。

在图像处理中,高斯滤波一般有两种实现方式:一种是用离散化窗口滑窗卷积,另一种是通过傅里叶变换。最常见的是第一种滑窗方式。

学习高斯滤波之前,必须先学习卷积的知识:

  • 卷积核(kernel):用来对图像矩阵进行平滑的矩阵,也称为过滤器(filter)。
  • 锚点:卷积核和图像矩阵重叠,进行内积运算后,锚点位置的像素点会被计算值取代。一般选取奇数卷积核的中心点作为锚点。
  • 步长:卷积核沿着图像矩阵每次移动的长度。
  • 内积:卷积核和图像矩阵对应像素点相乘,然后相加得到一个总和(不要和矩阵乘法混淆)。

高斯平滑即采用高斯卷积核对图像矩阵进行卷积操作。

高斯滤波和高斯模糊,就是依据滤波器是低通滤波器还是高通滤波器来区分的。

高斯滤波是用高斯函数作为滤波函数的滤波操作,而高斯模糊是用高斯低通滤波器进行的滤波操作。

二维高斯函数表达式如下:

在OpenCV中,函数GaussianBlur可以实现高斯滤波,函数声明如下:

cv.GaussianBlur(src, ksize, sigmaX [, dst [, sigmaY [ , borderType ]]] )  →  dst

  • src:表示输入图像;
  • dst:表示输出图像;
  • ksize:表示内核大小;
  • sigmaX:表示高斯核函数在X方向的标准偏差;
  • sigmaY:表示高斯核函数在Y方向的标准偏差;
  • borderType:表示边界模式,默认值为BORDER_DEFAULT。

高斯滤波需要用到高斯滤波器,即卷积核。

算法步骤如下:

  1. 根据公式,计算高斯卷积核内的具体值。此处用到建立二维高斯卷积核,在编写过程中省去了系数部分,并且对公式做了小小的修改,最后进行归一化。
  2. 对输入图像进行灰度化处理。
  3. 遍历灰度图像素点,对像素点邻域(和高斯卷积核一般大)进行高斯滤波。

代码实例:

import cv2 as cv
import math
import numpy as np

# 灰度化处理
def rgb2gray(img):   # 将RGB图像转换为灰度图像:
    h=img.shape[0]
    w=img.shape[1]
    # 创建一个零矩阵img1,用于存储灰度图像。
    img1=np.zeros((h,w),np.uint8)
    # 使用嵌套循环遍历每个像素,根据灰度转换公式计算灰度值。
    for i in range(h):
        for j in range(w):
            img1[i,j]=0.144*img[i,j,0]+0.587*img[i,j,1]+0.299*img[i,j,1]
    return img1

# 计算高斯卷积核
def gausskernel(size):   # size:卷积核的大小。
    sigma=1.0
    # 创建一个零矩阵gausskernel,用于存储高斯卷积核。
    gausskernel=np.zeros((size,size),np.float32)
    # 使用嵌套循环计算高斯核的每个元素值。
    for i in range (size):
        for j in range (size):
            norm=math.pow(i-1,2)+pow(j-1,2)
            gausskernel[i,j]=math.exp(-norm/(2*math.pow(sigma,2)))   # 求高斯卷积
    # 计算卷积核的总和并进行归一化。
    sum=np.sum(gausskernel)   # 求和
    kernel=gausskernel/sum   # 归一化
    return kernel

# 高斯滤波
def gauss(img):
    h=img.shape[0]
    w=img.shape[1]
    # 创建一个零矩阵img1,用于存储滤波后的图像。
    img1=np.zeros((h,w),np.uint8)
    kernel=gausskernel(3)   # 计算高斯卷积核
    # 使用嵌套循环实现高斯滤波过程。
    for i in range (1,h-1):
        for j in range (1,w-1):
            sum=0
            for k in range(-1,2):
                for l in range(-1,2):
                    sum+=img[i+k,j+l]*kernel[k+1,l+1]   # 高斯滤波
            img1[i,j]=sum
    return img1

image = cv.imread("lena.png")
# 将RGB图像转换为灰度图像,并存储在grayimage。
grayimage = rgb2gray(image)
# 对灰度图像进行高斯滤波,并存储结果在gaussimage。
gaussimage = gauss(grayimage)
cv.imshow("image",image)
cv.imshow("grayimage",grayimage)
cv.imshow("gaussimage",gaussimage)

cv.waitKey(0)
cv.destroyAllWindows()
 

3. 非线性滤波

3.1 中值滤波

中值滤波(Median Filter)用像素点领域灰度值的中值来代替该像素点的灰度值,也就是说用一片区域的中间值来代替所有值,可以除去最大值、最小值。

中值滤波的优点:是对除去斑点噪声和淑盐噪声很有用,均值滤波噪声也被参与运算;

中值滤波的缺点:是中值滤波时间在均值滤波的5倍以上。

中值平滑也有核,但是并不进行卷积计算,而是对和中所有像素值排序得到中间值,用该中间值来代替锚点值。

在OpenCV中,可以利用medianBlur()来进行中值平滑,声明如下:

medianBlur(src, ksize[, dst]) → dst

  • src:为被滤波图片,需要1、3或4通道的图像;
  • dst:滤波后的输出结果;
  • ksize:是mask的大小,比如,若用3*3的模板,ksize就传值3。

代码实例:

import cv2 as cv
import numpy as np

def blur_demo(src):
	# 使用cv.medianBlur函数对源图像src应用中值模糊,核心大小为7x7。
	dst = cv.medianBlur(src,   7)
	cv.imshow("medianBlur_res", dst)

if __name__ == "__main__":
	cv.namedWindow("src");
	src = cv.imread("test.jpg")
	cv.imshow("src", src)
	blur_demo(src)
	
	if cv.waitKey(0) == 27:
   		cv.destroyAllWindows()

效果图:

3.2 双边滤波

有些时候这些滤波器不仅仅消弱了噪声,还把边缘也给磨掉了。为了一定程度上避免这样的情形,我们可以使用双边滤波。

双边滤波是一种非线性滤波器,类似与高斯滤波,也给每一个邻域像素分配一个加权系数,可以达到保持边缘、降噪平滑的效果。

双边滤波与高斯滤波相比,对于图像的边缘信息能够更好地保存。

双边滤波的操作主要是通过cv.bilateralFilter函数来操作的,有美颜的效果,函数声明如下:

cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) → dst

  • src:表示输入图像;
  • d:表示过滤时周围每个像素领域的直径;
  • sigmaColor:表示在color space中过滤sigma,参数越大,临近像素将会在越远的地方混合;
  • sigmaSpace:表示在coordinate space中过滤sigma,参数越大,哪些足够相近的颜色影响越大。

代码实例:

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import random
import math

img = cv.imread(r"cat.png")
# 参数0是滤波器的大小,参数0.2是颜色空间中的滤波强度,值越小表示滤波越强。
# 参数40是空间域中的滤波强度,值越大表示滤波越强。
img_bilateral = cv.bilateralFilter(img,0,0.2,40)

cv.imshow("img",img)
cv.imshow("img_bilateral",img_bilateral)
cv.waitKey(0)
cv.destroyAllWindows()

参考学习书总结:OpenCv4.5 计算机视觉开发实战(基于python)

代码来自书本,供自己学习复习使用,继续学习ing...

  • 14
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听说你还在搞什么原创~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值