[三维重建] [机器学习] 图片相似度

由于论文可能会需要,学习顺便整理一下衡量两幅图片相似度的指标,根据需要可能会补上某些方法的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(aibi)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
	resize(img_1gray, img_1gray, Size(100, 75), 0, 0, CV_INTER_AREA);
	//展开成一维
	img_1gray = img_1gray.reshape(0,1);

	//载入变化比较小的图2
	Mat img_2 = imread("D:\\0002.jpg");
	//转化成灰度图
	Mat img_2gray;
	cvtColor(img_2, img_2gray, COLOR_BGR2GRAY);
	//resize
	resize(img_2gray, img_2gray, Size(100, 75), 0, 0, CV_INTER_AREA);
	//展开成一维
	img_2gray = img_2gray.reshape(0, 1);

	//载入完全不一样的图3
	Mat img_3 = imread("D:\\0003.jpg");
	//转化成灰度图
	Mat img_3gray;
	cvtColor(img_3, img_3gray, COLOR_BGR2GRAY);
	//resize
	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;


	//计算基准图与变化较小的图2的欧氏距离
	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;

	//计算基准图与完全不一样的图3的欧氏距离
	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=niai   (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=nibi   (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  (aiaverageA)  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  (biaverageB)  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=niai   (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=nibi   (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  (aiaverageA)  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  (biaverageB)  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<n1;++i)if(aiai+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 )=iaibi     (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矩阵展开成一维向量,求汉明距离。若汉明距离越小,意味着两个二值化矩阵之间的不同之处越少,意味着两张图片越相似。

结构特征的方法,由于关注图片中内容的轮廓,所以应该是不具有旋转、平移、缩放等不变性的。但对内容相似,颜色不同的图片,应该会有比较好的效果,比如下面两张图中的两只狗,颜色明显不同,从直方图的角度来说,图片相似性会比较低。但是由于图中内容都是“狗”,结构与轮廓会比较相似,从结构特征的角度来说,图片相似性会比较高,更符合实际的判定。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值