看完原论文后(英语不太好,只看懂了个大概),大致上明白了它是做什么以及如何实现的等,于是决定写一篇博客,所以该篇文章简单介绍一下 SSIM(有能力的话,看原论文或维基比我来介绍更好),并给出我用 opencv 在 C++ 中的 SSIM 算法实现。

首先什么是 SSIM 算法,该算法主要用于检测两张尺寸相同的图像的相似度,但注意到论文标题的中的 Structural,所以实际上它主要通过分别比较两个图像的亮度(l)、对比度(c)、结构(s),然后对这三个要素加权并乘积表示,而在论文中这三个要素用下面公式来表示:

$$l(x, y) = \frac{(2\mu _x\mu _y + C_1)}{(\mu _{x}^{2} + \mu _{y}^{2} + C_1)}$$

$$c(x, y) = \frac{(2\sigma _{x}\sigma _{y} + C_2)}{(\sigma _{x}^{2} + \sigma _{y}^{2} + C_2)}$$

$$s(x, y) = \frac{(\sigma _{xy} + C_3)}{(\sigma _{x} \sigma _{y} + C_3)}$$

这里 $\mu _x$ 为均值,$\sigma _{x}$ 为方差,$\sigma _{xy}$ 表示协方差。这里 $C_1$、$C_2$、$C_3$ 是为了避免当分母为 0 时造成的不稳定问题(所以写算法的时候可以放心,一定不会出现除 0 的情况)

而 SSIM 的一般方程为:

$$ssim(x, y) = [l(x, y)^\alpha \cdot c(x, y)^\beta \cdot s(x, y)^\gamma ]$$

这里一般 $\alpha$,$\beta$,$\gamma$ 取 $1$,并且令 $C_3 = \frac{C_2}{2}$,这样就得到简化的 SSIM 公式:

$$ssim(x, y) = \frac{(2\mu _x\mu _y + C_1)(\sigma _{xy} + C_2)}{(\mu _{x}^{2} + \mu _{y}^{2} + C_1)(\sigma _{x}^{2} + \sigma _{y}^{2} + C_2)}$$

论文中还指出该公式满足下面三个条件:

1.对称性,即 $ssim(x, y) = ssim(y, x)$.

2.有界性,即 $ssim(x, y) <= 1$.

3.有唯一最大值,即 $ssim(x, y) <= 1$,这里当且仅当 $x = y$ 时取等并且这里还有一点是说:两个矩阵的每个矩阵元素都一一对应相等.

上面三个条件很容易理解,对称性保证了当两张图交换位置带入公式时,显然测量指标不应该发生变化,有界性保证了归一化到 0 ~ 1(话说我觉得应该不可能出现小于 0 的情况),评估可以更直观的按百分比来说明,最后一个条件保证了我们使用相同的图像作为输入,一定能得到相等的结果。

实际上公式是十分显然的,我们只要分别求出两张图像的均值以及方差,再对两张图像求协方差就可以带入到 SSIM 公式进行计算,这里计算结果其实得到的是一张图像,它显示了两张图的混叠,借用一下python 以及两张 opencv 的示例图:

(pic5.png)

(pic6.png)

import numpy as np

import matplotlib.pyplot as plt

import cv2 as cv

from skimage import data, img_as_float

from skimage.metrics import structural_similarity as ssim

img1 = cv.imread('C:/opencv/samples/data/pic5.png', cv.CV_8U)

img2 = cv.imread('C:/opencv/samples/data/pic6.png', cv.CV_8U)

def mse(x, y):

return np.linalg.norm(x - y)

fig, axes = plt.subplots()

mse_none = mse(img1, img2)

mssim, s = ssim(img1, img2, data_range=img1.max() - img1.min(), full=True)

label = 'MSE: {:.2f}, SSIM: {:.2f}'

axes.imshow(s, cmap=plt.cm.gray, vmin=0, vmax=1)

axes.set_xlabel(label.format(mse_none, mssim))

axes.set_title('SSIM image')

plt.tight_layout()

plt.show()

会看到 ssim 输出:

而评估算法的性能指标公式是:

$$mssim(x, y) = \frac{1}{M}\sum_{j = 1}^{M} ssim(x_j, y_j)$$

也就是像素取均值。

该算法的优点在于对图像的结构信息敏感,所以相似图像的识别率很高,但缺点也有:

1.如果将原图经过旋转(宽度和高度不同的话试试旋转 180 度)、平移等变换或加噪点后再和原图进行比较就显得吃力了:

2.算法只能处理相同形状的两个图像,但可以多通道。

最后,给出我的 C++ 实现(不支持多通道图像,以后或许会考虑实现多通道):

/********************************************************************************************************************************************************

* 该部分主要功能实现 SSIM 算法

*

* 关于 SSIM 算法参考文献:https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf

* 公式:$$ssim(x, y) = \frac{(2\mu _x\mu _y + C_1)(\sigma _{xy} + C_2)}{(\mu _{x}^{2} + \mu _{y}^{2} + C_1)(\sigma _{x}^{2} + \sigma _{y}^{2} + C_2)}$$

*

* 参数:

* im1: 图像 1

* im2: 图像 2

* window: 滑动窗口大小,用于卷积滤波

* k1: 可调节常数,默认 k1 = 0.01

* k2: 可调节常数,默认 k2 = 0.03

* L: 单通道灰度图像像素值范围,默认 L = 255.0

*

* 返回值:

* 相似度指标 (类型:double)

*********************************************************************************************************************************************************/

float ssim(Mat im1, Mat im2, int window = 7, float k1 = 0.01f, float k2 = 0.03f, float L = 255.f)

{

CV_Assert(im1.size() == im2.size());

int ndim = im1.dims;

float NP = std::powf(window, ndim);

float cov_norm = NP / (NP - 1);

float C1 = (k1 * L) * (k1 * L);

float C2 = (k2 * L) * (k2 * L);

Mat ux, uy;

Mat uxx = im1.mul(im1);

Mat uyy = im2.mul(im2);

Mat uxy = im1.mul(im2);

blur(im1, ux, Size(window, window), Point(-1, -1));

blur(im2, uy, Size(window, window), Point(-1, -1));

blur(uxx, uxx, Size(window, window), Point(-1, -1));

blur(uyy, uyy, Size(window, window), Point(-1, -1));

blur(uxy, uxy, Size(window, window), Point(-1, -1));

Mat ux_sq = ux.mul(ux);

Mat uy_sq = uy.mul(uy);

Mat uxy_m = ux.mul(uy);

Mat vx = cov_norm * (uxx - ux_sq);

Mat vy = cov_norm * (uyy - uy_sq);

Mat vxy = cov_norm * (uxy - uxy_m);

Mat A1 = 2 * uxy_m;

Mat A2 = 2 * vxy;

Mat B1 = ux_sq + uy_sq;

Mat B2 = vx + vy;

Mat ssim_map = (A1 + C1).mul(A2 + C2) / (B1 + C1).mul(B2 + C2);

Scalar mssim = mean(ssim_map);

ssim_map.convertTo(ssim_map, CV_8UC1, 255, 0);

imshow("ssim", ssim_map);

return mssim[0];

}

用下面代码即可测试算法:

#include "opencv2/highgui.hpp"

#include "opencv2/imgproc.hpp"

#include "opencv2/videoio.hpp"

#include

using namespace std;

using namespace cv;

// ssim()

int main(int argc, const char** argv)

{

Mat orginal_im;

if (argc > 1)

orginal_im = imread(argv[1]);

else

orginal_im = imread("C:/opencv/samples/data/lena.jpg");

Mat frame = imread("C:/opencv/samples/data/lena_tmpl.jpg");

Mat im1f, im2f;

orginal_im.convertTo(im1f, CV_32FC1);

frame.convertTo(im2f, CV_32FC1);

ostringstream text;

text << "mssim:" << ssim(im1f, im2f);

putText(frame, text.str(), Point(30, 30),

FONT_HERSHEY_COMPLEX_SMALL, 0.8, Scalar(255, 200, 0), 1, 8);

imshow("Original image", orginal_im);

imshow("Frame", frame);

waitKey(0);

return 0;

}

运行结果:

参考文献:

算法实现参考:

3.skimage.metrics.structural_similarity 源码

ssim算法计算图片_OpenCV 实现图像结构相似度算法 (SSIM 算法)相关推荐

  1. ssim算法计算图片_图像质量评估算法 SSIM(结构相似性)

    SSIM的全称为structural similarity index,即为结构相似性,是一种衡量两幅图像相似度的指标.该指标首先由德州大学奥斯丁分校的图像和视频工程实验室(Laboratory fo ...

  2. 算法心得1:由$nlogn$复杂度的LIS算法引起的思考

    LIS(Longest Increasing Subsequence)是一类典型的动态规划类问题,简化描述如下: 给定$N(n) = \{1,2...,n\}$的一个排列$P(n)$,求$P(n)$中 ...

  3. Dijkstra算法——计算一个点到其他所有点的最短路径的算法

    迪杰斯特拉算法百度百科定义:传送门 gh大佬博客:传送门 迪杰斯特拉算法用来计算一个点到其他所有点的最短路径,是一种时间复杂度相对比较优秀的算法 O(n2)(相对于Floyd算法来说) 是一种单源最短 ...

  4. 聚类算法(四)—— 基于词语相似度的聚类算法(含代码)

    转载请注明出处 简单了解了下目前的一些聚类算法, 聚类算法(一)--DBSCAN 聚类算法(二)-- 优缺点对比 聚类算法(三)-- 评测方法1 聚类算法(三)-- 评测方法2 聚类算法(三)-- 评 ...

  5. 计算图片相似度的方法

    文章目录 1.余弦相似度计算 2.哈希算法计算图片的相似度 3.直方图计算图片的相似度 4.SSIM(结构相似度度量)计算图片的相似度 5.基于互信息(Mutual Information)计算图片的 ...

  6. 计算图片相似度的多种解决方案

    利用直方图距离计算图片相似度 计算公式: 其中,G和S为两张图片的图像颜色分布直方图,N为颜色空间样点数. 这里使用分块的方法计算相似度,用以提高各部分的特征,防止图片颜色相似导致计算的相似度高. 利 ...

  7. 相似图片去重--余弦相似度和sift算法

    ----------------题目------------------- 摄影师小刘爱好摄影,有许多照片(不同格式,不同分辨率),有的是自己拍摄的,有的是朋友的相机帮忙拍到的. 但他很苦恼,因为有很 ...

  8. 使用余弦相似度算法计算文本相似度-数学

    20211201 也就是效果 皮尔逊>余弦>欧式 余弦相似度的局限 皮尔逊的优势,相当于是改进版余弦相似度 欧式与 余弦 欧式侧重于直线距离 归一化之后的欧式和余弦的效果也不同 比如 0, ...

  9. java计算图片相似度_图片相似度比较--算法

    最近由于要租房,所以下载了58同城的APP,在找个人房源过程中发现,58同城会把图片相似的发帖纪录被标志出来,并警告用户此信息可能是假的.这里不讨论58同城的这方面做得人性化.而是就图片相似度算法来做 ...

  10. 批量计算图片的ssim和psnr

    psnr和ssim的计算 import torch import torch.nn.functional as F from math import log10 import cv2 import n ...

最新文章

  1. mongo下面总是缺少那么几个好用的工具--------试试这个吧-----MongoDB管理工具
  2. 20天减10斤 2020-10-21
  3. 阿里云MaxCompute印度开服,加速大数据产业升级
  4. MTK平台环境搭建---Ubuntu Linux 下执行sudo apt-get install提示“现在没有可用的软件包……...
  5. 软件接口测试一个项目的实战,全网最全postman接口测试教程和接口项目实战~从入门到精通!!!...
  6. ofo走出校园观察:市场定位导致产品错位?
  7. Dedecms会员中心注入漏洞
  8. java 用mysql游标_MySQL游标
  9. CentOS 6虚拟机Hadoop安装教程
  10. 汽车电子技术——传感器感知技术
  11. 关于STM32的AD采样串口输出
  12. PicGo搭建Gitee图库
  13. 流程图制作软件绘图技巧:快速学会怎么画流程图
  14. 笔试题算法系列(五)百度2017买帽子
  15. ns注册改服务器,NameSilo域名更改NS服务器简单过程介绍
  16. 天津市高中计算机会考,天津高中会考科目有哪些
  17. 基于JAVA-超市会员积分管理系统-计算机毕业设计源码+系统+lw文档+部署
  18. docker:配置 Docker 加速器
  19. JAVA数据结构--Arraylist
  20. html中div分页操作

热门文章

  1. qq在线客服html代码,QQ在线客服JS代码,自适应漂浮在网页右侧
  2. CCReportAdv(高级WinCC报表控件)
  3. JAVA中读写文件操作
  4. “Unable to register this add-in because its DllRegisterServer returns an error”的解决办法
  5. 堆栈平衡(子程序调用)
  6. 在线抽签html,抽签网页板代码
  7. win7计算机高级还原,最好用的win7一键恢复64位方法
  8. 古建筑测绘任重道远,三维实景建模是唯一突破口?
  9. vs2010 破解版,试用版变正式版
  10. C++程序员常用工具集