图像质量评价(IQA)算法以任意图像作为输入,输出质量分数作为输出。有三种类型的IQA:
在OpenCV contrib的quality模块中一共有提供了5种图像质量评价算法,按上面的类别分仅提供全参考图像质量评价和无参考图像质量评价两种类别的算法,没有半参考图像质量评价算法。
其中包含的5种图像质量评价算法具体如下:
这5种图像质量评价算法中,除了BRISQUE是无参考图像质量评价算法外,其他都是全参考图像质量评价。本文不具体介绍这些算法的原理,仅介绍这些算法的应用。想知道具体原理见链接:
事实上,各种图像质量评估算法都是寻找不同数学公式给出一个评判结果,差异并不那么大,仅知道使用即可。就全参考图像质量评价算法而言,一般情况下GMSD效果比其他全参考图像质量评价算法效果好。无参考图形质量评价以BRISQUE为代表。半参考图像质量评价更多用于发论文,实际应用不多。近年来也有深度学习应用于图像质量评估,但是效果还不错,但速度太慢。关于图像质量评估算法具体进一步研究可参考链接:
图像质量评估指标(Image Quality Assessment,IQA)
#include <opencv2/opencv.hpp>
#include <opencv2/quality.hpp>
using namespace std;
using namespace cv;
// 计算结果均值
double calMEAN(Scalar result)
{
int i = 0;
double sum = 0;
// 计算总和
for (auto val : result.val)
{
if (0 == val || isinf(val))
{
break;
}
sum += val;
i++;
}
return sum / i;
}
// 均方误差 MSE
double MSE(Mat img1, Mat img2)
{
// output quality map
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// compute MSE via static method
// cv::noArray() if not interested in output quality maps
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
cv::Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_map);
/* 另外一种动态计算的方法
// alternatively, compute MSE via instance
cv::Ptr<quality::QualityBase> ptr = quality::QualityMSE::create(img1);
// compute MSE, compare img1 vs img2
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);
*/
return calMEAN(result_static);
}
// 峰值信噪比 PSNR
double PSNR(Mat img1, Mat img2)
{
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
// 第四个参数为PSNR计算公式中的MAX,即图片可能的最大像素值,通常为255
cv::Scalar result_static = quality::QualityPSNR::compute(img1, img2, quality_map, 255.0);
/* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualityPSNR::create(img1, 255.0);
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);*/
return calMEAN(result_static);
}
// 梯度幅度相似性偏差 GMSD
double GMSD(Mat img1, Mat img2)
{
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
cv::Scalar result_static = quality::QualityGMSD::compute(img1, img2, quality_map);
/* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualityGMSD::create(img1);
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);*/
return calMEAN(result_static);
}
// 结构相似性 SSIM
double SSIM(Mat img1, Mat img2)
{
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
cv::Scalar result_static = quality::QualitySSIM::compute(img1, img2, quality_map);
/* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualitySSIM::create(img1);
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);*/
return calMEAN(result_static);
}
// 盲/无参考图像空间质量评估器 BRISQUE
double BRISQUE(Mat img)
{
// path to the trained model
cv::String model_path = "./model/brisque_model_live.yml";
// path to range file
cv::String range_path = "./model/brisque_range_live.yml";
// 静态计算方法
cv::Scalar result_static = quality::QualityBRISQUE::compute(img, model_path, range_path);
/* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualityBRISQUE::create(model_path, range_path);
// computes BRISQUE score for img
cv::Scalar result = ptr->compute(img);*/
return calMEAN(result_static);
}
void qualityCompute(String methodType, Mat img1, Mat img2)
{
// 算法结果和算法耗时
double result;
TickMeter costTime;
costTime.start();
if ("MSE" == methodType)
result = MSE(img1, img2);
else if ("PSNR" == methodType)
result = PSNR(img1, img2);
else if ("PSNR" == methodType)
result = PSNR(img1, img2);
else if ("GMSD" == methodType)
result = GMSD(img1, img2);
else if ("SSIM" == methodType)
result = SSIM(img1, img2);
else if ("BRISQUE" == methodType)
result = BRISQUE(img2);
costTime.stop();
cout << methodType << "_result is: " << result << endl;
cout << methodType << "_cost time is: " << costTime.getTimeSec() / costTime.getCounter() << " s" << endl;
}
int main()
{
// img1为基准图像,img2为检测图像
cv::Mat img1, img2;
img1 = cv::imread("image/original-rotated-image.jpg");
img2 = cv::imread("image/noise-version.jpg");
if (img1.empty() || img2.empty())
{
cout << "img empty" << endl;
return 0;
}
// 结果越小,检测图像和基准图像的差距越小
qualityCompute("MSE", img1, img2);
// 结果越小,检测图像和基准图像的差距越大
qualityCompute("PSNR", img1, img2);
// 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越大
qualityCompute("GMSD", img1, img2);
// 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越小
qualityCompute("SSIM", img1, img2);
// BRISQUE不需要基准图像
// 结果为一个0到100之间的数,越小表示检测图像质量越好
qualityCompute("BRISQUE", cv::Mat{}, img2);
system("pause");
return 0;
}
小陶还是更加推荐使用Python代码,因为简单很多。
# -*- coding: utf-8 -*-
"""
Created on Fri Oct 9 05:27:28 2020
@author: luohenyueji
"""
import cv2
import numpy as np
import time
# ----- 时间装饰器,打印运行结果和运行时间
def usetime(func):
def inner(*args, **kwargs):
time_start = time.time()
# 装饰的函数在此运行
result = func(*args, **kwargs)
time_run = time.time() - time_start
# 打印结果
print(func.__name__ + '_result is: {:.3f}'.format(result))
# 打印运行时间
print(func.__name__ + '_cost time is: {:.3f} s'.format(time_run))
return inner
# ----- 均方误差 MSE
@usetime
def MSE(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
result_static, quality_map = cv2.quality.QualityMSE_compute(img1, img2)
# 另外一种动态计算的方法,但是MSE的计算可能有问题
# obj = cv2.quality.QualityMSE_create(img1)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score
# ----- 峰值信噪比 PSNR
@usetime
def PSNR(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
# maxPixelValue参数为PSNR计算公式中的MAX,即图片可能的最大像素值,通常为255
result_static, quality_map = cv2.quality.QualityPSNR_compute(img1, img2, maxPixelValue=255)
# 另外一种动态计算的方法
# obj = cv2.quality.QualityPSNR_create(img1, maxPixelValue=255)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
return score
# ----- 梯度幅度相似性偏差 GMSD
@usetime
def GMSD(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
result_static, quality_map = cv2.quality.QualityGMSD_compute(img1, img2)
# 另外一种动态计算的方法
# obj = cv2.quality.QualityGMSD_create(img1)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score
# ----- 结构相似性 SSIM
@usetime
def SSIM(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
result_static, quality_map = cv2.quality.QualitySSIM_compute(img1, img2)
# 另外一种动态计算的方法
# obj = cv2.quality.QualitySSIM_create(img1)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score
# ----- 盲/无参考图像空间质量评估器 BRISQUE
@usetime
def BRISQUE(img):
# path to the trained model
model_path = "./model/brisque_model_live.yml"
# path to range file
range_path = "./model/brisque_range_live.yml"
# 静态计算方法
result_static = cv2.quality.QualityBRISQUE_compute(img, model_path, range_path)
# # 另外一种动态计算的方法
# obj = cv2.quality.QualityBRISQUE_create(model_path, range_path)
# result = obj.compute(img)
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score
def main():
# img1为基准图像,img2为检测图像
img1 = cv2.imread("image/cut-original-rotated-image.jpg")
img2 = cv2.imread("image/cut-noise-version.jpg")
if img1 is None or img2 is None:
print("img empty")
return
# 结果越小,检测图像和基准图像的差距越小
MSE(img1, img2)
# 结果越小,检测图像和基准图像的差距越大
PSNR(img1, img2)
# 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越大
GMSD(img1, img2)
# 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越小
SSIM(img1, img2)
# 结果为一个0到100之间的数,越小表示检测图像质量越好
BRISQUE(img2)
if __name__ == '__main__':
main()
对于612x816分辨率图片,结果正确的有MSE,GMSD,BRISQUE;对于305x305分辨率图片,如果从局部上来看,噪声图片和模糊图片清晰图差不太多,结果正确的有PSNR,GMSD。然而对于BRISQUE模糊图片的清晰度评分比原图高。所以通常情况下,有参考图片,GMSD准确率最高,其他方法并不靠谱,BRISQUE需要更加完整的大图才有好的效果。
现在图像质量评价算法都只能针对某种特定环境使用,在实际最好针对每一种图像噪声情况设定一种判定算法,现在各个视频检测平台也都是这样做的。如果普通使用看看GMSD和BRISQUE即可。
本文内容主要转载于[[OpenCV实战]48 基于OpenCV实现图像质量评价](https://blog.csdn.net/LuohenYJ/article/details/108984313)