昊虹君 发表于 2022-11-25 13:04

尺寸相同的两幅图像的两个相似度评价指标-PSNR和SSIM(原理介绍及C++代码实现)

尺寸相同的两幅图像的两个相似度评价指标-PSNR和SSIM(原理介绍及C++代码实现)

图像的相似度在目标检测跟踪、图像内容搜索、特征分析领域有着广泛的应用。

对于尺寸相同的图像,常见的图像相似度评较指标有:峰值信噪比PSNR与结构相似性SSIM。
峰值信噪比PSNR的原理比结构相似性SSIM的原理简单。

下面分别介绍两种相似度评较指标。

1 峰值信噪比PSNR(Peak Signal to Noise Ratio)
PSNR基于图像像素灰度值进行统计分析。
由于与人类视觉的特点有差异,PSNR通常出现的评价结果与人的主要感觉不一致,但其仍然是一个有参考价值的评价指标。

PSNR可简单地由均方差MSE进行定义。
对于两幅图像I与K,尺寸大小均为m×n,它们的均方误差(MSE)的定义为:
http://pic1.hhai.cc/pic1/2022/2022-11/005/11.png

峰值信噪比PSNR的定义为:
http://pic1.hhai.cc/pic1/2022/2022-11/005/12.png
其中,
MSE表示当前图像I与K的均方误差,
MAX表示图像I像素点的最大可取值,如果每个像素点用8位表示,那么最大值就是255;如果每个像素点用N位表示,那么MAX就为2^{N}-1。
如果图像是三通道的,那么MSE为所有通道的方差之和除以图像尺寸再除以3。
PSNR的单位为dB,PSNR的值越大,图像相似度越高。在图像压缩中,压缩前和压缩后的图像峰值信噪比PSNR通常为30~40dB。

可以使用OpenCV的函数cv::PSNR()来求PSNR。
函数cv::PSNR()的官方资料如下:
http://pic1.hhai.cc/pic1/2022/2022-11/005/13.png
该函数的使用示例代码如下:
代码中用到的图像下载链接:
https://pan.baidu.com/s/1jY1Qd1IzGh-5Ah2iK_PmRQ?pwd=ctdf

//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术

//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询

//OpenCV版本 OpenCV3.0

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{

        // 读取源图像及两幅待检测相似度的图像
        cv::Mat srcImage1 = cv::imread("F:/material/images/2022/2022-11/PSNR-SSIM/hand1.jpg", 1);
        if (srcImage1.empty())
                return -1;

        cv::Mat srcImage2 = cv::imread("F:/material/images/2022/2022-11/PSNR-SSIM/hand2.jpg", 1);
        if (srcImage2.empty())
                return -1;

        cv::Mat srcImage3 = cv::imread("F:/material/images/2022/2022-11/PSNR-SSIM/circle.jpg", 1);
        if (srcImage3.empty())
                return -1;

        double psnr1, psnr2;

        psnr1 = cv::PSNR(srcImage1, srcImage2);
        std::cout << "hand1.jpg VS hand2.jpg PSNR is: " << psnr1 << std::endl << std::endl;

        psnr2 = cv::PSNR(srcImage1, srcImage3);
        std::cout << "hand1.jpg VS circle.jpg PSNR is: " << psnr2 << std::endl << std::endl;

        return 0;
}
运行结果如下:
http://pic1.hhai.cc/pic1/2022/2022-11/005/14.png
                                          
http://pic1.hhai.cc/pic1/2022/2022-11/005/15.png

也可以根据上面的原理自己实现对PSNR指标值的求取。
自己写的计算两幅图像峰值信噪比PSNR的C++代码如下:
代码中用到的图像下载链接:
https://pan.baidu.com/s/1jY1Qd1IzGh-5Ah2iK_PmRQ?pwd=ctdf

//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术

//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询

//OpenCV版本 OpenCV3.0

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

// PSNR 峰值信噪比计算,如果两幅图像相似度较高,那么返回数值通常为30-50dB,值越大相似度越高
double PSNR(const Mat& I1, const Mat& I2)//注意,两幅图的大小要一致
{
        cv::Mat s1;
        // 计算图像差|I1 - I2|
        absdiff(I1, I2, s1);
        // 转成32浮点数进行平方
        s1.convertTo(s1, CV_32F);
        // s1*s1,即|I1 - I2|^2
        s1 = s1.mul(s1);
        // 分别叠加每个通道的元素,存于s中
        cv::Scalar s = sum(s1);
        // 计算所有通道元素和
        double sse = s.val + s.val + s.val;
        // 当元素很小时返回0值
        if (sse <= 1e-10)
                return 0;
        else
        {
                // 根据公式计算当前I1与I2的均方误差
                double mse = sse / (double)(I1.channels() * I1.total());
                // 计算峰值信噪比
                double psnr = 10.0*log10((255 * 255) / mse);
                return psnr;
        }
}

int main()
{
        // 读取源图像及两幅待检测相似度的图像
        cv::Mat srcImage1 = cv::imread("F:/material/images/2022/2022-11/PSNR-SSIM/hand1.jpg", 1);
        if (srcImage1.empty())
                return -1;

        cv::Mat srcImage2 = cv::imread("F:/material/images/2022/2022-11/PSNR-SSIM/hand2.jpg", 1);
        if (srcImage2.empty())
                return -1;

        cv::Mat srcImage3 = cv::imread("F:/material/images/2022/2022-11/PSNR-SSIM/circle.jpg",1);
        if (srcImage3.empty())
                return -1;

        cv::imshow("hand1", srcImage1);
        cv::imshow("hand3", srcImage2);
        cv::imshow("circle", srcImage3);

        double psnr_01 = 0;
        psnr_01 = PSNR(srcImage1, srcImage2);//注意,两幅图的大小要一致
        std::cout << "hand1.jpg VS hand2.jpg PSNR is: " << psnr_01 << std::endl;

        double psnr_02 = 0;
        psnr_02 = PSNR(srcImage1, srcImage3);//注意,两幅图的大小要一致
        std::cout << "hand1.jpg VS circle.jpg PSNR is: " << psnr_02 << std::endl;



        cv::waitKey(0);
        return 0;
}
运行结果如下:
http://pic1.hhai.cc/pic1/2022/2022-11/005/16.png
                                          
http://pic1.hhai.cc/pic1/2022/2022-11/005/17.png
从上面的运行结果可以看出,用OpenCV的函数cv::PSNR()和自己写的求解PSNR的函数,结果相同。


2 结构相似性SSIM(Structural Similarity)
SSIM是一种衡量两幅图像相似程度的结构相似性指标,相对PSNR而言,结构相似性在评价图像质量上更能符合人类的视觉特性。
结构相似于基于的原理是自然影像是高度结构化的,领域像素具有较强的关联性。

给定两幅图像,分别为x和y,二者的结构相似性SSIM定义为:
http://pic1.hhai.cc/pic1/2022/2022-11/005/18.png
其中:
μ_x与μ_y分别表示图像x与图像y的均值;

σ_x和σ_y分别表示图像x与图像y的标准差;

C1、C2是用来维持稳定的常数:
C1=(k1*L)^2,C2=(k2*L)^2
k1=0.01,k2= 0.03
L是像素值的动态范围,如果图像某个通道的像素值用一个字节存储,则动态范围L=255。
当L=255,k1=0.01,k2= 0.03时
C1 = 6.5025,C2 = 58.5225

结构相似性的值范围为0到1,当两张图像一模一样时,SSIM的值等于1。

根据上述的计算式,利用OpenCV容易写出计算SSIM的代码。

基于OpenCV计算SSIM的C++代码如下:
下面代码中用到的图像百度网盘下载链接如下:
https://pan.baidu.com/s/1jY1Qd1IzGh-5Ah2iK_PmRQ?pwd=ctdf

各位朋友,很抱歉,写文不易,
如果您需要这个计算SSIM的C++代码,请您花费5元移步淘宝购买(自动发货):
淘宝购买链接:https://item.taobao.com/item.htm?ft=t&id=696136635898

运行结果如下:
http://pic1.hhai.cc/pic1/2022/2022-11/005/19.png
                                          
http://pic1.hhai.cc/pic1/2022/2022-11/005/20.png
结果说明:
上面每个结果都是四个数,之所以是四个数,是因为我们用容器Scalar来存储各通道的SSIM值,一个Scalar容器里有四个dobuble类型的数,所以每个结果都是四个数。
四个数我们只用了前面三个,分别表示第0通道,1通道,2通道的SSIM值。
关于容器Scalar的详细介绍,见链接 https://www.hhai.cc/thread-144-1-1.html
从上面的结果来看,两幅图像的相似度越高,SSIM的值越大,当两幅图像完全相同时,值为1。
页: [1]
查看完整版本: 尺寸相同的两幅图像的两个相似度评价指标-PSNR和SSIM(原理介绍及C++代码实现)