由于论文可能会需要,学习顺便整理一下衡量两幅图片相似度的指标,根据需要可能会补上某些方法的C++实现代码
一、resize
首先需要改变一下图片的尺寸,现在一张普通的照片都上千万像素(如博主自己的手机拍出来的照片都5792×4344,2500多万像素),算法的输入采用原始图片的话,需要处理几千万个像素值,时间太长而且也没什么必要。
所以原始图片需要经过resize改变一下尺寸,但这里采用的缩小尺寸的算法应该会对后续计算图片相似度的算法产生影响。
1、OpenCV cv::resize()
OpenCV库内有个resize()函数,封装了大部分改变图片尺寸的算法
void cv::resize (InputArray src,
OutputArray dst,
Size dsize,
double fx = 0,
double fy = 0,
int interpolation = INTER_LINEAR
)
参数: src - 输入图像。 dst - 输出图像;它的大小为 dsize,dst 的类型与 src 的类型相同。 dsize - 输出图像的大小;如果它等于零,则计算为:dsize = Size(round(fxsrc.cols), round(fysrc.rows))。dsize 或 fx 和 fy 必须为非零。 fx - 沿水平轴的比例因子;当它等于 0 时,它被计算为(double)dsize.width/src.cols fy - 沿垂直轴的比例因子;当它等于 0 时,它被计算为(double)dsize.height/src.rows interpolation - 插值方法
其中,选择不同的interpolation参数就可以选择不同的OpenCV实现的大部分改变图片尺寸的算法,包括: INTER_NEAREST- 最近邻插值(最近邻插值) INTER_LINEAR双线性插值(默认使用) INTER_AREA- 使用像素区域关系重采样。当图像被缩放时,它类似于 INTER_NEAREST 方法。 INTER_CUBIC- 4x4 像素邻域上的双三次插值 INTER_LANCZOS4- 8x8 像素邻域上的 Lanczos 插值
二、欧氏距离
第一个能想到的也是最简单最常见的,就是欧氏距离。
欧氏距离在机器学习中常用于衡量两个样本之间的差异程度,距离越远说明样本间的差异越大,也就是相似度越小
将两张图片展开成一维向量:
A
?
=
(
a
1
,
a
2
,
a
3
,
.
.
.
.
.
.
,
a
n
)
\vec{A}=(a_1,a_2,a_3,......,a_n)
A
=(a1?,a2?,a3?,......,an?)
B
?
=
(
b
1
,
b
2
,
b
3
,
.
.
.
.
.
.
,
b
n
)
\vec{B}=(b_1,b_2,b_3,......,b_n)
B
=(b1?,b2?,b3?,......,bn?) 其中
a
i
,
b
i
a_i,b_i
ai?,bi?自然是各个像素点的像素值,计算向量
A
?
\vec{A}
A
和
B
?
\vec{B}
B
的欧氏距离:
d
(
A
?
,
B
?
)
=
∑
i
(
a
i
?
b
i
)
2
???
(
i
=
1
,
.
.
.
.
.
.
,
n
)
d(\vec{A},\vec{B})=\sqrt{\sum_i(a_i-b_i)^2}~~~(i=1,......,n)
d(A
,B
)=i∑?(ai??bi?)2
????(i=1,......,n) d越大,表示两张图片相似度越小;d越小,表示两张图片相似度越大
实现:
随便找三张图,一张作为基准图,一张是变化稍小的图,一张是完全不一样的图
#include<opencv2/opencv.hpp>
#include<iostream>
#include <math.h>
#include<string>
using namespace cv;
int main()
{
Mat img_1 = imread("D:\\0001.jpg");
Mat img_1gray;
cvtColor(img_1, img_1gray, COLOR_BGR2GRAY);
resize(img_1gray, img_1gray, Size(100, 75), 0, 0, CV_INTER_AREA);
img_1gray = img_1gray.reshape(0,1);
Mat img_2 = imread("D:\\0002.jpg");
Mat img_2gray;
cvtColor(img_2, img_2gray, COLOR_BGR2GRAY);
resize(img_2gray, img_2gray, Size(100, 75), 0, 0, CV_INTER_AREA);
img_2gray = img_2gray.reshape(0, 1);
Mat img_3 = imread("D:\\0003.jpg");
Mat img_3gray;
cvtColor(img_3, img_3gray, COLOR_BGR2GRAY);
resize(img_3gray, img_3gray, Size(100, 75), 0, 0, CV_INTER_AREA);
img_3gray = img_3gray.reshape(0, 1);
float eucDis1_1 = 0;
for (auto i = 0; i < img_1gray.cols; ++i)
{
eucDis1_1 += sqrt(pow(img_1gray.at<uchar>(0, i) - img_1gray.at<uchar>(0, i), 2));
}
std::cout << eucDis1_1 << std::endl;
float eucDis1_2 = 0;
for (auto i = 0; i < img_1gray.cols;++i)
{
eucDis1_2 += sqrt(pow(img_1gray.at<uchar>(0, i) - img_2gray.at<uchar>(0, i), 2));
}
std::cout << eucDis1_2 << std::endl;
float eucDis1_3 = 0;
for (auto i = 0; i < img_1gray.cols; ++i)
{
eucDis1_3 += sqrt(pow(img_1gray.at<uchar>(0, i) - img_3gray.at<uchar>(0, i),2));
}
std::cout << eucDis1_3 << std::endl;
return 0;
}
结果还算合理: 从公式可以看出,以欧氏距离作为衡量指标,只关注了两张图片相同坐标处的像素值的差异(如
a
1
,
b
1
a_1,b_1
a1?,b1?),但判断两张图片相似度的方法感觉不应该只是关注相同坐标处的像素值的差异,图片自身像素值之间的位置关系应该也要作为考虑的信息,欧氏距离显然没有把这个信息纳入考虑范围。
图A
|
图B
| 比如上面两张图片,它们的欧氏距离肯定比较大,但从直观上看,这两张图片肯定是比较相似的图片。
三、角度
https://blog.csdn.net/weixin_44179561/article/details/124205071?spm=1001.2014.3001.5501
参考这篇博客中比较文档距离的一个思路,可以通过计算两个向量间的角度来衡量图片相似度:
d
(
A
?
,
B
?
)
=
a
r
c
c
o
s
<
A
?
,
B
?
>
=
a
r
c
c
o
s
(
A
?
?
B
?
∣
A
?
∣
?
∣
B
?
∣
)
d(\vec{A},\vec{B})= arccos<\vec{A},\vec{B}>=arccos(\frac{\vec{A}·\vec{B}}{|\vec{A}|~|\vec{B}|})
d(A
,B
)=arccos<A
,B
>=arccos(∣A
∣?∣B
∣A
?B
?) 最终可以用两个向量之间的角度来表示图片相似度,
0
°
0°
0°表示两个图片完全一样,
90
°
90°
90°表示两个图片完全不一样。
这样似乎也有问题,因为余弦距离更加关注两个向量在方向性的差异,同样没有考虑像素之间的位置信息
图A
|
图B
| 这么两张图片所对应的向量之间的角度肯定比较大,但是直观上,这两张图片也应该是比较相似的图片
四、汉明距离
汉明距离表示两个(相同长度)的二进制数对应位不同的数量,将这两个二进制数按位做异或操作,统计得到的结果的1的数量,就是这两个二进制数的汉明距离。比如:
A
?
=
(
1
,
0
,
0
,
1
,
0
,
1
)
\vec{A}=(1,0,0,1,0,1)
A
=(1,0,0,1,0,1)
B
?
=
(
1
,
0
,
0
,
1
,
0
,
1
)
\vec{B}=(1,0,0,1,0,1)
B
=(1,0,0,1,0,1)
对应位的0或1全部一样,将这两个二进制数做异或操作,结果为:
000000
000000
000000,结果中1的数量为0,则这两个向量所化成的二进制数的汉明距离为0
向量相似度越高,对应的汉明距离应越小。上文中的
A
?
和
B
?
\vec{A}和\vec{B}
A
和B
,完全一样,所以汉明距离为0
问题是,一张图片(当然是展开成一维向量后),该按照什么样的规则化成二进制数,根据所用的规则的不同,分成了几个不同的方法:
1、ahash(Average Hash,均值哈希算法)
- 计算每个向量中数值的均值:
A
?
=
(
a
1
,
a
2
,
a
3
,
.
.
.
.
.
.
,
a
n
)
????????
a
v
e
r
a
g
e
A
=
∑
i
a
i
n
???
(
i
=
1
,
.
.
.
.
.
.
,
n
)
\vec{A}=(a_1,a_2,a_3,......,a_n)~~~~~~~~averageA=\frac{\sum_ia_i}{n}~~~(i=1,......,n)
A
=(a1?,a2?,a3?,......,an?)????????averageA=n∑i?ai?????(i=1,......,n)
B
?
=
(
b
1
,
b
2
,
b
3
,
.
.
.
.
.
.
,
b
n
)
????????
a
v
e
r
a
g
e
B
=
∑
i
b
i
n
???
(
i
=
1
,
.
.
.
.
.
.
,
n
)
\vec{B}=(b_1,b_2,b_3,......,b_n)~~~~~~~~averageB=\frac{\sum_ib_i}{n}~~~(i=1,......,n)
B
=(b1?,b2?,b3?,......,bn?)????????averageB=n∑i?bi?????(i=1,......,n) - 将向量内各位置的值与该向量求得的均值作比较,大于或等于的,记为1;小于的记为0
i
f
??
(
a
i
≥
a
v
e
r
a
g
e
A
)
??
a
i
=
1
e
l
s
e
??
a
i
=
0
if~~(a_i≥averageA)~~a_i=1\\ else~~a_i=0
if??(ai?≥averageA)??ai?=1else??ai?=0
i
f
??
(
b
i
≥
a
v
e
r
a
g
e
B
)
??
b
i
=
1
e
l
s
e
??
b
i
=
0
if~~(b_i≥averageB)~~b_i=1\\ else~~b_i=0
if??(bi?≥averageB)??bi?=1else??bi?=0 - 计算得到的两个二进制数的汉明距离,汉明距离越小,意味着两个图片越相似
2、phash(Perceptual Hash,感知哈希算法)
- 计算每个向量的离散余弦变换(DCT):
离散余弦变换博主尚未了解,贴上OpenCV的内置函数:
cv::dct(src, srcDCT);
D
A
?
=
d
c
t
(
A
?
)
\vec{D_A}=dct(\vec{A})
DA?
?=dct(A
)
D
B
?
=
d
c
t
(
B
?
)
\vec{D_B}=dct(\vec{B})
DB?
?=dct(B
) 参数: src-需要进行计算的向量(或矩阵,即图片) srcDCT-完成离散余弦变换后的向量(或矩阵,即图片)
离散余弦变换具有很强的”能量集中”特性,计算得到的图片,左上方称为低频数据,右下方称为高频数据。大多数的自然信号(包括声音和图像)的能量都集中在离散余弦变换后的低频部分。因此也可以在图像压缩算法中用来进行有损压缩。(如JPEG压缩编码)
- 计算经过离散余弦变换后得到的向量中各位置数值的均值:
D
A
?
=
(
a
1
,
a
2
,
a
3
,
.
.
.
.
.
.
,
a
n
)
????????
a
v
e
r
a
g
e
A
=
∑
i
a
i
n
???
(
i
=
1
,
.
.
.
.
.
.
,
n
)
\vec{D_A}=(a_1,a_2,a_3,......,a_n)~~~~~~~~averageA=\frac{\sum_ia_i}{n}~~~(i=1,......,n)
DA?
?=(a1?,a2?,a3?,......,an?)????????averageA=n∑i?ai?????(i=1,......,n)
D
B
?
=
(
b
1
,
b
2
,
b
3
,
.
.
.
.
.
.
,
b
n
)
????????
a
v
e
r
a
g
e
B
=
∑
i
b
i
n
???
(
i
=
1
,
.
.
.
.
.
.
,
n
)
\vec{D_B}=(b_1,b_2,b_3,......,b_n)~~~~~~~~averageB=\frac{\sum_ib_i}{n}~~~(i=1,......,n)
DB?
?=(b1?,b2?,b3?,......,bn?)????????averageB=n∑i?bi?????(i=1,......,n) - 将
D
A
?
\vec{D_A}
DA?
?和
D
B
?
\vec{D_B}
DB?
?分别与该向量求得的均值作比较,大于或等于的,记为1;小于的记为0
i
f
??
(
a
i
≥
a
v
e
r
a
g
e
A
)
??
a
i
=
1
e
l
s
e
??
a
i
=
0
if~~(a_i≥averageA)~~a_i=1\\ else~~a_i=0
if??(ai?≥averageA)??ai?=1else??ai?=0
i
f
??
(
b
i
≥
a
v
e
r
a
g
e
B
)
??
b
i
=
1
e
l
s
e
??
b
i
=
0
if~~(b_i≥averageB)~~b_i=1\\ else~~b_i=0
if??(bi?≥averageB)??bi?=1else??bi?=0 - 计算得到的两个二进制数的汉明距离,汉明距离越小,意味着两个图片越相似
phash与ahash的区别在于计算均值前,图片先经过了一遍离散余弦变换(DCT)
3、dhash(Difference Hash,差异哈希算法)
-
遍历每个向量,逐位与下一位进行比较,若大于下一位,则该位为1,否则为0
f
o
r
(
i
=
1
;
i
<
n
?
1
;
+
+
i
)
???????????????
i
f
??
(
a
i
≥
a
i
+
1
)
??
a
i
=
1
e
l
s
e
??
a
i
=
0
for(i=1;i<n-1;++i)\\ ~~~~~~~~~~~~~~~if~~(a_i≥a_i+1)~~a_i=1\\ else~~a_i=0
for(i=1;i<n?1;++i)???????????????if??(ai?≥ai?+1)??ai?=1else??ai?=0 由于最后一位没有下一位,向量上会少掉最后一位,图片矩阵上会少掉最后一列 -
计算得到的两个二进制数的汉明距离,汉明距离越小,意味着两个图片越相似
图像均值(ahash)哈希本质上是对像素(颜色)的比较 图像感知(phash)哈希由于做了离散余弦变换操作,本质上是对频率的比较 图像差异(dhash)哈希本质上是基于变化的感知,更关注图片的像素值的变化
五、直方图+巴氏系数
1、直方图
直方图是描述一个图片中,颜色出现的次数(或频率)的一种统计图象,因为只关心某个像素值出现的次数,不关注像素值的位置,所以具有旋转、平移、缩放等不变性。且计算代价较小,只需要遍历整个图像就能得到
直方图的横坐标为某个像素值,纵坐标为像素值在图片内出现的次数,如下图所示的图片矩阵中,像素值“1”出现了两次,所以该图片的直方图中,像素值“1”对应的纵坐标就该是2
图片矩阵
|
直方图
|
有时候为了避免图片分辨率造成的影响,(分辨率高的图像,像素个数肯定多,出现次数肯定多),会将出现次数除以图片的总像素个数,转化成出现概率。如上图中的图片矩阵的总像素个数是9,像素值“1”的出现次数是2,它的出现概率为
2
9
\frac{2}{9}
92?。 直方图分为灰度直方图与RGB直方图
灰度直方图:对于灰度直方图,只需要维护一个长度为256的数组Gray[256],遍历图片矩阵,假设矩阵内某个位置的像素值为124,则Gray[124]++。
RGB直方图:如果每种原色都可以取256个值,那么整个颜色空间共有1600万种颜色(256的三次方)。针对这1600万种颜色比较直方图,计算量实在太大了,因此可以通过划分bin的方式进行简化。可以将0~255分成四个区:0~63为第0区,64~127为第1区,128~191为第2区,192~255为第3区。这意味着红绿蓝分别有4个区,总共可以构成64种组合(4的3次方)。再根据图片矩阵中某个位置的RGB值,决定直方图内哪个bin值加一。
直方图最终也可以表示成一个向量的形式,向量维数为直方图横坐标的个数,向量中的值为直方图纵坐标的值,如上图中的统计出现次数的直方图实际上可以写成向量形式:
A
?
=
(
2
,
3
,
2
,
1
,
0
,
1
)
\vec{A}=(2,3,2,1,0,1)
A
=(2,3,2,1,0,1)
2、巴氏系数
既然直方图最终表示成了一个向量,而每张图片都有它相应的直方图,那么通过计算两个向量之间的某些指标,自然也能衡量图片的相似度了。
如上二、欧氏距离,三、角度,四、汉明距离均可以在此处用来计算
但在直方图匹配中,巴氏系数是最常用也最有效的指标,计算公式如下:
p
(
A
?
,
B
?
)
=
∑
i
a
i
b
i
????
(
i
=
1
,
.
.
.
.
.
.
,
n
)
p(\vec{A},\vec{B})=\sum_i\sqrt{a_ib_i}~~~~(i=1,......,n)
p(A
,B
)=i∑?ai?bi?
?????(i=1,......,n)
对于以概率分布为纵坐标的直方图来说,巴氏系数的值在0到1之间,0表示完全不相同,1表示完全相同
可以看到,巴氏系数更关注两张图片中,同样的颜色出现的次数是否相近。如
a
i
a_i
ai?很大时,若
b
i
b_i
bi?稍小,便会对最终结果造成负面影响,而若
b
i
b_i
bi?为0,乘积的结果完全就是0,对最终结果的值毫无奉献。
六、结构特征
相比于直方图关注图片中颜色的频率,忽略图片中内容的结构(也正因此,直方图相关的方法才具有旋转、平移、缩放等不变性),也能尝试关注图片中内容的结构(比如说物体的轮廓)
首先,将原图转成一张灰度图片,然后,确定一个阈值(可以用大津法——最大类间方差法来确定),将灰度图片转成黑白图片
灰度图
|
二值化后的图
|
可以预见的是,如果两张图片中的内容是一样的(比如图中都是鹦鹉),那么两张图片的黑白轮廓应该是相近的。
二值化后的图像其实是一个0-1矩阵,两个二值化矩阵的不同之处越少,就代表两张图片越相似。这可以用汉明距离衡量,将两个0-1矩阵展开成一维向量,求汉明距离。若汉明距离越小,意味着两个二值化矩阵之间的不同之处越少,意味着两张图片越相似。
结构特征的方法,由于关注图片中内容的轮廓,所以应该是不具有旋转、平移、缩放等不变性的。但对内容相似,颜色不同的图片,应该会有比较好的效果,比如下面两张图中的两只狗,颜色明显不同,从直方图的角度来说,图片相似性会比较低。但是由于图中内容都是“狗”,结构与轮廓会比较相似,从结构特征的角度来说,图片相似性会比较高,更符合实际的判定。
|