实验环境
Python3
OpenCV3.2
Window 10
算法描述
注意:在实际的操作中,还需要对图片进行预处理操作(检测出人脸区域、设置相同大小的图片、图片灰度化等等)
只有这样才不会受到图像大小以及除去人脸以外的因素的影响。
本文初步对以上几种方法进行了实现。
(1)拉普拉斯算子(Laplacian算子)
图像中的边缘区域,像素值会发生“跳跃”,对这些像素求导,在其一阶导数在边缘位置为极值,这就是Sobel算子使用的原理——极值处就是边缘。如下图(下图来自OpenCV官方文档):
如果对像素值求二阶导数,会发现边缘处的导数值为0。如下(下图来自OpenCV官方文档):
Laplace函数实现的方法是先用Sobel 算子计算二阶x和y导数,再求和:(CSDN,你打水印,让我的公式怎么办?)
函数原型
在OpenCV-Python中,Laplace算子的函数原型如下:
dst = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
如果看了上一篇Sobel算子的介绍,这里的参数应该不难理解。
前两个是必须的参数:
第一个参数是需要处理的图像;
第二个参数是图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;
其后是可选的参数:
dst不用解释了;
ksize是算子的大小,必须为1、3、5、7。默认为1。
scale是缩放导数的比例常数,默认情况下没有伸缩系数;
delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
(2)Brenner 检测
Brenner梯度函数最简单的梯度评价函数指标,他只是简单的计算相邻两个像素灰度差的平方,该函数定义如下:
其中f(x,y)f(x,y)表示图像ff所对应的像素点(x,y)(x,y)的灰度值,D(f)D(f)为图像清晰度计算的结果。
(3)Tenengrad梯度函数
Tenengrad梯度函数采用Sobel算子分别提取水平和垂直方向的梯度,基于Tenengrad的图像清晰度定义如下:
G(x,y)的形式如下:
其中,T是给定的边缘检测阈值,Gx和Gy分别是像素点(x,y)处Sobel水平和垂直方向边缘检测算子的卷积。(参见参考文档[12,17])其余的方式都是一个这种类似的方式计算的额,
# -*-coding=UTF-8-*-
"""
在无参考图下,检测图片质量的方法
"""
import os
import cv2
import numpy as np
from skimage import filters
class BlurDetection:
def __init__(self, strDir):
print("图片检测对象已经创建...")
self.strDir = strDir
def _getAllImg(self, strType='jpg'):
"""
根据目录读取所有的图片
:param strType: 图片的类型
:return: 图片列表
"""
names = []
for root, dirs, files in os.walk(self.strDir): # 此处有bug 如果调试的数据还放在这里,将会递归的遍历所有文件
for file in files:
# if os.path.splitext(file)[1]=='jpg':
names.append(str(file))
return names
def _imageToMatrix(self, image):
"""
根据名称读取图片对象转化矩阵
:param strName:
:return: 返回矩阵
"""
imgMat = np.matrix(image)
return imgMat
def _blurDetection(self, imgName):
# step 1 图像的预处理
img2gray, reImg = self.preImgOps(imgName)
imgMat=self._imageToMatrix(img2gray)/255.0
x, y = imgMat.shape
score = 0
for i in range(x - 2):
for j in range(y - 2):
score += (imgMat[i + 2, j] - imgMat[i, j]) ** 2
# step3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
score=score/10
newImg = self._drawImgFonts(reImg, str(score))
newDir = self.strDir + "/_blurDetection_/"
if not os.path.exists(newDir):
os.makedirs(newDir)
newPath = newDir + imgName
cv2.imwrite(newPath, newImg) # 保存图片
# cv2.imshow(imgName, newImg)
# cv2.waitKey(0)
return score
def _SMDDetection(self, imgName):
# step 1 图像的预处理
img2gray, reImg = self.preImgOps(imgName)
f=self._imageToMatrix(img2gray)/255.0
x, y = f.shape
score = 0
for i in range(x - 1):
for j in range(y - 1):
score += np.abs(f[i+1,j]-f[i,j])+np.abs(f[i,j]-f[i+1,j])
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
score=score/100
newImg = self._drawImgFonts(reImg, str(score))
newDir = self.strDir + "/_SMDDetection_/"
if not os.path.exists(newDir):
os.makedirs(newDir)
newPath = newDir + imgName
cv2.imwrite(newPath, newImg) # 保存图片
# cv2.imshow(imgName, newImg)
# cv2.waitKey(0)
return score
def _SMD2Detection(self, imgName):
"""
灰度方差乘积
:param imgName:
:return:
"""
# step 1 图像的预处理
img2gray, reImg = self.preImgOps(imgName)
f=self._imageToMatrix(img2gray)/255.0
x, y = f.shape
score = 0
for i in range(x - 1):
for j in range(y - 1):
score += np.abs(f[i+1,j]-f[i,j])*np.abs(f[i,j]-f[i,j+1])
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
score=score
newImg = self._drawImgFonts(reImg, str(score))
newDir = self.strDir + "/_SMD2Detection_/"
if not os.path.exists(newDir):
os.makedirs(newDir)
newPath = newDir + imgName
cv2.imwrite(newPath, newImg) # 保存图片
# cv2.imshow(imgName, newImg)
# cv2.waitKey(0)
return score
def _Variance(self, imgName):
"""
灰度方差乘积
:param imgName:
:return:
"""
# step 1 图像的预处理
img2gray, reImg = self.preImgOps(imgName)
f = self._imageToMatrix(img2gray)
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
score = np.var(f)
newImg = self._drawImgFonts(reImg, str(score))
newDir = self.strDir + "/_Variance_/"
if not os.path.exists(newDir):
os.makedirs(newDir)
newPath = newDir + imgName
cv2.imwrite(newPath, newImg) # 保存图片
# cv2.imshow(imgName, newImg)
# cv2.waitKey(0)
return score
def _Vollath(self,imgName):
"""
灰度方差乘积
:param imgName:
:return:
"""
# step 1 图像的预处理
img2gray, reImg = self.preImgOps(imgName)
f = self._imageToMatrix(img2gray)
source=0
x,y=f.shape
for i in range(x-1):
for j in range(y):
source+=f[i,j]*f[i+1,j]
source=source-x*y*np.mean(f)
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
newImg = self._drawImgFonts(reImg, str(source))
newDir = self.strDir + "/_Vollath_/"
if not os.path.exists(newDir):
os.makedirs(newDir)
newPath = newDir + imgName
cv2.imwrite(newPath, newImg) # 保存图片
# cv2.imshow(imgName, newImg)
# cv2.waitKey(0)
return source
def _Tenengrad(self,imgName):
"""
灰度方差乘积
:param imgName:
:return:
"""
# step 1 图像的预处理
img2gray, reImg = self.preImgOps(imgName)
f = self._imageToMatrix(img2gray)
tmp = filters.sobel(f)
# out = np.sqrt(cv2.Sobel(f, cv2.CV_64F, 0, 1, ksize=3) ** 2 + cv2.Sobel(f, cv2.CV_64F, 1, 0, ksize=3) ** 2)
# out /= np.sqrt(2)
source=np.sum(tmp**2)
source=np.sqrt(source)
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
newImg = self._drawImgFonts(reImg, str(source))
newDir = self.strDir + "/_Tenengrad_/"
if not os.path.exists(newDir):
os.makedirs(newDir)
newPath = newDir + imgName
cv2.imwrite(newPath, newImg) # 保存图片
# cv2.imshow(imgName, newImg)
# cv2.waitKey(0)
return source
def Test_Tenengrad(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._Tenengrad(imgList[i])
print(str(imgList[i]) + " is " + str(score))
def Test_Vollath(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._Variance(imgList[i])
print(str(imgList[i]) + " is " + str(score))
def TestVariance(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._Variance(imgList[i])
print(str(imgList[i]) + " is " + str(score))
def TestSMD2(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._SMD2Detection(imgList[i])
print(str(imgList[i]) + " is " + str(score))
return
def TestSMD(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._SMDDetection(imgList[i])
print(str(imgList[i]) + " is " + str(score))
return
def TestBrener(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._blurDetection(imgList[i])
print(str(imgList[i]) + " is " + str(score))
return
def preImgOps(self, imgName):
"""
图像的预处理操作
:param imgName: 图像的而明朝
:return: 灰度化和resize之后的图片对象
"""
strPath = self.strDir + imgName
img = cv2.imread(strPath) # 读取图片
# cv2.moveWindow("", 1000, 100)
# cv2.imshow("原始图", img)
# 预处理操作
# reImg = cv2.resize(img, (800, 900), interpolation=cv2.INTER_CUBIC) #
reImg = img
img2gray = cv2.cvtColor(reImg, cv2.COLOR_BGR2GRAY) # 将图片压缩为单通道的灰度图
return img2gray, reImg
def _drawImgFonts(self, img, strContent):
"""
绘制图像
:param img: cv下的图片对象
:param strContent: 书写的图片内容
:return:
"""
font = cv2.FONT_HERSHEY_SIMPLEX
fontSize = 1
# 照片 添加的文字 /左上角坐标 字体 字体大小 颜色 字体粗细
cv2.putText(img, strContent, (0, 50), font, fontSize, (0, 255, 0), 1)
return img
def _lapulaseDetection(self, imgName):
"""
:param strdir: 文件所在的目录
:param name: 文件名称
:return: 检测模糊后的分数
"""
# step1: 预处理
img2gray, reImg = self.preImgOps(imgName)
# step2: laplacian算子 获取评分
resLap = cv2.Laplacian(img2gray, cv2.CV_64F)
score = resLap.var()
print("Laplacian %s score of given image is %s", str(score))
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
newImg = self._drawImgFonts(reImg, str(score))
newDir = self.strDir + "/_lapulaseDetection_/"
if not os.path.exists(newDir):
os.makedirs(newDir)
newPath = newDir + imgName
# 显示
cv2.imwrite(newPath, newImg) # 保存图片
# cv2.imshow(imgName, newImg)
# cv2.waitKey(0)
# step3: 返回分数
return score
def TestDect(self):
names = self._getAllImg()
for i in range(len(names)):
score = self._lapulaseDetection(names[i])
print(str(names[i]) + " is " + str(score))
return
if __name__ == "__main__":
BlurDetection = BlurDetection(strDir=r"F:\code\test_code_data\\")
BlurDetection.Test_Tenengrad () # TestSMD
BlurDetection.Test_Vollath()
BlurDetection.TestBrener()
BlurDetection.TestDect()
BlurDetection.TestSMD()
BlurDetection.TestSMD2()
BlurDetection.TestVariance()
SVD方法
https://github.com/fled/blur_detection/blob/master/blur_detection.py
import cv2
import numpy as np
def get_blur_degree(image_file, sv_num=10):
img = cv2.imread(image_file,cv2.IMREAD_GRAYSCALE)
u, s, v = np.linalg.svd(img)
top_sv = np.sum(s[0:sv_num])
total_sv = np.sum(s)
return top_sv/total_sv
def get_blur_map(image_file, win_size=10, sv_num=3):
img = cv2.imread(image_file, cv2.IMREAD_GRAYSCALE)
new_img = np.zeros((img.shape[0]+win_size*2, img.shape[1]+win_size*2))
for i in range(new_img.shape[0]):
for j in range(new_img.shape[1]):
if i<win_size:
p = win_size-i
elif i>img.shape[0]+win_size-1:
p = img.shape[0]*2-i
else:
p = i-win_size
if j<win_size:
q = win_size-j
elif j>img.shape[1]+win_size-1:
q = img.shape[1]*2-j
else:
q = j-win_size
#print p,q, i, j
new_img[i,j] = img[p,q]
#cv2.imwrite('test.jpg', new_img)
#cv2.imwrite('testin.jpg', img)
blur_map = np.zeros((img.shape[0], img.shape[1]))
max_sv = 0
min_sv = 1
for i in range(img.shape[0]):
for j in range(img.shape[1]):
block = new_img[i:i+win_size*2, j:j+win_size*2]
u, s, v = np.linalg.svd(block)
top_sv = np.sum(s[0:sv_num])
total_sv = np.sum(s)
sv_degree = top_sv/total_sv
if max_sv < sv_degree:
max_sv = sv_degree
if min_sv > sv_degree:
min_sv = sv_degree
blur_map[i, j] = sv_degree
#cv2.imwrite('blurmap.jpg', (1 - blur_map) * 255)
blur_map = (blur_map-min_sv)/(max_sv-min_sv)
#cv2.imwrite('blurmap_norm.jpg', (1-blur_map)*255)
return blur_map
# import glob
#
# files = glob.glob('data/test*')
# for file in files:
# print file, get_blur_degree(file)
# out_file = file.replace('test_image','blur_map')
# blur_map = get_blur_map(file)
# cv2.imwrite(out_file, (1-blur_map)*255)