基于拉普拉斯方差阈值的图像模糊检测

1,337 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第28天,点击查看活动详情

前言

在本节中,我们将学习如何通过使用 OpenCV 库计算图像的拉普拉斯 (Laplacian) 方差阈值来检测图像是否模糊。执行模糊检测的步骤如下:

  • 如果图像足够清晰,则拉普拉斯运算将在图像中检测水平和垂直边缘,以获得输出图像在给定范围内的方差
  • 相反,如果图像相对模糊,则拉普拉斯运算在图像中并不能检测到足够的细节,从而导致输出方差小于给定阈值

该过程对于阈值非常敏感,因此需要选择合适的阈值。

图像模糊检测

在本节中,我们将使用灰度图像作为输入图像,并且为了进行演示,我们需要生成模糊的图像(通过使用不同类型的模糊核并与输入图像执行卷积),以测试我们实现的模糊检测函数。  

(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.pyplotimshow() 绘制模糊输入图像:

def plot_blurry(im, title):
    plt.imshow(im), plt.axis('off'), plt.title(title, size=10)

(4) 构造一个大小为 15x15、参数 σ=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) 最后,生成半径 715x15 失焦核,用此核模糊图像,然后检查图像是否模糊:

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()

Figure_9.png

从上图中可以看到,原始图像的 laplacian 的方差显著高于模糊图像。我们也可以使用不同的模糊核和阈值来检查模糊检测方法对阈值的敏感性。