py
# -*-coding=UTF-8-*-
"""
在无参考图下,检测图片质量的方法
"""
import os
import cv2
import ctypes
import sys
import numpy as np
from skimage import filters
def _load_shared_lib(libpath):
return ctypes.cdll.LoadLibrary(libpath)
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 _Tenengrad(self,imgName):
"""
灰度方差乘积
:param imgName:
:return:
"""
# step 1 图像的预处理
# img2gray, reImg = self.preImgOps(imgName)
# f = self._imageToMatrix(img2gray)
# tmp = filters.sobel(f)
# source=np.sum(tmp**2)
# source=np.sqrt(source)
imgPath = self.strDir + imgName
reImg = cv2.imread(imgPath)
reImg = cv2.resize(reImg, (144,144))
image_data = np.asarray(reImg, dtype=np.uint8)
# image_data = reImg
image_data = image_data.ctypes.data_as(ctypes.c_char_p)
c_lib.tenengard.argtypes = (ctypes.c_int,ctypes.c_int,ctypes.c_char_p)
c_lib.tenengard.restype = ctypes.c_double
source = round(c_lib.tenengard(reImg.shape[0], reImg.shape[1], image_data),4)
# 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)
# if cv2.waitKey(10) & 0xff == ord("q"):
# # break
# cv2.destroyAllWindows()
return source
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
imgPath = self.strDir + imgName
reImg = cv2.imread(imgPath)
reImg = cv2.resize(reImg, (144,144))
# imgPath = ctypes.c_char_p(imgPath.encode('utf-8'))
image_data = np.asarray(reImg, dtype=np.uint8)
image_data = image_data.ctypes.data_as(ctypes.c_char_p)
c_lib.brenner.argtypes = (ctypes.c_int,ctypes.c_int,ctypes.c_char_p)
c_lib.brenner.restype = ctypes.c_double
score = round(c_lib.brenner(reImg.shape[0], reImg.shape[1], image_data),4)
# step3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
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 _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
imgPath = self.strDir + imgName
reImg = cv2.imread(imgPath)
reImg = cv2.resize(reImg, (144,144))
# imgPath = ctypes.c_char_p(imgPath.encode('utf-8'))
image_data = np.asarray(reImg, dtype=np.uint8)
image_data = image_data.ctypes.data_as(ctypes.c_char_p)
c_lib.smd2.argtypes = (ctypes.c_int,ctypes.c_int,ctypes.c_char_p)
c_lib.smd2.restype = ctypes.c_double
score = round(c_lib.smd2(reImg.shape[0], reImg.shape[1], image_data),4)
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 _Energy_gradient(self, imgName):
"""
灰度方差乘积
:param imgName:
:return:
"""
# step 1 图像的预处理
imgPath = self.strDir + imgName
reImg = cv2.imread(imgPath)
reImg = cv2.resize(reImg, (144,144))
# imgPath = ctypes.c_char_p(imgPath.encode('utf-8'))
image_data = np.asarray(reImg, dtype=np.uint8)
image_data = image_data.ctypes.data_as(ctypes.c_char_p)
c_lib.energy_gradient.argtypes = (ctypes.c_int,ctypes.c_int,ctypes.c_char_p)
c_lib.energy_gradient.restype = ctypes.c_double
score = round(c_lib.energy_gradient(reImg.shape[0], reImg.shape[1], image_data),4)
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
newImg = self._drawImgFonts(reImg, str(score))
newDir = self.strDir + "/_Energy_gradient_/"
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 _Nrss(self,imgName):
"""
灰度方差乘积
:param imgName:
:return:
"""
# step 1 图像的预处理
imgPath = self.strDir + imgName
reImg = cv2.imread(imgPath)
reImg = cv2.resize(reImg, (144,144))
# imgPath = ctypes.c_char_p(imgPath.encode('utf-8'))
image_data = np.asarray(reImg, dtype=np.uint8)
image_data = image_data.ctypes.data_as(ctypes.c_char_p)
c_lib.nrss.argtypes = (ctypes.c_int,ctypes.c_int,ctypes.c_char_p)
c_lib.nrss.restype = ctypes.c_double
source = round(c_lib.nrss(reImg.shape[0], reImg.shape[1], image_data),4)
# strp3: 绘制图片并保存 不应该写在这里 抽象出来 这是共有的部分
newImg = self._drawImgFonts(reImg, str(source))
newDir = self.strDir + "/_Nrss_/"
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 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, (144, 144), interpolation=cv2.INTER_CUBIC) #
h1, w1, _ = reImg.shape
reImg=reImg[int(h1*0.1):int(h1*0.9), int(w1*0.1):int(w1*0.9), :]
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, (5, 50), font, fontSize, (0, 255, 0), 2)
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()
imgPath = self.strDir + imgName
reImg = cv2.imread(imgPath)
reImg = cv2.resize(reImg, (144,144))
image_data = np.asarray(reImg, dtype=np.uint8)
# image_data = reImg
image_data = image_data.ctypes.data_as(ctypes.c_char_p)
c_lib.laplacian.argtypes = (ctypes.c_int,ctypes.c_int,ctypes.c_char_p)
c_lib.laplacian.restype = ctypes.c_double
score = round(c_lib.laplacian(reImg.shape[0], reImg.shape[1], image_data),4)
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 Test_Tenengrad(self):
out_file = 'Test_Tenengrad_c_2.txt'
with open(out_file, 'w') as fout:
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._Tenengrad(imgList[i])
print(str(imgList[i]) + " is " + str(score))
write_line = '%s\t' % str(imgList[i])
write_line += '%f\n' % float(score)
fout.write(write_line)
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 TestLapulase(self):
out_file = 'TestLapulase_c_2.txt'
with open(out_file, 'w') as fout:
names = self._getAllImg()
for i in range(len(names)):
score = self._lapulaseDetection(names[i])
print(str(names[i]) + " is " + str(score))
write_line = '%s\t' % str(names[i])
write_line += '%f\n' % float(score)
fout.write(write_line)
return
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 Test_EnergyGrad(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._Energy_gradient(imgList[i])
print(str(imgList[i]) + " is " + str(score))
def Test_Nrss(self):
imgList = self._getAllImg(self.strDir)
for i in range(len(imgList)):
score = self._Nrss(imgList[i])
print(str(imgList[i]) + " is " + str(score))
if __name__ == "__main__":
# imgpath = str(sys.argv[1])
# libpath = str(sys.argv[2])
libpath = 'blur_detection2.so'
c_lib = _load_shared_lib(libpath)
BlurDetection = BlurDetection(strDir="save_images3/")
BlurDetection.Test_Tenengrad () # TestSMD
BlurDetection.TestLapulase() # TestSMD
# BlurDetection.TestBrener()
# BlurDetection.TestSMD2()
# BlurDetection.Test_EnergyGrad()
# BlurDetection.Test_Nrss()
C++
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#define FIXED_SIZE 144
#define EDGE_THRESH 35
#define MIN_ZERO 0.05
/**
* Tenengrad梯度方法
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
double tenengard(int height, int width, uchar* frame_data)
{
int count = 0;
cv::Mat image(height, width, CV_8UC3);
uchar* pxvec =image.ptr<uchar>(0);
for(int row = 0; row < height; row++)
{
pxvec = image.ptr<uchar>(row);
for(int col = 0; col < width; col++)
{
for(int c = 0; c < 3; c++)
{
pxvec[col*3+c] = frame_data[count];
count++;
}
}
}
cv::Mat gray_img, sobel_x, sobel_y, G;
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
}
//分别计算x/y方向梯度
cv::Sobel(gray_img, sobel_x, CV_32FC1, 1, 0);
cv::Sobel(gray_img, sobel_y, CV_32FC1, 0, 1);
cv::multiply(sobel_x, sobel_x, sobel_x);
cv::multiply(sobel_y, sobel_y, sobel_y);
cv::Mat sqrt_mat = sobel_x + sobel_y;
cv::sqrt(sqrt_mat, G);
// std::cout << "tenengard: " << cv::mean(G)[0] << std::endl;
// std::cout << "type: " << typeid(cv::mean(G)[0]).name() << std::endl;
return cv::mean(G)[0];
}
}
/**
* Brenner梯度方法
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
double brenner(int height, int width, uchar* frame_data)
{
int count = 0;
cv::Mat image(height, width, CV_8UC3);
uchar* pxvec =image.ptr<uchar>(0);
for(int row = 0; row < height; row++)
{
pxvec = image.ptr<uchar>(row);
for(int col = 0; col < width; col++)
{
for(int c = 0; c < 3; c++)
{
pxvec[col*3+c] = frame_data[count];
count++;
}
}
}
cv::Mat gray_img;
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
}
double result = .0f;
for (int i = 0; i < gray_img.rows; ++i){
uchar *data = gray_img.ptr<uchar>(i);
for (int j = 0; j < gray_img.cols - 2; ++j){
result += pow(data[j + 2] - data[j], 2);
}
}
// std::cout << "brenner: " << result/gray_img.total() << std::endl;
return result/gray_img.total();
}
}
/**
* Laplacian 梯度函数
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
// double laplacian(cv::Mat &image)
double laplacian(int height, int width, uchar* frame_data)
{
int count = 0;
cv::Mat image(height, width, CV_8UC3);
uchar* pxvec =image.ptr<uchar>(0);
for(int row = 0; row < height; row++)
{
pxvec = image.ptr<uchar>(row);
for(int col = 0; col < width; col++)
{
for(int c = 0; c < 3; c++)
{
pxvec[col*3+c] = frame_data[count];
count++;
}
}
}
cv::Mat gray_img=cv::Mat(image.size(),CV_8UC1);
cv::Mat lap_image=cv::Mat(image.size(),CV_8UC1);
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
// std::cout << "file_path: " << file_path << std::endl;
// std::cout << "width: " << image.rows << std::endl;
// std::cout << "height: " << image.cols << std::endl;
// std::cout << "img.size: " << image.size() << std::endl;
}
cv::Laplacian(gray_img, lap_image, CV_32FC1);
lap_image=cv::abs(lap_image);
// std::cout << "laplacian: " << cv::mean(lap_image)[0] << std::endl;
return cv::mean(lap_image)[0];
}
}
/**
* SMD(灰度方差)函数
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
double smd(cv::Mat &image)
{
// assert(image.empty());
cv::Mat gray_img, smd_image_x, smd_image_y, G;
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
}
cv::Mat kernel_x(3, 3, CV_32F, cv::Scalar(0));
kernel_x.at<float>(1, 2) = -1.0;
kernel_x.at<float>(1, 1) = 1.0;
cv::Mat kernel_y(3, 3, CV_32F, cv::Scalar(0));
kernel_y.at<float>(0, 1) = -1.0;
kernel_y.at<float>(1, 1) = 1.0;
cv::filter2D(gray_img, smd_image_x, gray_img.depth(), kernel_x);
cv::filter2D(gray_img, smd_image_y, gray_img.depth(), kernel_y);
smd_image_x = cv::abs(smd_image_x);
smd_image_y = cv::abs(smd_image_y);
G = smd_image_x + smd_image_y;
return cv::mean(G)[0];
}
}
/**
* SMD2 (灰度方差乘积)函数
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
double smd2(int height, int width, uchar* frame_data)
{
int count = 0;
cv::Mat image(height, width, CV_8UC3);
uchar* pxvec =image.ptr<uchar>(0);
for(int row = 0; row < height; row++)
{
pxvec = image.ptr<uchar>(row);
for(int col = 0; col < width; col++)
{
for(int c = 0; c < 3; c++)
{
pxvec[col*3+c] = frame_data[count];
count++;
}
}
}
cv::Mat gray_img, smd_image_x, smd_image_y, G;
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
}
cv::Mat kernel_x(3, 3, CV_32F, cv::Scalar(0));
kernel_x.at<float>(1, 2) = -1.0;
kernel_x.at<float>(1, 1) = 1.0;
cv::Mat kernel_y(3, 3, CV_32F, cv::Scalar(0));
kernel_y.at<float>(1, 1) = 1.0;
kernel_y.at<float>(2, 1) = -1.0;
cv::filter2D(gray_img, smd_image_x, gray_img.depth(), kernel_x);
cv::filter2D(gray_img, smd_image_y, gray_img.depth(), kernel_y);
smd_image_x = cv::abs(smd_image_x);
smd_image_y = cv::abs(smd_image_y);
cv::multiply(smd_image_x, smd_image_y, G);
// std::cout << "smd2: " << cv::mean(G)[0] << std::endl;
return cv::mean(G)[0];
}
}
/**
* 能量梯度函数
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
double energy_gradient(int height, int width, uchar* frame_data)
{
int count = 0;
cv::Mat image(height, width, CV_8UC3);
uchar* pxvec =image.ptr<uchar>(0);
for(int row = 0; row < height; row++)
{
pxvec = image.ptr<uchar>(row);
for(int col = 0; col < width; col++)
{
for(int c = 0; c < 3; c++)
{
pxvec[col*3+c] = frame_data[count];
count++;
}
}
}
cv::Mat gray_img, smd_image_x, smd_image_y, G;
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
}
cv::Mat kernel_x(3, 3, CV_32F, cv::Scalar(0));
kernel_x.at<float>(1, 2) = -1.0;
kernel_x.at<float>(1, 1) = 1.0;
cv::Mat kernel_y(3, 3, CV_32F, cv::Scalar(0));
kernel_y.at<float>(1, 1) = 1.0;
kernel_y.at<float>(2, 1) = -1.0;
cv::filter2D(gray_img, smd_image_x, gray_img.depth(), kernel_x);
cv::filter2D(gray_img, smd_image_y, gray_img.depth(), kernel_y);
cv::multiply(smd_image_x, smd_image_x, smd_image_x);
cv::multiply(smd_image_y, smd_image_y, smd_image_y);
G = smd_image_x + smd_image_y;
// std::cout << "energy_gradient: " << cv::mean(G)[0] << std::endl;
return cv::mean(G)[0];
}
}
/**
* EAV点锐度算法函数
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
double eav(cv::Mat &image)
{
// assert(image.empty());
cv::Mat gray_img, smd_image_x, smd_image_y, G;
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
}
double result = .0f;
for (int i = 1; i < gray_img.rows-1; ++i){
uchar *prev = gray_img.ptr<uchar>(i - 1);
uchar *cur = gray_img.ptr<uchar>(i);
uchar *next = gray_img.ptr<uchar>(i + 1);
for (int j = 0; j < gray_img.cols; ++j){
result += (abs(prev[j - 1] - cur[i])*0.7 + abs(prev[j] - cur[j]) + abs(prev[j + 1] - cur[j])*0.7 +
abs(next[j - 1] - cur[j])*0.7 + abs(next[j] - cur[j]) + abs(next[j + 1] - cur[j])*0.7 +
abs(cur[j - 1] - cur[j]) + abs(cur[j + 1] - cur[j]));
}
}
return result / gray_img.total();
}
}
/**
* 误差灵敏度分析和结构相似度分析
*
* Inputs:
* @param i1:
* @param i2:
* Return: double
*/
extern "C"
{
double ssim(cv::Mat &i1, cv::Mat & i2)
{
const double C1 = 6.5025, C2 = 58.5225;
int d = CV_32F;
cv::Mat I1, I2;
i1.convertTo(I1, d);
i2.convertTo(I2, d);
cv::Mat I1_2 = I1.mul(I1);
cv::Mat I2_2 = I2.mul(I2);
cv::Mat I1_I2 = I1.mul(I2);
cv::Mat mu1, mu2;
GaussianBlur(I1, mu1, cv::Size(11, 11), 1.5);
GaussianBlur(I2, mu2, cv::Size(11, 11), 1.5);
cv::Mat mu1_2 = mu1.mul(mu1);
cv::Mat mu2_2 = mu2.mul(mu2);
cv::Mat mu1_mu2 = mu1.mul(mu2);
cv::Mat sigma1_2, sigam2_2, sigam12;
GaussianBlur(I1_2, sigma1_2, cv::Size(11, 11), 1.5);
sigma1_2 -= mu1_2;
GaussianBlur(I2_2, sigam2_2, cv::Size(11, 11), 1.5);
sigam2_2 -= mu2_2;
GaussianBlur(I1_I2, sigam12, cv::Size(11, 11), 1.5);
sigam12 -= mu1_mu2;
cv::Mat t1, t2, t3;
t1 = 2 * mu1_mu2 + C1;
t2 = 2 * sigam12 + C2;
t3 = t1.mul(t2);
t1 = mu1_2 + mu2_2 + C1;
t2 = sigma1_2 + sigam2_2 + C2;
t1 = t1.mul(t2);
cv::Mat ssim_map;
divide(t3, t1, ssim_map);
cv::Scalar mssim = cv::mean(ssim_map);
double ssim = (mssim.val[0] + mssim.val[1] + mssim.val[2]) / 3;
return ssim;
}
}
/**
* NRSS梯度结构相似度
*
* Inputs:
* @param image:
* Return: double
*/
extern "C"
{
double nrss(int height, int width, uchar* frame_data)
{
int count = 0;
cv::Mat image(height, width, CV_8UC3);
uchar* pxvec =image.ptr<uchar>(0);
for(int row = 0; row < height; row++)
{
pxvec = image.ptr<uchar>(row);
for(int col = 0; col < width; col++)
{
for(int c = 0; c < 3; c++)
{
pxvec[col*3+c] = frame_data[count];
count++;
}
}
}
cv::Mat gray_img, Ir, G, Gr;
if (image.channels() == 3){
cv::cvtColor(image, gray_img, CV_BGR2GRAY);
}
//构造参考图像
cv::GaussianBlur(gray_img, Ir, cv::Size(7, 7), 6, 6);
//提取图像和参考图像的梯度信息
cv::Sobel(gray_img, G, CV_32FC1, 1, 1);//计算原始图像sobel梯度
cv::Sobel(Ir, Gr, CV_32FC1, 1, 1);//计算构造函数的sobel梯度
//找出梯度图像 G 中梯度信息最丰富的 N 个图像块,n=64(即划分为8x8的大小)
//计算每个小方块的宽/高
int block_cols = G.cols * 2 / 9;
int block_rows = G.rows * 2 / 9;
//获取方差最大的block
cv::Mat best_G,best_Gr;
float max_stddev = .0f;
int pos = 0;
for (int i = 0; i < 64; ++i){
int left_x = (i % 8)*(block_cols / 2);
int left_y = (i / 8)*(block_rows / 2);
int right_x = left_x + block_cols;
int right_y = left_y + block_rows;
if (left_x < 0) left_x = 0;
if (left_y < 0) left_y = 0;
if (right_x >= G.cols) right_x = G.cols - 1;
if (right_y >= G.rows) right_y = G.rows - 1;
cv::Rect roi(left_x,left_y,right_x-left_x,right_y-left_y);
cv::Mat temp=G(roi).clone();
cv::Scalar mean,stddev;
cv::meanStdDev(temp, mean, stddev);
if (stddev.val[0]>max_stddev){
max_stddev = static_cast<float>(stddev.val[0]);
pos = i;
best_G = temp;
best_Gr = Gr(roi).clone();
}
}
//计算结构清晰度NRSS
double result = 1 - ssim(best_G, best_Gr);
return result;
}
}
compile.sh
[ ! -f $1 ] && exit
srcfile=$1
srcname=${1##*/};
# g++ -g $srcfile -o ${srcname%.*} `pkg-config --cflags --libs opencv`
# g++ hello.cpp -fPIC -shared -o libhello.so
g++ $srcfile -o ${srcname%.*}.so -std=c++11 -lopencv_core -lopencv_highgui -lopencv_imgproc -shared -fPIC
# g++ src/blur_detection.cpp -o lib/blur_detection.so -std=c++11 -lopencv_core -lopencv_highgui -lopencv_imgproc -shared -fPIC
参考:
https://github.com/Leezhen2014/python–/blob/master/BlurDetection.py
https://www.pyimagesearch.com/2015/09/07/blur-detection-with-opencv/