图像的相似度在目标检测跟踪、图像内容搜索、特征分析领域有着广泛的应用。
对于尺寸相同的图像,常见的图像相似度评较指标有:峰值信噪比PSNR与结构相似性SSIM。
峰值信噪比PSNR的原理比结构相似性SSIM的原理简单。
下面分别介绍两种相似度评较指标。
1 峰值信噪比PSNR(Peak Signal to Noise Ratio)
PSNR基于图像像素灰度值进行统计分析。
由于与人类视觉的特点有差异,PSNR通常出现的评价结果与人的主要感觉不一致,但其仍然是一个有参考价值的评价指标。
PSNR可简单地由均方差MSE进行定义。
对于两幅图像I与K,尺寸大小均为m×n,它们的均方误差(MSE)的定义为:
峰值信噪比PSNR的定义为:
其中,
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()的官方资料如下:
该函数的使用示例代码如下:
代码中用到的图像下载链接:
https://pan.baidu.com/s/1jY1Qd1IzGh-5Ah2iK_PmRQ?pwd=ctdf
//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;
}
运行结果如下:
也可以根据上面的原理自己实现对PSNR指标值的求取。
自己写的计算两幅图像峰值信噪比PSNR的C++代码如下:
代码中用到的图像下载链接:
https://pan.baidu.com/s/1jY1Qd1IzGh-5Ah2iK_PmRQ?pwd=ctdf
//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[0] + s.val[1] + s.val[2];
// 当元素很小时返回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;
}
运行结果如下:
从上面的运行结果可以看出,用OpenCV的函数cv::PSNR()和自己写的求解PSNR的函数,结果相同。
2 结构相似性SSIM(Structural Similarity)
SSIM是一种衡量两幅图像相似程度的结构相似性指标,相对PSNR而言,结构相似性在评价图像质量上更能符合人类的视觉特性。
结构相似于基于的原理是自然影像是高度结构化的,领域像素具有较强的关联性。
给定两幅图像,分别为x和y,二者的结构相似性SSIM定义为:
其中:
μ_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的代码。
计算SSIM的代码运行结果如下:
结果说明:
上面每个结果都是四个数,之所以是四个数,是因为我们用容器Scalar来存储各通道的SSIM值,一个Scalar容器里有四个dobuble类型的数,所以每个结果都是四个数。
四个数我们只用了前面三个,分别表示第0通道,1通道,2通道的SSIM值。
关于容器Scalar的详细介绍,见链接 https://www.hhai.cc/thread-144-1-1.html 从上面的结果来看,两幅图像的相似度越高,SSIM的值越大,当两幅图像完全相同时,值为1。