IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Python/C++代码:基于OpenCV实现图像质量评价(MSE、PSNR、GMSD、SSIM、BRISQUE)

    52txr发表于 2024-02-28 21:09:00
    love 0

    机器人LOGO AI 摘要 讯飞星光LOGO
    OpenCV中提供了五种图像质量评价算法,包括分为全参考图像质量评价、无参考图像质量评价和半参考图像质量评价三类。其中,全参考图像质量评价主要有均方误差(MSE)、峰值信噪比(PSNR)、结构相似性(SSIM)和梯度幅度相似性偏差(GMSD);无参考图像质量评价有盲/无参考图像空间质量评估器(BRISQUE);半参考图像质量评价主要用于发论文,实际应用不多。在代码实现部分,首先定义了一个计算结果均值的函数calMEAN,然后分别实现了MSE、PSNR、GMSD、SSIM和BRISQUE五个图像质量评价算法的函数。最后在main函数中读取两张图片进行质量评价计算,并输出计算结果和计算耗时。
    内容由讯飞星火GPT生成,经过人工审核后发布

    OpenCV中图像质量评价算法介绍

    相关背景

    图像质量评价(IQA)算法以任意图像作为输入,输出质量分数作为输出。有三种类型的IQA:

    • 全参考图像质量评价,适用情形:一个“干净”参考(非扭曲)图像以衡量扭曲图像的质量。此度量可用于评估图像压缩算法的质量。
    • 半参考图像质量评价,适用情形:如果没有参考图像,而是具有一些选择性信息的图像(例如,水印图像)来比较和测量失真图像的质量。
    • 无参考图像质量评价,适用情形:算法得到的唯一输入是要测量其质量的图像。

    在OpenCV contrib的quality模块中一共有提供了5种图像质量评价算法,按上面的类别分仅提供全参考图像质量评价和无参考图像质量评价两种类别的算法,没有半参考图像质量评价算法。

    其中包含的5种图像质量评价算法具体如下:

    • 均方误差 Mean squared error (MSE)
    • 峰值信噪比 Peak signal-to-noise ratio (PSNR)
    • 结构相似性 Structural similarity (SSIM)
    • 梯度幅度相似性偏差 Gradient Magnitude Similarity Deviation (GMSD)
    • 盲/无参考图像空间质量评估器 Blind/Referenceless Image Spatial Quality Evaluation (BRISQUE)

    这5种图像质量评价算法中,除了BRISQUE是无参考图像质量评价算法外,其他都是全参考图像质量评价。本文不具体介绍这些算法的原理,仅介绍这些算法的应用。想知道具体原理见链接:

    图像质量评价指标MSE/PSNR/SSIM

    梯度幅相似性偏差 GMSD

    图像质量评价BRISQUE

    事实上,各种图像质量评估算法都是寻找不同数学公式给出一个评判结果,差异并不那么大,仅知道使用即可。就全参考图像质量评价算法而言,一般情况下GMSD效果比其他全参考图像质量评价算法效果好。无参考图形质量评价以BRISQUE为代表。半参考图像质量评价更多用于发论文,实际应用不多。近年来也有深度学习应用于图像质量评估,但是效果还不错,但速度太慢。关于图像质量评估算法具体进一步研究可参考链接:

    图像质量评估指标(Image Quality Assessment,IQA)

    代码实现

    C++

    
    #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(推荐)

    小陶还是更加推荐使用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)



沪ICP备19023445号-2号
友情链接