利用反卷积执行图像去模糊
- 0. 前言
- 1. 图像模糊检测
- 1.1 拉普拉斯 (Laplacian) 方差阈值
- 1.2 使用 OpenCV 执行模糊检测
- 2. 使用 SimpleITK 反卷积滤波器实现非盲去模糊
- 2.1 去模糊分类
- 2.2 实现非盲去模糊
- 3. 使用 scikit-image 执行非盲去模糊
- 小结
- 系列链接
0. 前言
我们已经知道可以使用低通滤波器执行模糊操作,并减弱图像中较高频域。模糊操作(例如,高斯模糊)是线性的,在数学意义上是可逆的,但在实践中,该问题的逆过程计算起来非常困难。在本节中,我们将学习如何解决以下两个问题:
- 如何通过对图像的拉普拉斯方差进行简单的阈值处理来检测图像是否模糊
- 如何使用
SimpleItk
和scikit-image
库函数的反卷积算法来消除图像中的模糊
1. 图像模糊检测
1.1 拉普拉斯 (Laplacian) 方差阈值
在本节中,我们将学习如何通过使用 OpenCV
库计算图像的拉普拉斯 (Laplacian
) 方差阈值来检测图像是否模糊。执行模糊检测的步骤如下:
- 如果图像足够清晰,则拉普拉斯运算将在图像中检测水平和垂直边缘,以获得输出图像在给定范围内的方差
- 相反,如果图像相对模糊,则拉普拉斯运算在图像中并不能检测到足够的细节,从而导致输出方差小于给定阈值
该过程对于阈值非常敏感,因此需要选择合适的阈值。
1.2 使用 OpenCV 执行模糊检测
在本节中,我们将使用灰度图像作为输入图像,并且为了进行演示,我们需要生成模糊的图像(通过使用不同类型的模糊核并与输入图像执行卷积),以测试我们实现的模糊检测函数。
(1) 我们首先使用 convolve()
函数实现 2D
(空间)卷积,并将卷积输出值缩放至 [0,1]
范围内:
import scipy.fftpack as fp
from skimage.io import imread
from skimage.color import rgb2gray
import numpy as np
import matplotlib.pylab as plt
from scipy.signal import convolve2d
import cv2
def convolve(im, kernel):
im1 = convolve2d(im, kernel, mode='same')
return im1 / np.max(im1)
(2) 接下来,使用在前言中介绍的方法实现函数 check_if_blurry()
检查图像是否模糊。该函数接受输入图像和要使用的阈值作为参数,并返回 Laplacian
方差以及图像是否模糊。
def check_if_blurry(image, threshold):
# compute the Laplacian of the image and then return the focus
# measure, which is simply the variance of the Laplacian
var = cv2.Laplacian(image, cv2.CV_64F).var()
return 'Var Laplacian = {}\n{}'.format(round(var, 6), 'Blurry' if var < threshold else 'Not Blurry')
(3) 定义函数 plot_blurry()
以用 matplotlib.pyplot
的 imshow()
绘制模糊输入图像:
def plot_blurry(im, title):
plt.imshow(im), plt.axis('off'), plt.title(title, size=10)
(4) 构造一个大小为 15x15
、参数
σ
=
3
σ=3
σ=3 的高斯模糊核,通过与核执行卷积来模糊图像。
def get_gaussian_edge_blur_kernel(sigma, sz=15):
# First create a 1-D Gaussian kernel
x = np.linspace(-10, 10, sz)
kernel_1d = np.exp(-x**2/sigma**2)
kernel_1d /= np.trapz(kernel_1d) # normalize the sum to 1.0
# create a 2-D Gaussian kernel from the 1-D kernel
kernel_2d = kernel_1d[:, np.newaxis] * kernel_1d[np.newaxis, :]
return kernel_2d
(5) 将阈值定义为 0.01
,并在模糊图像上调用函数 check_if_blurry()
以返回图像是否模糊以及 laplacian
的方差。将原始图像和模糊图像插入列表中,以便进行绘制:
threshold = 0.01
imlist = []
im = rgb2gray(imread('1.png'))
imlist.append((im, 'original image\n' + check_if_blurry(im, threshold)))
kernel = get_gaussian_edge_blur_kernel(3)
im1 = convolve(im, kernel)
imlist.append((im1, '(edge) blurred image\n' + check_if_blurry(im1, threshold)))
(6) 创建一个长度为 9
、角度为 45°
的 15x15
运动模糊核,用该核模糊图像,并检查图像是否模糊:
def get_motion_blur_kernel(ln, angle, sz=15):
kern = np.ones((1, ln), np.float32)
angle = -np.pi*angle/180
c, s = np.cos(angle), np.sin(angle)
A = np.float32([[c, -s, 0], [s, c, 0]])
sz2 = sz // 2
A[:,2] = (sz2, sz2) - np.dot(A[:,:2], ((ln-1)*0.5, 0))
kern = cv2.warpAffine(kern, A, (sz, sz), flags=cv2.INTER_CUBIC)
return kern
kernel = get_motion_blur_kernel(9, 45)
im1 = convolve(im, kernel)
imlist.append((im1, '(motion) blurred image\n' + check_if_blurry(im1, threshold)))
(7) 最后,生成半径 7
的 15x15
失焦核,用此核模糊图像,然后检查图像是否模糊:
def get_out_of_focus_kernel(r, sz=15):
kern = np.zeros((sz, sz), np.uint8)
cv2.circle(kern, (sz, sz), r, 255, -1, cv2.LINE_AA, shift=1)
kern = np.float32(kern) / 255
return kern
kernel = get_out_of_focus_kernel(7)
im1 = convolve(im, kernel)
imlist.append((im1, '(out-of-focus) blurred image\n' + check_if_blurry(im1, threshold)))
(8) 绘制原始图像和模糊的图像,并显示关于图像是否模糊的相关信息:
plt.figure(figsize=(20,7))
plt.gray()
for i in range(len(imlist)):
im, title = imlist[i]
plt.subplot(1,4,i+1), plot_blurry(im, title)
plt.tight_layout()
plt.show()
从上图中可以看到,原始图像的 laplacian
的方差显著高于模糊图像。我们也可以使用不同的模糊核和阈值来检查模糊检测方法对阈值的敏感性。
2. 使用 SimpleITK 反卷积滤波器实现非盲去模糊
2.1 去模糊分类
图像反卷积是指一类试图从模糊图像中恢复清晰图像的算法,其使用模糊图像和可能的卷积核 h
(假设卷积核是已知的或估计的)作为输入。如果模糊核 h
已知,则去模糊过程称为非盲取模糊 (non-blind deblurring
),反之称为盲去模糊 (blind deblurring
)。在本节中,我们将学习如何使用 SimpleITK
库的反卷积滤波器执行非盲去模糊。
2.2 实现非盲去模糊
(1) 首先,读取输入图像,并使用失焦模糊核模糊该图像:
import SimpleITK as sitk
from scipy import signal
from skimage.metrics import peak_signal_noise_ratio
from skimage.io import imread
from skimage.color import rgb2gray
import numpy as np
import matplotlib.pylab as plt
import cv2
def get_out_of_focus_kernel(r, sz=15):
kern = np.zeros((sz, sz), np.uint8)
cv2.circle(kern, (sz, sz), r, 255, -1, cv2.LINE_AA, shift=1)
kern = np.float32(kern) / 255
return kern
im = rgb2gray(imread('1.png'))
psf = get_out_of_focus_kernel(7, 9).astype(np.float)
im_blur = signal.convolve2d(im, psf, 'same')
im_blur = im_blur / np.max(im_blur)
(2) 使用逆滤波器恢复图像是最直接的反卷积方法。根据卷积定理,空间域中的两个图像的卷积等价于两个图像的傅立叶变换乘积,因此逆滤波器需要对乘法求逆。换句话说,逆滤波器计算公式如下所示:
F ^ ( u , v ) = { G ( u , v ) H ( u , v ) ∣ H ( u , v ) ∣ ≥ ϵ 0 o t h e r w i s e \hat F(u,v)=\left\{ \begin{array}{rcl} \frac {G(u,v)} {H(u,v)} & & {|H(u,v)|≥\epsilon}\\ 0 & & {otherwise} \end{array} \right. F^(u,v)={H(u,v)G(u,v)0∣H(u,v)∣≥ϵotherwise
(3) 使用 SimpleITK.InverseDeconvolutionImageFilter()
函数实现直接线性反卷积滤波器:
tkfilter = sitk.InverseDeconvolutionImageFilter()
tkfilter.SetNormalize(True)
im_res_IN = sitk.GetArrayFromImage(tkfilter.Execute (sitk.GetImageFromArray(im_blur), sitk.GetImageFromArray(psf)))
(4) 使用 Wiener
反卷积图像滤波器恢复与模糊核卷积后得到的图像,同时将噪声降至最低。
Wiener
滤波器旨在最大程度地减少低信噪比 (signal-to-noise ratio
, SNR
) 频率引起的噪声。Wiener
滤波器核是在频域中定义的,如下公式所示( SNR
越高,恢复的图像质量越高):
W ( u , v ) = H ∗ ( u , v ) ∣ H ( u , v ) ∣ 2 + K ( u , v ) = H ∗ ( u , v ) ∣ H ( u , v ) ∣ 2 + 1 S N R ( u , v ) K ( u , v ) = S η ( u , v ) S f ( u , v ) W(u,v)=\frac {H^*(u,v)} {|H(u,v)|^2+K(u,v)}=\frac {H^*(u,v)} {|H(u,v)|^2+\frac 1 {SNR(u,v)}}\\ K(u,v)=\frac {S_{\eta}(u,v)}{S_f(u,v)} W(u,v)=∣H(u,v)∣2+K(u,v)H∗(u,v)=∣H(u,v)∣2+SNR(u,v)1H∗(u,v)K(u,v)=Sf(u,v)Sη(u,v)
使用 SimpleITK.WienerDeconvolutionImageFilter()
函数实现 Wiener
滤波器。在这里,模糊后未向原始图像添加额外的噪音。因此,噪声方差设置为零:
tkfilter = sitk.WienerDeconvolutionImageFilter()
tkfilter.SetNoiseVariance(0)
tkfilter.SetNormalize(True)
im_res_WN = sitk.GetArrayFromImage(tkfilter.Execute (sitk.GetImageFromArray(im_blur), sitk.GetImageFromArray(psf)))
(5) 使用 Tikhonov
方法恢复图像。该方法通过在分母中添加正则化项来改进估计的反卷积滤波器(去模糊),该滤波器最小化以下公式中:
m i n f ^ ∣ ∣ f ^ ⊗ h − g ∣ ∣ L 2 2 + μ ∣ ∣ f ^ ∣ ∣ 2 T ( u , v ) = H ∗ ( u , v ) ∣ H ( u , v ) ∣ 2 + μ \underset {\hat f} {min} ||\hat f \otimes h-g||^2_{L_2}+ \mu ||\hat f||^2 \\ T(u,v)=\frac {H^*(u,v)} {|H(u,v)|^2+\mu} f^min∣∣f^⊗h−g∣∣L22+μ∣∣f^∣∣2T(u,v)=∣H(u,v)∣2+μH∗(u,v)
其中,
f
^
\hat f
f^ 表示去模糊图像
f
f
f 的估计值;
h
h
h 表示模糊核;
g
g
g 表示模糊图像;项
μ
μ
μ 在此滤波器中称为正则化,如果将μ设置为零,则该滤波器等价于逆滤波器。以下代码使用 simpleitk.tikhonovdeconvolutionImageFilter()
函数实现 Tikhonov
方法正则化的逆反卷积滤波器。
其中,将正则化常数设置为 0.008
,我们也可以尝试不同的值,并观察不同值到对去模糊输出图像的影响:
tkfilter = sitk.TikhonovDeconvolutionImageFilter()
tkfilter.SetRegularizationConstant(0.008) #0.06)
tkfilter.SetNormalize(True)
im_res_TK = sitk.GetArrayFromImage(tkfilter.Execute (sitk.GetImageFromArray(im_blur), sitk.GetImageFromArray(psf)))
(6) 使用 Richardson-Lucy
算法恢复图像,该滤波器实现了 Richardson-Lucy
反卷积算法。该算法假定输入图像是由具有已知核的线性移位不变系统形成的。迭代算法使用贝叶斯的方法来改善每次迭代的估计(去模糊)图像:
f i + 1 ( x ) = { [ c ( x ) f i ( x ) ⊗ g ( x ) ] ⊗ g ( − x ) } f i ( x ) f_{i+1}(x)=\{[\frac {c(x)} {f_i(x)\otimes g(x)}]\otimes g(-x)\}f_i(x) fi+1(x)={[fi(x)⊗g(x)c(x)]⊗g(−x)}fi(x)
其中
⊗
\otimes
⊗ 表示卷积操作,
g
(
x
)
g(x)
g(x) 表示 PSF
,
c
(
x
)
c(x)
c(x) 表示模糊图像,
f
i
(
x
)
f_i(x)
fi(x) 表示去模糊图像。
以下代码使用 SimpleItk.RichardsonLucyDeconVolutionImageFilter()
函数利用 Richardson-Lucy
反卷积算法对输入图像执行反卷积。我们可以尝试使用不同的迭代次数,并观察对输出图像的影响:
tkfilter = sitk.RichardsonLucyDeconvolutionImageFilter()
tkfilter.SetNumberOfIterations(100)
tkfilter.SetNormalize(True)
im_res_RL = sitk.GetArrayFromImage(tkfilter.Execute (sitk.GetImageFromArray(im_blur), sitk.GetImageFromArray(psf)))
(7) 最后,绘制所有原始、模糊和使用不同反卷积算法得到的去模糊图像并计算 PSNR
值(以比较使用不同反卷积算法得到的去模糊图像的质量):
plt.figure(figsize=(20, 60))
plt.subplots_adjust(0,0,1,1,0.07,0.07)
plt.gray()
plt.subplot(231), plt.imshow(im), plt.axis('off'), plt.title('Original Image', size=10)
plt.subplot(232), plt.imshow(im_blur), plt.axis('off'), plt.title('Blurred (out-of-focus) Image, PSNR={:.3f}'.format(peak_signal_noise_ratio(im, im_blur)), size=10)
plt.subplot(233), plt.imshow(im_res_IN, vmin=im_blur.min(), vmax=im_blur.max()), plt.axis('off')
plt.title('Deconvolution using SimpleITK (Inverse Deconv.), PSNR={:.3f}'.format(peak_signal_noise_ratio(im, im_res_IN)), size=10)
plt.subplot(234), plt.imshow(im_res_WN, vmin=im_blur.min(), vmax=im_blur.max()), plt.axis('off')
plt.title('Deconvolution using SimpleITK (Wiener Deconv.), PSNR={:.3f}'.format(peak_signal_noise_ratio(im, im_res_WN)), size=10)
plt.subplot(235), plt.imshow(im_res_RL, vmin=im_blur.min(), vmax=im_blur.max()), plt.axis('off')
plt.title('Deconvolution using SimpleITK (Richardson-Lucy), PSNR={:.3f}'.format(peak_signal_noise_ratio(im, im_res_RL)), size=10)
plt.subplot(236), plt.imshow(im_res_TK, vmin=im_blur.min(), vmax=im_blur.max()), plt.axis('off')
plt.title('Deconvolution using SimpleITK (Tikhonov Deconv.), PSNR={:.3f}'.format(peak_signal_noise_ratio(im, im_res_TK)), size=10)
plt.show()
3. 使用 scikit-image 执行非盲去模糊
在本节中,我们将继续学习使用 Richardson-Lucy
算法学习如何实现非盲反卷积,但本节使用了 scikit-image
的 restoration
模块。绘制输出(去模糊)图像及其 PSNR
值:
from skimage import restoration
from scipy import signal
from skimage.metrics import peak_signal_noise_ratio
from skimage.io import imread
from skimage.color import rgb2gray
import numpy as np
import matplotlib.pylab as plt
import cv2
def get_out_of_focus_kernel(r, sz=15):
kern = np.zeros((sz, sz), np.uint8)
cv2.circle(kern, (sz, sz), r, 255, -1, cv2.LINE_AA, shift=1)
kern = np.float32(kern) / 255
return kern
im = rgb2gray(imread('1.png'))
psf = get_out_of_focus_kernel(7, 9).astype(np.float)
im_blur = signal.convolve2d(im, psf, 'same')
im_blur = im_blur / np.max(im_blur)
im_res_RL = restoration.richardson_lucy(im_blur, psf, iterations=20)
plt.figure(figsize=(20, 15))
plt.subplots_adjust(0,0,1,1,0.07,0.07)
plt.gray()
plt.imshow(im_res_RL, vmin=im_blur.min(), vmax=im_blur.max()), plt.axis('off')
plt.title('Deconvolution using skimage (Richardson-Lucy), PSNR={:.3f}'.format(peak_signal_noise_ratio(im, im_res_RL)), size=20)
plt.show()
小结
模糊操作(例如,高斯模糊)是线性的,从数学上讲模糊过程是可逆的,即可以通过模糊图像恢复原始高清图像。但在实践中,该问题的逆过程计算起来非常困难。在本节中,我们学习了如何通过对图像的拉普拉斯方差进行简单的阈值处理来检测图像是否模糊,以及如何使用 SimpleItk
和 scikit-image
库函数的反卷积算法来消除图像中的模糊。
系列链接
Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【4】图像线性变换
Python图像处理【5】图像扭曲/逆扭曲
Python图像处理【6】通过哈希查找重复和类似的图像
Python图像处理【7】采样、卷积与离散傅里叶变换
Python图像处理【8】使用低通滤波器模糊图像
Python图像处理【9】使用高通滤波器执行边缘检测
Python图像处理【10】基于离散余弦变换的图像压缩