SSIM 结构相似性指数

结构相似性指数(Structural Similarity Index,简称SSIM)是一种用于评估两幅图像之间相似性的图像质量评估指标。它是一种广泛应用于图像处理和计算机视觉领域的指标,用于比较两幅图像的相似程度,特别是在图像压缩、图像传输和图像质量改善等应用中。

SSIM的计算基于三个关键的图像质量因素,分别是亮度、对比度和结构。具体来说,SSIM考虑了以下三个方面:

  1. 亮度(Luminance):SSIM测量了两幅图像的亮度分量之间的相似程度。它表示了图像的亮度和对比度是否相似。
  2. 对比度(Contrast):SSIM测量了两幅图像的对比度分量之间的相似程度。它表示了图像中不同区域的颜色对比是否相似。
  3. 结构(Structure):SSIM测量了两幅图像的结构分量之间的相似程度。它表示了图像中纹理和细节的相似性。

SSIM的计算方法包括对图像的每个像素进行比较,并计算相似性分数。SSIM的取值范围在-1到1之间,其中1表示两幅图像完全相同,0表示两幅图像没有相似性,-1表示两幅图像完全相反。

SSIM算法的一个优点是它不仅考虑了亮度信息,还考虑了对比度和结构信息,因此更能反映图像质量的整体感知。它广泛用于图像压缩算法的性能评估、图像质量改善、视频编码和解码、医学图像处理等领域。

公式

参考自 Matlab 文档。SSIM 索引质量评估索引基于三个项的计算,即亮度项、对比度项和结构项。整体索引是三个项的乘法组合。

Exponents — 亮度、对比度和结构项的指数 [1 1 1] (默认) | 由非负数组成的三元素向量

RegularizationConstants — 亮度、对比度和结构项的正则化常量 由非负数组成的三元素向量 亮度、对比度和结构项的正则化常量,指定为由 [c1 c2 c3] 形式的非负数组成的三元素向量。ssim 函数使用这些正则化常量来避免局部均值或标准差接近于零的图像区域的不稳定性。因此,这些常量应使用小的非零值。

默认情况下,

C1 = (0.01*L).^2,其中 L 是指定的 DynamicRange 值。

C2 = (0.03*L).^2,其中 L 是指定的 DynamicRange 值。

C3 = C2/2

C++ 代码实现

跟matlab输出结果会有些出入,因为matlab首先会对图片进行高斯滤波以更好的模拟人眼视觉,对向量的操作也不同,它是元素与元素之间进行加减乘除,最后再取均值,数值会更精确,但也差不了多少。

#include <cmath>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;

double L = 255;
double C1 = 6.5025;
double C2 = 58.5225;
double C3 = C2 / 2;

//向量逐元素的任意次幂
vector<double> mpow(vector<double> v, int n) {
  vector<double> result;
  for (int i = 0; i < v.size(); ++i) {
    double buff = pow(v[i], n);
    result.push_back(buff);
  }
  return result;
}

//计算平均值
double mean(vector<double> v) {
  double result = 0.0;
  for (int i = 0; i < v.size(); ++i) {
    result += v[i];
  }
  return result / v.size();
}

//计算样本方差 n-1
double variance(vector<double> v) {
  double result = 0.0;
  double m = mean(v);
  for (int i = 0; i < v.size(); ++i) {
    result += pow(v[i] - m, 2);
  }
  return result / (v.size() - 1);
}

//计算协方差
// Cov(X, Y) = Σ[(xi - μX) * (yi - μY)] / (n - 1)
//使用 n-1 可以确保协方差的估计与样本方差的估计方法保持一致。
double covariance(vector<double> x, vector<double> y) {
  double result = 0.0;
  double meanX = mean(x);
  double meanY = mean(y);

  //样本数量必须相同
  if (x.size() != y.size()) {
    return 0.0;
  }

  for (int i = 0; i < x.size(); ++i) {
    result += (x[i] - meanX) * (y[i] - meanY);
  }

  return result / (x.size() - 1);
}

//亮度相似性
double l(vector<double> x, vector<double> y) {
  double meanX = mean(x);
  double meanY = mean(y);
  double s1 = 2 * meanX * meanY + C1;
  double s2 = pow(meanX, 2) + pow(meanY, 2) + C1;

  return s1 / s2;
}

//对比度相似性
double c(vector<double> x, vector<double> y) {
  //样本标准差
  double sdX = sqrt(variance(x));
  double sdY = sqrt(variance(y));

  double s1 = 2 * sdX * sdY + C2;
  double s2 = pow(sdX, 2) + pow(sdY, 2) + C2;
  return s1 / s2;
}

//结构相似性
double s(vector<double> x, vector<double> y) {
  //样本标准差
  double sdX = sqrt(variance(x));
  double sdY = sqrt(variance(y));
  double s1 = covariance(x, y) + C3;
  double s2 = sdX * sdY + C3;

  return s1 / s2;
}

// SSIM(x, y) = [l(x, y)]^α * [c(x, y)]^β * [s(x, y)]^γ
//α(亮度权重):0.5
//β(对比度权重):0.5
//γ(结构权重):1.0
double ssim(vector<double> x, vector<double> y) {
  double a1 = 1.0;
  double b1 = 1.0;
  double c1 = 1.0;
  double s1 = pow(l(x, y), a1);
  double s2 = pow(c(x, y), b1);
  double s3 = pow(s(x, y), c1);

  return s1 * s2 * s3;
}

int main() {
  //参考公式
  //https://ww2.mathworks.cn/help/images/ref/ssim.html?s_tid=srchtitle_site_search_1_ssim

  vector<double> img1 = {1, 1, 255, 178};
  vector<double> img2 = {3, 4, 2, 10};

  cout << "l: " << l(img1, img2) << endl;
  cout << "c: " << c(img1, img2) << endl;
  cout << "s: " << s(img1, img2) << endl;
  cout << "ssim value:" << ssim(img1, img2) << endl;

  return 0;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注