转载自:https://zhuanlan.zhihu.com/p/97024018,本文只做个人记录学习使用,版权归原作者所有。
需求:在一堆图像中找到模糊图像,背景虚化(景深模式)的照片定义为清晰照片。
传统的图像清晰度评价算法:
Tenengrad 梯度函数
Tenengrad 梯度函数采用Sobel算子分别提取水平和垂直方向的梯度值,基与Tenengrad 梯度函数的图像清晰度定义如下:
G(x,y) 的形式如下:
其中:T是给定的边缘检测阈值,Gx和Gy分别是像素点(x,y)处Sobel水平和垂直方向边缘检测算子的卷积,建议使用以下的Sobel算子模板来检测边缘:
python程序实现:
Laplacian 梯度函数
Laplacian 梯度函数与Tenengrad梯度函数基本一致,用Laplacian算子替代Sobel算子即可,该算子定义如下:
因此基于Laplacian 梯度函数的图像星清晰度的定义如下:
其中G(x,y)是像素点(x,y)处Laplacian算子的卷积。
python程序实现:
#Laplacian梯度函数计算
def Laplacian(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
return cv2.Laplacian(img,cv2.CV_64F).var()
SMD(灰度方差)函数
当完全聚焦时,图像最清晰,图像中的高频分量也最多,故可将灰度变化作为聚焦评价的依据,灰度方差法的公式如下:
python程序实现:
#SMD梯度函数计算
def SMD(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(1, shape[0]-1):
for y in range(0, shape[1]):
out+=math.fabs(int(img[x,y])-int(img[x,y-1]))
out+=math.fabs(int(img[x,y]-int(img[x+1,y])))
return out
SMD2 (灰度方差乘积)函数
灰度差分评价函数具有较好的计算性能,但其缺点也很明显,即在焦点附近灵敏度不高,即该函数在极值点附近过于平坦,从而导致聚焦精度难以提高。在文章《一种快速高灵敏度聚焦评价函数》中提出了一种新的评价函数,称之为灰度方差乘积法,即对每一个像素领域两个灰度差相乘后再逐个像素累加,该函数定义如下:
python程序实现:
#SMD2梯度函数计算
def SMD2(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(0, shape[0]-1):
for y in range(0, shape[1]-1):
out+=math.fabs(int(img[x,y])-int(img[x+1,y]))*math.fabs(int(img[x,y]-int(img[x,y+1])))
return out
Brenner 梯度函数
Brenner梯度函数是最简单的梯度评价函数,它只是简单的计算相邻两个像素灰度差的平方,该函数定义如下:
其中:f(x,y) 表示图像f对应像素点(x,y)的灰度值,D(f)为图像清晰度计算结果.
python程序实现:
#brenner梯度函数计算
def brenner(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(0, shape[0]-2):
for y in range(0, shape[1]):
out+=(int(img[x+2,y])-int(img[x,y]))**2
return out
方差函数
因为清晰聚焦的图像有着比模糊图像更大的灰度差异,可以将方差函数作为评价函数:
其中:μ为整幅图像的平均灰度值,该函数对噪声比较敏感,图像画面越纯净,函数值越小。
python程序实现:
#方差函数计算
def variance(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
out = 0
u = np.mean(img)
shape = np.shape(img)
for x in range(0,shape[0]):
for y in range(0,shape[1]):
out+=(img[x,y]-u)**2
return out
能量梯度函数
能量梯度函数更适合实时评价图像清晰度,该函数定义如下:
python程序实现:
#energy函数计算
def energy(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(0, shape[0]-1):
for y in range(0, shape[1]-1):
out+=((int(img[x+1,y])-int(img[x,y]))**2)*((int(img[x,y+1]-int(img[x,y])))**2)
return out
Vollath函数
Vollath函数定义如下:
其中:μ为整幅图像的平均灰度值,M和N分别为图像宽和高。
python程序实现:
#Vollath函数计算
def Vollath(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
u = np.mean(img)
out = -shape[0]*shape[1]*(u**2)
for x in range(0, shape[0]-1):
for y in range(0, shape[1]):
out+=int(img[x,y])*int(img[x+1,y])
return out
熵函数
基于统计特征的熵函数是衡量图像信息丰富程度的一个重要指标,有信息论可知,一幅图像 f 的信息量是由该图像的信息熵 D(f) 来度量:
其中:Pi 是图像中灰度值为i的像素出现的概率,L为灰度级总数(通常取值256)。根据Shannon信息论,熵最大时信息量最多。将此原理应用到对焦过程,D(f)越大则图像越清晰。熵函数灵敏度不高,依据图像内容不同容易出现与真实情况相反的结果。
python程序实现:
#entropy函数计算
def entropy(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
out = 0
count = np.shape(img)[0]*np.shape(img)[1]
p = np.bincount(np.array(img).flatten())
for i in range(0, len(p)):
if p[i]!=0:
out-=p[i]*math.log(p[i]/count)/count
return out
以上算法利用python实现,主要基于opencv,numpy, math库 ,以下是完整代码:
import cv2
import numpy as np
import math
#brenner梯度函数计算
def brenner(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(0, shape[0]-2):
for y in range(0, shape[1]):
out+=(int(img[x+2,y])-int(img[x,y]))**2
return out
#Laplacian梯度函数计算
def Laplacian(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
return cv2.Laplacian(img,cv2.CV_64F).var()
#SMD梯度函数计算
def SMD(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(1, shape[0]-1):
for y in range(0, shape[1]):
out+=math.fabs(int(img[x,y])-int(img[x,y-1]))
out+=math.fabs(int(img[x,y]-int(img[x+1,y])))
return out
#SMD2梯度函数计算
def SMD2(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(0, shape[0]-1):
for y in range(0, shape[1]-1):
out+=math.fabs(int(img[x,y])-int(img[x+1,y]))*math.fabs(int(img[x,y]-int(img[x,y+1])))
return out
#方差函数计算
def variance(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
out = 0
u = np.mean(img)
shape = np.shape(img)
for x in range(0,shape[0]):
for y in range(0,shape[1]):
out+=(img[x,y]-u)**2
return out
#energy函数计算
def energy(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
out = 0
for x in range(0, shape[0]-1):
for y in range(0, shape[1]-1):
out+=((int(img[x+1,y])-int(img[x,y]))**2)*((int(img[x,y+1]-int(img[x,y])))**2)
return out
#Vollath函数计算
def Vollath(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
shape = np.shape(img)
u = np.mean(img)
out = -shape[0]*shape[1]*(u**2)
for x in range(0, shape[0]-1):
for y in range(0, shape[1]):
out+=int(img[x,y])*int(img[x+1,y])
return out
#entropy函数计算
def entropy(img):
'''
:param img:narray 二维灰度图像
:return: float 图像越清晰越大
'''
out = 0
count = np.shape(img)[0]*np.shape(img)[1]
p = np.bincount(np.array(img).flatten())
for i in range(0, len(p)):
if p[i]!=0:
out-=p[i]*math.log(p[i]/count)/count
return out
def main(img1, img2):
print('Brenner',brenner(img1),brenner(img2))
print('Laplacian',Laplacian(img1),Laplacian(img2))
print('SMD',SMD(img1), SMD(img2))
print('SMD2',SMD2(img1), SMD2(img2))
print('Variance',variance(img1),variance(img2))
print('Energy',energy(img1),energy(img2))
print('Vollath',Vollath(img1),Vollath(img2))
print('Entropy',entropy(img1),entropy(img2))
if __name__ == '__main__':
#读入原始图像
img1 = cv2.imread('./data/MotionL100H0_15.jpg')
img2 = cv2.imread('./data/MotionL100H0_13.jpg')
#灰度化处理
img1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
main(img1,img2)
c++实现请参考:
无参考图像的清晰度评价方法及c++实现 - 代码先锋网www.codeleading.com
MATLAB实现请参考:
https://blog.csdn.net/kungfu_rabbit/article/details/90243838blog.csdn.net
Reblur 二次模糊
如果一幅图像已经模糊了,那么再对它进行一次模糊处理,高频分量变化不大;但如果原图是清楚的,对它进行一次模糊处理,则高频分量变化会非常大。因此可以通过对待评测图像进行一次高斯模糊处理,得到该图像的退化图像,然后再比较原图像和退化图像相邻像素值的变化情况,根据变化的大小确定清晰度值的高低,计算结果越小表明图像越清晰,反之越模糊。这种思路可称作基于二次模糊的清晰度算法,其算法简化流程如下图:
NRSS 梯度结构相似度
python实现:
#encoding=utf-8
import cv2
import numpy as np
from skimage.measure import compare_ssim
def gauseBlur(img):
img_Guassian = cv2.GaussianBlur(img,(7,7),0)
return img_Guassian
def loadImage(filepath):
img = cv2.imread(filepath, 0) ## 读入灰度图
return img
def showImage(img):
cv2.imshow('image', img)
cv2.waitKey(0)
def saveImage(path, img):
cv2.imwrite(path, img)
def sobel(img):
x = cv2.Sobel(img, cv2.CV_16S, 1, 0)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(x) # 转回uint8
absY = cv2.convertScaleAbs(y)
dst = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return dst
def getBlock(G,Gr):
(h, w) = G.shape
G_blk_list = []
Gr_blk_list = []
sp = 6
for i in range(sp):
for j in range(sp):
G_blk = G[int((i / sp) * h):int(((i + 1) / sp) * h), int((j / sp) * w):int(((j + 1) / sp) * w)]
Gr_blk = Gr[int((i / sp) * h):int(((i + 1) / sp) * h), int((j / sp) * w):int(((j + 1) / sp) * w)]
G_blk_list.append(G_blk)
Gr_blk_list.append(Gr_blk)
sum = 0
for i in range(sp*sp):
mssim = compare_ssim(G_blk_list[i], Gr_blk_list[i])
sum = mssim + sum
nrss = 1-sum/(sp*sp*1.0)
print(nrss)
def NRSS(path):
image = loadImage(path)
#高斯滤波
Ir = gauseBlur(image)
G = sobel(image)
Gr = sobel(Ir)
blocksize = 8
## 获取块信息
getBlock(G, Gr)
if __name__ == "__main__":
filepath = "F:\\testpic\\new\\blurtest\\clear\\3.jpg"
NRSS(filepath)
传统的图像模糊检测主要存在以下缺点:
1.模糊界限难以确定,需要定一个阈值去区分(根据应用场景确定)。
2.众多的算法那个算法更适合,需要去验证。
针对传统算法也有一些机器学习和深度学习的方法去解决:
机器学习方法:
- 使用拉普拉斯滤镜在输入图像中查找边缘
- 计算方差和已过滤图像像素值的最大值
- 高方差(和高最大值)表明边缘清晰可见,即图像清晰。低方差表明图像模糊。
- 基于Laplace的特征的图,我们注意到我们的两类(模糊和非模糊)是线性可分离的,然后利用SVM进行
实现代码:
#计算laplace算子
import numpy as np
from scipy.ndimage import variance
from skimage import io
from skimage.color import rgb2gray
from skimage.filters import laplace
from skimage.transform import resize
# Load image
path = f'test_data/0001.jpg'
img = io.imread(path)
# Resize image
img = resize(img, (400, 600))
# Grayscale image
img = rgb2gray(img)
# Edge detection
edge_laplace = laplace(img, ksize=3)
# Print output
print(f"Variance: {variance(edge_laplace)}")
print(f"Maximum : {np.amax(edge_laplace)}")
#利用SVM进行分类
import numpy as np
from sklearn import preprocessing, svm
# start with the results from the previous script
sharp_laplaces = [ (variance(edge_laplace_sharp_1), np.amax(edge_laplace_sharp_1)), ... ]
blurry_laplaces = [ (variance(edge_laplace_blurry_1), np.amax(edge_laplace_blurry_1)), ... ]
# set class labels (non-blurry / blurry) and prepare features
y = np.concatenate((np.ones((25, )), np.zeros((25, ))), axis=0)
laplaces = np.concatenate((np.array(sharp_laplaces), np.array(blurry_laplaces)), axis=0)
# scale features
laplaces = preprocessing.scale(laplaces)
# train the classifier (support vector machine)
clf = svm.SVC(kernel='linear', C=100000)
clf.fit(laplaces, y)
# print parameters
print(f'Weights: {clf.coef_[0]}')
print(f'Intercept: {clf.intercept_}')
# make sample predictions
clf.predict([[0.00040431, 0.1602369]]) # result: 0 (blurred)
clf.predict([[0.00530690, 0.7531759]]) # result: 1 (sharp)
深度学习方法:
针对模糊非模糊进行简单的二分类
数据集:
CERTH Image Blur Dataset - MKLabmklab.iti.gr
方法参考:
priyabagaria/Image-Blur-Detectiongithub.com
模糊区域检测,利用奇异值分解(SVD):
https://github.com/fled/blur_detectiongithub.com
参考:
[1].https://blog.csdn.net/Real_Myth/article/details/50827940
[2].https://medium.com/snapaddy-tech-blog/mobile-image-blur-detection-with-machine-learning-c0b703eab7de
[3].https://github.com/Leezhen2014/python--/blob/master/BlurDetection.py
发布于 2019-12-13