运用维纳滤波实现图像去模糊(OpenCV实现)

目录

一、背景描述

二、技术应用

三、实际处理

(1)对运动模糊的处理

处理效果

(2)对均值模糊的处理

处理效果

(3)对高斯模糊的处理

处理效果

(4)对运动模糊+高斯噪声的处理

处理效果 

四、振铃现象

五、总结


一、背景描述

图像去模糊是低级计算机视觉中的一个经典问题,它的目的是从模糊的输入图像中恢复清晰的图像,旨在通过算法和数学模型来减少或消除由于摄像机抖动、物体运动或镜头失真等因素引起的图像模糊。这种技术通常用于提高图像的清晰度和可视化质量,使图像更易于分析、识别或用于其他应用。在摄影、医学成像、天文学、安防监控和计算机视觉等领域,图像去模糊都具有重要的应用价值。

什么是图像模糊呢?通常来说,图像模糊是由拍摄图像过程中的各种因素引起,包括相机抖动、目标运动以及离焦等。根据模糊图片的不同,一般将模糊图片分为如下几类:运动模糊,离焦模糊,高斯模糊,以及混合模糊。

(1)运动模糊(Motion Blur):在光照充足的条件下,当曝光时间足够短时,相机可以捕捉到清晰的图像。但是,当曝光时间相对于物体或者相机运动过长的时候,图像会产生模糊,该模糊图片一般被称为运动模糊。

(2)离焦模糊(Defocus Blur):除了运动模糊之外,图像清晰度还受到目标位置以及相机焦距的影响。在相机的成像区域中,不同目标的景深是不同的,当相机的对焦系统无法对焦到某些目标时,相机就会拍摄到离焦模糊的图片。

(3)高斯模糊(Gaussian Blur):一种常见的模糊方法,它通过对图像进行高斯滤波、高斯卷积来降低图像的高频细节,从而使图像看起来更加平滑。

(4)混合模糊(Mix Blur):当一个图片同时被多种因素影响时,造成的模糊就是混合模糊,比如相机拍摄在离焦状态下的高速运动物体时,得到的模糊就是一种混合模糊。

下面是几种模糊的具体例子:

为了评估去模糊的效果,通常采用一些图片评估算法,这些算法包括基于主观和基于客观的评测。主观方法是不需要清晰图像作参考的,一个具有代表性的指标是 MOS,评测者用 1-5 的等级对图像质量进行评分,之后对分数进行平均。对于图像去模糊的问题,大多数现有方法都是根据基于客观的指标进行评测,该方法可以进一步分为两类:有参考和无参考的指标。有参考的指标通过将恢复的图像与清晰图进行比较来评估图像质量,包括PSNR、SSIM、WSNR、MS-SSIM、IFC、NQM、UIQI、VIT、LPIPS等等,其中PSNR跟SSIM是最常用的指标。与有参考的指标不同,无参考指标仅使用去模糊的图像就可以衡量其质量。常见的指标包括BIQI、BLINDS、BRISQUE、CORNIA、NIQE、SSEQ等。此外,也有人通过测量去模糊算法对不同视觉任务(例如目标检测和识别)准确性的影响,来评估图像去模糊算法的性能。由于我们本次大作业的主要目的是通过我们编写实现图像去模糊方法的教程博客供以后选数字图像处理这门课的同学进行学习,不需要对评估指标进行而外限定,故我们的指标(metric)由我们几个组员从主观上评判图像去模糊后的效果。

由于我们在日常生活中使用手机、相机等电子产品进行拍摄时,我们得到的图片非常容易出现图像模糊的问题,因此寻找能实现图像去模糊的算法或方法对于我们现实世界中的应用这一方面有十分重要的意义。

二、技术应用

在本次实验中,我们主要采用去卷积中的维纳滤波实现图像去模糊。

去卷积领域庞大且复杂,对于运动模糊/PSF模糊/噪声/不同成像领域,用法都不尽相同,本文中总结在点扩散函数PSF(Point Spread Function)模糊的非相干成像领域的去卷积方法。下面介绍一下何为去卷积方法。

在真实世界中理想的镜头时不存在的,例如对于小孔成像,由于光的波动性,如果小孔太小,那么会产生光的衍射,导致成像的模糊,而且信噪比很低,导致图像的噪声较大。而相反,如果小孔太大,同一个像点的光会来自于多个物点,也会导致图像的模糊。所以只有在两者之间某个平衡的尺寸,才能让成像既比较清晰,又具有较低的噪声。真实世界中的成像还有许多方面的不足,因此镜头总是存在一些缺陷会导致一个物点会投影为很多点。一个理想点经过相机后成的像由点扩散函数PSF(Point Spread Function)来描述。当我们考虑镜头只有衍射这一缺陷时,那么一个理想的小点通过圆形光圈后的PSF会呈现出一种特别的形态,这是一种叫做Airy Pattern的图像,这种因为衍射形成的PSF我们称之为衍射极限PSF(Diffraction Limit PSF)。而如果对此PSF做傅里叶变换,就可以得到这个镜头的光学传递函数OTF(Optical Transfer Function)。PSF或OTF,对于成像都有影响,我们以PSF为模糊核C为例:

当我们有实际镜头的成像b,另外还知道了镜头的PSF(即图中的C),如果能找到一种方法实现已知b和c得到X,我们就能实现图像去模糊,而这种方法便是去卷积方法。

对于去卷积方法,可以分为盲去卷积(不知道模糊核)和非盲去卷积(知道模糊核)。本次大作业主要采用的是一种最具有代表性、用的最广的非盲去卷积方法——wiener去卷积法。

在真实世界中,需要考虑噪声的存在,那么我们令n为噪声,则该公式改写成:

空域的卷积对应着频域的乘法,于是:

现在我们可以把去卷积的问题看作是寻找一个频域函数H(ω),它可以使得下面的期望误差最小:

于是问题就转换为:

将B的表达式带入:

展开这个式子有:

这里因为噪声N和图像X不相关,于是

因为噪声均值为0,这样E[XN] = 0

这样我们可以把上面的最小化表达式写作:

对于下面这个lose求梯度:

并设ϑLoss / ϑH=0,那么有

分子分母同时除以,就可以得到:

那么维纳滤波就是

将C重新写作F(c), B写作F(b), 于是就得到了我们上面介绍过的维纳滤波表达式:

维纳滤波还可以改写成:

G(f))和H(f)是g和h在频率域ff的傅里叶变换

S(f)是输入信号x(t) 的功率谱

N(f)是噪声的n(t) 的功率谱

*代表复数共轭。

滤波过程可以在频率域完成:X^(f)=G(f)*Y(f),这里X^(f)是x^(t)的傅里叶变换,通过逆傅里叶变化可以得到去卷积后的结果 x^(t)。

化简后可得出:

上面我们把问题看做为了一个在频域上的最小化问题,但我们也可以把问题看做是空域上的最小化问题(L2正则化):

我们发现经过正则化后,图片的噪声大幅减少,图像质量更好。

不仅如此,我们还可以使用L1正则化等其它正则化。

原图、L1正则化与L0.8正则化的对比

三、实际处理

(1)对运动模糊的处理

import numpy as np
from numpy.fft import fft2, ifft2
import cv2




#计算得到卷积运动模糊核PSF
def motion_blur(image,image_size, angle, degree):
    image = np.array(image)
    # 这里生成任意角度的运动模糊kernel的矩阵, degree越大,模糊程度越高
    M = cv2.getRotationMatrix2D((degree / 2, degree / 2), angle, 1)
    motion_blur_kernel = np.diag(np.ones(degree))
    motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, M, (degree, degree))
    motion_blur_kernel = motion_blur_kernel / degree

    return motion_blur_kernel / motion_blur_kernel.sum()
    '''
    blurred = cv2.filter2D(image, -1, motion_blur_kernel)
    # convert to uint8
    cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
    blurred = np.array(blurred, dtype=np.uint8)
    return blurred
    '''

#给清晰图片加上卷积模糊核,给图片制造模糊
def apply_blur(input_img, psf, epsilon):
    blurred = cv2.filter2D(input_img, -1, psf)
    # convert to uint8
    cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
    blurred = np.array(blurred, dtype=np.uint8)
    return blurred

#维纳逆波实现图像去除模糊
def apply_wiener(input_img, psf, epsilon, k=0.001):
    #psf /= np.sum(psf)
    dummy = np.copy(input_img)
    dummy = fft2(dummy)
    psf = fft2(psf, s = input_img.shape)
    psf = np.conj(psf) / (np.abs(psf) ** 2 + k)
    dummy = dummy * psf
    dummy = np.abs(ifft2(dummy))
    return dummy

#数组裁切
def clip_and_cast(array):
    array = np.where(array < 0, 0, array)
    array = np.where(array > 255, 255, array)
    array = array.astype(np.uint8)
    return array

#对运动模糊进行单一通道进行处理
def motion_main_process(input_image):
    motion_blur_channels = []

    img_height, img_width = input_image.shape[:2]
    # 运动模糊模糊核
    motion_blur_psf = motion_blur(input_image,(img_height, img_width), 45, 35)

    motion_blurred_result = np.abs(apply_blur(input_image, motion_blur_psf, 1e-3))

    motion_wiener_result = apply_wiener(motion_blurred_result, motion_blur_psf, 1e-3)

    motion_blur_channels.append((clip_and_cast(motion_blurred_result), clip_and_cast(motion_wiener_result)))

    return motion_blur_channels

if __name__ == '__main__':
    input_image = cv2.imread('lena1960.jpg')
    b_channel, g_channel, r_channel = cv2.split(input_image.copy())

    motion_final_result = []
    for channel in [b_channel, g_channel, r_channel]:
        processed_channel = motion_main_process(channel)
        motion_final_result.append(processed_channel)

    motion_blurred_img = cv2.merge([motion_final_result[0][0][0], motion_final_result[1][0][0], motion_final_result[2][0][0]])

    wiener_deblurred_img = cv2.merge([motion_final_result[0][0][1], motion_final_result[1][0][1], motion_final_result[2][0][1]])

    cv2.imwrite('Motion_Blurred_Image.JPG', motion_blurred_img)
    cv2.imwrite('Wiener_Motion_Image.JPG', wiener_deblurred_img)
处理效果
原图、运动模糊处理图和维纳滤波处理图的对比

(2)对均值模糊的处理

​
import numpy as np
from numpy.fft import fft2, ifft2
import cv2

#计算得到卷积均值模糊核PSF
def mean_blur(kernel_size):
    if kernel_size % 2 == 0:
        kernel_size += 1  # Ensure kernel size is odd

    kernel = np.ones((kernel_size, kernel_size), dtype=np.float32) / kernel_size**2
    PSF =kernel/np.sum(kernel)
    return PSF

#给清晰图片加上卷积模糊核,给图片制造模糊
def apply_blur(input_img, psf, epsilon):
    blurred = cv2.filter2D(input_img, -1, psf)
    # convert to uint8
    cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
    blurred = np.array(blurred, dtype=np.uint8)
    return blurred


#维纳逆波实现图像去除模糊
def apply_wiener(input_img, psf, epsilon, k=1e-3):
    #psf /= np.sum(psf)
    dummy = np.copy(input_img)
    dummy = fft2(dummy)
    psf = fft2(psf, s = input_img.shape)
    psf = np.conj(psf) / (np.abs(psf) ** 2 + k)
    dummy = dummy * psf
    dummy = np.abs(fft.fftshift(ifft2(dummy)))
    return dummy

#数组裁切
def clip_and_cast(array):
    array = np.where(array < 0, 0, array)
    array = np.where(array > 255, 255, array)
    array = array.astype(np.uint8)
    return array

#对均值模糊进行单一通道进行处理
def mean_main_process(input_image):
    mean_blur_channels = []

    # 均值模糊模糊核
    mean_blur_psf = mean_blur(25)

    mean_blurred_result = np.abs(apply_blur(input_image, mean_blur_psf, 1e-3))

    mean_wiener_result = apply_wiener(mean_blurred_result, mean_blur_psf, 1e-3)

    mean_blur_channels.append((clip_and_cast(mean_blurred_result), clip_and_cast(mean_wiener_result)))

    return mean_blur_channels

if __name__ == '__main__':
    input_image = cv2.imread('lena1960.jpg')
    b_channel, g_channel, r_channel = cv2.split(input_image.copy())

    mean_final_result = []
    for channel in [b_channel, g_channel, r_channel]:
        processed_channel = mean_main_process(channel)
        mean_final_result.append(processed_channel)

    mean_blurred_img = cv2.merge([mean_final_result[0][0][0], mean_final_result[1][0][0], mean_final_result[2][0][0]])

    wiener_deblurred_img = cv2.merge([mean_final_result[0][0][1], mean_final_result[1][0][1], mean_final_result[2][0][1]])

    cv2.imwrite('Motion_Blurred_Image.JPG', mean_blurred_img)
    cv2.imwrite('Wiener_Motion_Image.JPG', wiener_deblurred_img)

​
处理效果
原图、均值模糊处理图和维纳滤波处理图的对比

(3)对高斯模糊的处理

import numpy as np
from numpy.fft import fft2, ifft2
from scipy.signal import gaussian
import cv2




#得到卷积高斯模糊核PSF
def gaussian_kernel(kernel_size ):
	h = gaussian(kernel_size, kernel_size / 3).reshape(kernel_size, 1)
	h = np.dot(h, h.transpose())
	h /= np.sum(h)
	return h

#给清晰图片加上卷积模糊核,给图片制造高斯模糊
def apply_blur(input_img, psf, epsilon):
    blurred = cv2.filter2D(input_img, -1, psf)
    # convert to uint8
    cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
    blurred = np.array(blurred, dtype=np.uint8)
    return blurred

#维纳逆波实现图像去除模糊
'''
def apply_wiener(input_img, psf, epsilon, k=0.001):
    s = psf.shape
    input_fft = fft.fft2(input_img, s)
    psf_fft = fft.fft2(psf) + epsilon
    psf_fft_1 = np.conj(psf_fft) / (np.abs(psf_fft) ** 2 + k)
    result = fft.ifft2(input_fft * psf_fft_1)
    result = np.abs(fft.fftshift(result))
    return result
'''
def apply_wiener(input_img, psf, epsilon, k=0.001):
    #psf /= np.sum(psf)
    dummy = np.copy(input_img)
    dummy = fft2(dummy)
    psf = fft2(psf, s = input_img.shape)
    psf = np.conj(psf) / (np.abs(psf) ** 2 + k)
    dummy = dummy * psf
    dummy = np.abs(ifft2(dummy))
    return dummy

#数组裁切
def clip_and_cast(array):
    array = np.where(array < 0, 0, array)
    array = np.where(array > 255, 255, array)
    array = array.astype(np.uint8)
    return array


#对高斯模糊进行单一通道进行处理
def gaussian_main_process(input_image):
    gaussian_blur_channels = []

    img_height, img_width = input_image.shape[:2]
    # 高斯模糊模糊核
    gaussian_blur_psf = gaussian_kernel(15)

    gaussian_blurred_result = np.abs(apply_blur(input_image, gaussian_blur_psf, 1e-3))

    new_gaussian_blur_psf = gaussian_kernel(15)
    gaussian_wiener_result = apply_wiener(gaussian_blurred_result, new_gaussian_blur_psf, 1e-3)

    gaussian_blur_channels.append((clip_and_cast(gaussian_blurred_result), clip_and_cast(gaussian_wiener_result)))

    return gaussian_blur_channels


if __name__ == '__main__':
    input_image = cv2.imread('lena1960.jpg')
    b_channel, g_channel, r_channel = cv2.split(input_image.copy())


    gaussian_final_result = []
    for channel in [b_channel, g_channel, r_channel]:
        processed_channel = gaussian_main_process(channel)
        gaussian_final_result.append(processed_channel)

    gaussian_blurred_img = cv2.merge([gaussian_final_result[0][0][0], gaussian_final_result[1][0][0], gaussian_final_result[2][0][0]])

    wiener_deblurred_img = cv2.merge([gaussian_final_result[0][0][1], gaussian_final_result[1][0][1], gaussian_final_result[2][0][1]])

    cv2.imwrite('Gaussian_Blurred_Image.JPG', gaussian_blurred_img)
    cv2.imwrite('Wiener_Deblurred_Image.JPG', wiener_deblurred_img)
处理效果
原图、高斯模糊处理图和维纳滤波处理图的对比

(4)对运动模糊+高斯噪声的处理

import os
import numpy as np
from numpy.fft import fft2, ifft2
from scipy.signal import gaussian, convolve2d
import matplotlib.pyplot as plt
import cv2

#添加运动模糊
def add_motion_blur(img, kernel_size = 3):
	dummy = np.copy(img)
	h = np.eye(kernel_size) / kernel_size
	dummy = convolve2d(dummy, h, mode = 'valid')
	return dummy
#添加高斯噪声
def add_gaussian_niose(img, sigma):
	gauss = np.random.normal(0, sigma, np.shape(img))
	noisy_img = img + gauss
	noisy_img[noisy_img < 0] = 0
	noisy_img[noisy_img > 255] = 255
	return noisy_img

#维纳滤波器
def wiener_filter(img, kernel, K):
	kernel /= np.sum(kernel)
	dummy = np.copy(img)
	dummy = fft2(dummy)
	kernel = fft2(kernel, s = img.shape)
	kernel = np.conj(kernel) / (np.abs(kernel) ** 2 + K)
	dummy = dummy * kernel
	dummy = np.abs(ifft2(dummy))
	return dummy
#高斯核
def gaussian_kernel(kernel_size = 3):
	h = gaussian(kernel_size, kernel_size / 3).reshape(kernel_size, 1)
	h = np.dot(h, h.transpose())
	h /= np.sum(h)
	return h
#如果想将输入的彩色图片转换成灰色图片后进行模糊化和去模糊化,可以在读取图片时使用这个函数
def rgb2gray(rgb):
	return np.dot(rgb[...,:3], [0.2989, 0.5870, 0.1140])


if __name__ == '__main__':
	input_image = cv2.imread('lena1960.jpg')

	# 将图像划分成三通道
	b_channel, g_channel, r_channel = cv2.split(input_image.copy())
	# 在这个循环内给每个通道用 blur(img, kernel_size=15) 添加运动模糊
	motion_blurred_channels = [add_motion_blur(channel, kernel_size=15) for channel in [b_channel, g_channel, r_channel]]
	motion_blurred_img = cv2.merge(motion_blurred_channels)

	# 将经过运动模糊后图像划分成三通道
	mb_channel, mg_channel, mr_channel = cv2.split(motion_blurred_img.copy())
	# 在这个循环内给每个通道用 add_gaussian_noise(img, sigma=20) 添加高斯噪声
	motion_gaussian_blurred_channels = [add_gaussian_niose(channel, sigma=20) for channel in [mb_channel, mg_channel, mr_channel]]
	motion_gaussian_blurred_img = cv2.merge(motion_gaussian_blurred_channels)

	# 保存运动模糊和加入高斯噪声后的运动模糊图像
	cv2.imwrite('Motion_Blurred_Image.JPG', motion_blurred_img)
	cv2.imwrite('Gaussian_Blurred_Image.JPG', motion_gaussian_blurred_img)

	# 使用Wiener滤波器进行运动模糊图像的去模糊
	estimated_kernel = gaussian_kernel(15)  # 估计的模糊核
	motion_filtered_channels = [wiener_filter(channel, estimated_kernel, K=0.01) for channel in motion_blurred_channels]
	motion_filtered_img = cv2.merge(motion_filtered_channels)
	cv2.imwrite('Wiener_Motion_Deblurred_Image.JPG', motion_filtered_img)

	# 使用Wiener滤波器进行高斯模糊图像的去模糊
	new_estimated_kernel = gaussian_kernel(3)  # 估计的模糊核
	motion_gaussian_filtered_channels = [wiener_filter(channel, new_estimated_kernel, K=0.01) for channel in motion_gaussian_blurred_channels]
	motion_gaussian_filtered_img = cv2.merge(motion_gaussian_filtered_channels)
	cv2.imwrite('Wiener_Gaussian_Motion_Deblurred_Image.JPG', motion_gaussian_filtered_img)









处理效果 
原图、运动模糊处理图、高斯噪声处理图、维纳滤波对运动模糊处理图和维纳滤波对高斯噪声处理图的对比

四、振铃现象

图像处理中,对一幅图像进行滤波处理,若选用的频域滤波器具有陡峭的变化,则会使滤波图像产生“振铃”。

产生的可能原因如下:
1) 滤波器噪声振铃En:观测像素中存在不可避免的测量和量化误差。
2) 滤波器偏差振铃Ed:点扩散函数PSF频域响应有零点,正则化过程把一个不适定的问题转换成适定的,导致复原滤波器与原来的PSF的逆卷积的偏差。
3) 边界截断振铃Eb:图像在边界处梯度剧变,对图像做傅里叶变换时,周期延拓导致边界处梯度剧变,引起振铃。

经过网上查资料,发现可以用扩展边界预处理方法(去噪、平滑)可以很好地消除边界振铃。

五、总结

维纳滤波的带通频率依赖于信噪比SNR(f)=S(f)/N(f)。当信噪比趋近于无穷(即噪声为零),G(f)增大,维纳滤波简化成逆滤波;当噪声增加时,信噪比降低,G(f)也会减小。因此,我们可以通过适当调节信噪比的取值,这样有助于提高噪声模糊图像的复原效果。

猜你喜欢

转载自blog.csdn.net/qq3098320650/article/details/135292563
今日推荐