图像相似性搜索的原理

本文转自:

相似图片搜索的原理

相似图片搜索的原理(二)


http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html

http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html

http://blog.sina.com.cn/s/blog_b27f71160101gp9c.html

http://blog.csdn.net/luoweifu/article/details/8220992

http://blog.csdn.net/zmazon/article/details/8618775

http://blog.sina.com.cn/s/blog_b27f71160101gpep.html


计算机怎么知道两张图片相似呢?

根据Neal Krawetz博士的解释,原理非常简单易懂。我们可以用一个快速算法,就达到基本的效果。

这里的关键技术叫做"感知哈希算法"(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。

下面是一个最简单的实现:

一、平均哈希算法(aHash)


此算法是基于比较灰度图每个像素与平均值来实现的。

第一步,缩小尺寸。

将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。

 

第二步,简化色彩。

8*8的小图片转换成灰度图像,将64个像素的颜色(red,green,blue)转换成一种颜色(黑白灰度)。

第三步,计算平均值。

计算所有64个像素的灰度平均值。

第四步,比较像素的灰度。

将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。

第五步,计算哈希值。

将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。

 =  = 8f373714acfcf4d0

得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算"汉明距离"(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。


步骤说明:

1.缩放图片:为了保留结构去掉细节,去除大小、横纵比的差异,把图片统一缩放到8*8,共64个像素的图片。

2.转化为灰度图:把缩放后的图片转化为256阶的灰度图。

附上灰度图相关算法(R = red, G = green, B = blue)

 

1.浮点算法:Gray=R*0.3+G*0.59+B*0.11
2.整数方法:Gray=(R*30+G*59+B*11)/100
3.移位方法:Gray =(R*76+G*151+B*28)>>8;
4.平均值法:Gray=(R+G+B)/3;
5.仅取绿色:Gray=G;
      3.计算平均值: 计算进行灰度处理后图片的所有像素点的平均值。

 

4.比较像素灰度值:遍历灰度图片每一个像素,如果大于平均值记录为1,否则为0.

5.得到信息指纹:组合64个bit位,顺序随意保持一致性即可。

6.对比指纹:计算两幅图片的指纹,计算汉明距离(从一个指纹到另一个指纹需要变几次),汉明距离越大则说明图片越不一致,反之,汉明距离越小则说明图片越相似,当距离为0时,说明完全相同。(通常认为距离>10 就是两张完全不同的图片)


算法实现


关于均值哈希算法的实现,请参考:Google 以图搜图 - 相似图片搜索原理 - Java实现http://blog.csdn.net/luoweifu/article/details/7733030


下面给出matlab实现平均哈希算法(aHash)

%http://blog.sina.com.cn/s/blog_b27f71160101gp9c.html
%输入两幅图片,返回值为它们的为汉明距离。
%相似图片搜索原理:平均哈希算法
%对两幅图分别作如下处理:
%1:将两副256等级的灰度图像转化成8x8大小的64等级的灰度图像
%2:求全局灰度平均值
%3:逐次将灰度值与平均灰度值比较,大于等于的置为1,否则置为0
%4:将0、1序列看做8个字节(统一顺序)
%5:比较两幅图的数据位,如果不同的数据为不超过5位,则非常相似,若超过10为则认为两幅图无关
function v=tineyesearch_ahash(picture1,picture2)
t1=imresize(picture1,[8 8],'bicubic'); %图片放缩到固定大小
t2=imresize(picture2,[8 8],'bicubic'); %图片放缩到固定大小
t1=round(t1/4);
t2=round(t2/4);
mem1=round(sum(sum(t1))/64);
mem2=round(sum(sum(t2))/64);
for i=1:8for j=1:8if t1(i,j)>=mem1t1(i,j)=1;elset1(i,j)=0;endif t2(i,j)>=mem2t2(i,j)=1;elset2(i,j)=0;endend
end
h=abs(t1-t2);
v=sum(sum(h));

%http://blog.sina.com.cn/s/blog_b27f71160101gp9c.html
clear;
close all;
clc;
srcDir=uigetdir('Choose source directory.'); 
cd(srcDir);
allnames=struct2cell(dir('*.jpg'));
[k,len]=size(allnames); 
FinalResult = {};for i=1:lenname=allnames{1,i};
picture1 = imread(name);
picture2 = imread('006.jpg');
v=tineyesearch_ahash(picture1,picture2);
FinalResult{i,1} = char('006.jgp');
FinalResult{i,2} = name;
FinalResult{i,3} = v;
endfigure('NumberTitle', 'off', 'Name', '统计表');
columnname =   {'ReferenceImage','TestImage', 'SimilarScore'};     %各列的名称
% columnformat = {'numeric', 'bank', 'numeric'};  %各列的数据类型
columneditable =  [false true true];                %各列是否是可编辑的,true是可以编辑,false是不可编辑
t = uitable('Units','normalized','Position',...[0.1 0.1 0.9 0.9], 'Data', FinalResult,...'ColumnName', columnname,...'ColumnEditable', columneditable);





具体的代码实现,可以参见Wote用python语言写的imgHash.py。代码很短,只有53行。使用的时候,第一个参数是基准图片,第二个参数是用来比较的其他图片所在的目录,返回结果是两张图片之间不相同的数据位数量(汉明距离)。

这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。如果在图片上加几个文字,它就认不出来了。所以,它的最佳用途是根据缩略图,找出原图。

实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。这些算法虽然更复杂,但是原理与上面的简便算法是一样的,就是先将图片转化成Hash字符串,然后再进行比较。

如果图片放大或缩小,或改变纵横比,结果值也不会改变。增加或减少亮度或对比度,或改变颜色,对hash值都不会太大的影响。最大的优点:计算速度快!

如果你想比较两张图片,为每张图片构造hash值并且计算不同位的个数。(汉明距离)如果这个值为0,则表示这两张图片非常相似,如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。


二、效果更佳的感知哈希算法pHash


虽然均值哈希更简单且更快速,但是在比较上更死板、僵硬。它可能产生错误的漏洞,如果有一个伽马校正或颜色直方图被用于到图像。这是因为颜色沿着一个非线性标尺 改变其中“平均值”的位置,并因此改变哪些高于/低于平均值的比特数。

一个更健壮的算法叫pHash pHash的做法是将均值的方法发挥到极致。使用离散余弦变换(DCT)降低频率。

平均哈希算法过于严格,不够精确,更适合搜索缩略图,为了获得更精确的结果可以选择感知哈希算法,它采用的是DCT(离散余弦变换)来降低频率的方法


1.缩小尺寸

pHash以小图片开始,但图片大于8*832*32是最好的。这样做的目的是简化了DCT的计算,而不是减小频率。

2.简化色彩

将图片转化成灰度图像,进一步简化计算量。

3.计算DCT

DCT是把图片分解频率聚集和梯状形,虽然JPEG使用8*8DCT变换,在这里使用32*32DCT变换。

4.缩小DCT

虽然DCT的结果是32*32大小的矩阵,但我们只要保留左上角的8*8的矩阵,这部分呈现了图片中的最低频率。

5.计算平均值

如同均值哈希一样,计算DCT的均值,

6.进一步减小DCT

这是最主要的一步,根据8*8DCT矩阵,设置0164位的hash值,大于等于DCT均值的设为”1”,小于DCT均值的设为“0”。结果并不能告诉我们真实性的低频率,只能粗略地告诉我们相对于平均值频率的相对比例。只要图片的整体结构保持不变,hash结果值就不变。能够避免伽马校正或颜色直方图被调整带来的影响。

7.构造hash

64bit设置成64位的长整型,组合的次序并不重要,只要保证所有图片都采用同样次序就行了。将32*32DCT转换成32*32的图像。


与均值哈希一样,pHash同样可以用汉明距离来进行比较。(只需要比较每一位对应的位置并算计不同的位的个数)


步骤说明:

步骤:

1.缩小图片:32 * 32是一个较好的大小,这样方便DCT计算

2.转化为灰度图:把缩放后的图片转化为256阶的灰度图。(具体算法见平均哈希算法步骤)

3.计算DCT:DCT把图片分离成分率的集合

4.缩小DCT:DCT是32*32,保留左上角的8*8,这些代表的图片的最低频率

5.计算平均值:计算缩小DCT后的所有像素点的平均值。

6.进一步减小DCT:大于平均值记录为1,反之记录为0.

7.得到信息指纹:组合64个信息位,顺序随意保持一致性即可。

8.对比指纹:计算两幅图片的指纹,计算汉明距离(从一个指纹到另一个指纹需要变几次),汉明距离越大则说明图片越不一致,反之,汉明距离越小则说明图片越相似,当距离为0时,说明完全相同。(通常认为距离>10 就是两张完全不同的图片)


使用matlab实现 PHash 算法

clear;
close all;
clc;
% read image
I = imread('cameraman.tif');% cosine transform and reduction
d = dct2(I);
d = d(1:8,1:8);% compute average
a = mean(mean(d));% set bits, here unclear whether > or >= shall be used
b = d > a;
% maybe convert to string:
string = num2str(b(:)');


同类中的最佳算法?


自从我做了大量关于数码照片取证和巨幅图片的收集工作之后,我需要一种方法来搜索图片,所以,我用了一些不同的感知哈希算法做一个图片搜索工具,根据我并不很科学但长期使用的经验来看,我发现均值哈希比pHash显著地要快。如果你找一些明确的东西,均值Hash是一个极好的算法,例如,我有一张图片的小缩略图,并且我知道它的大图存在于一个容器的某个地方,均值哈希能算法快速地找到它。然而,如果图片有些修改,如过都添加了一些内容或头部叠加在一起,均值哈希就无法处理,虽然pHash比较慢,但它能很好地容忍一些小的变型(变型度小于25%的图片)

其次,如果,你运行的服务器像TinEye这样,你就可以不用每次都计算pHash值,我确信它们肯定之前就把pHash值保存在数据库中,核心的比较系统非常快,所以只需花费一次计算的时间,并且几秒之内能进行成千上百次的比较,非常有实用价值。


改进


有许多感知哈希算法的变形能改进它的识别率,例如,在减小尺寸之前可以被剪裁,通过这种方法,主体部分周围额外的空白区域不会产生不同。也可以对图片进行分割,例如,你有一个人脸识别算法,然后你需要计算每张脸的hash值,可以跟踪一般性的着色(例如,她的头发比蓝色或绿色更红,而背景比黑色更接近白色)或线的相对位置。

如果你能比较图片,那么你就可以做一些很酷的事情。例如, 你可以在GazoPa搜索引擎拖动图片,和TinEye一样,我并不知道GazoPa工作的细节,然而它似乎用的是感知哈希算法的变形,由于哈希把所有东西降低到最低频率,我三个人物线条画的素描可以和其它的图片进行比较——如匹配含有三个人的照片。


差异哈希算法dHash

相比pHash,dHash的速度要快的多,相比aHash,dHash在效率几乎相同的情况下的效果要更好,它是基于渐变实现的。

步骤:

1.缩小图片:收缩到9*8的大小,一遍它有72的像素点

2.转化为灰度图:把缩放后的图片转化为256阶的灰度图。(具体算法见平均哈希算法步骤)

3.计算差异值:dHash算法工作在相邻像素之间,这样每行9个像素之间产生了8个不同的差异,一共8行,则产生了64个差异值

4.获得指纹:如果左边的像素比右边的更亮,则记录为1,否则为0.


四、直方图相似度


 每张图片都可以生成其灰度图像直方图(histogram)。如果两张图片的直方图很接近,就可以认为它们很相似。

     因此,此处我们利用两幅图像的直方图来进行相似度的比较。原理较为简单,具体算法如下:
1、获得输入灰度图像的直方图分布;
2、将直方图划分为64个区,每个区为连续的4个灰度等级;
3、对每个区的4个值进行求和运算,得到1个数据,如此,会得到64个数据,即为该幅图像的一个向量(指纹);
4、根据步骤【1、2、3】,我们将输入的两幅图像转化为了2个向量,记为A、B;
5、计算两个向量的相似度,可以用皮尔逊相关系数或者余弦相似度计算,这里我们采用【余弦相似度】;
下面就顺便介绍一下余弦相似度的概念及用法:
      对于两个向量,我们可以把它们想象成空间中的两条线段,都是从原点([0, 0, ...])出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为0度,意味着方向相同、线段重合;如果夹角为90度,意味着形成直角,方向完全不相似;如果夹角为180度,意味着方向正好相反。因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。

                             数字图像处理算法及原理(四):相似图片搜索(直方图相似度)

以二维空间为例,上图的a和b是两个向量,我们要计算它们的夹角θ。余弦定理告诉我们,可以用下面的公式求得:

                  数字图像处理算法及原理(四):相似图片搜索(直方图相似度)

                         数字图像处理算法及原理(四):相似图片搜索(直方图相似度)

假定a向量是[x1, y1],b向量是[x2, y2],那么可以将余弦定理改写成下面的形式:

             数字图像处理算法及原理(四):相似图片搜索(直方图相似度)

            数字图像处理算法及原理(四):相似图片搜索(直方图相似度)

数学家已经证明,余弦的这种计算方法对n维向量也成立。假定A和B是两个n维向量,A是 [A1, A2, ..., An] ,B是 [B1, B2, ..., Bn] ,则A与B的夹角θ的余弦等于:

         数字图像处理算法及原理(四):相似图片搜索(直方图相似度)

使用这个公式,我们就可以得到,句子A与句子B的夹角的余弦。

数字图像处理算法及原理(四):相似图片搜索(直方图相似度)

余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"。

6、得到两个向量的夹角之后,我们就可以通过角度的大小来判别它们的相似程度。

7、至此,我们就完成了两幅图像的相似度计算,因此,可以通过此算法来寻找相似的图像。


下面给出matlab实现直方图分布相似度

%相似图像搜索:利用直方图分布相似度
%1:获得输入两幅图片的直方图分布
%2:将直方图依次划分为64个区,即每个区有4个灰度等级
%3:分别将各自的64个区生成64个元素,即一个向量(图像指纹)
%4:计算两个向量的余弦相似度
%5:判断,若相似度
function v=tineyesearch_hist(picture1,picture2)
t1=picture1;
[a1,b1]=size(t1);
t2=picture2;
t2=imresize(t2,[a1 b1],'bicubic');%缩放为一致大小
t1=round(t1);
t2=round(t2);
e1=zeros(1,256);
e2=zeros(1,256);
%获取直方图分布
for i=1:a1for j=1:b1n1=t1(i,j)+1;n2=t2(i,j)+1;e1(n1)=e1(n1)+1;e2(n2)=e2(n2)+1;end
end
figure;
imhist(uint8(t1));
figure;
imhist(uint8(t2));
%将直方图分为64个区
m1=zeros(1,64);
m2=zeros(1,64);
for i=0:63m1(1,i+1)=e1(4*i+1)+e1(4*i+2)+e1(4*i+3)+e1(4*i+4);m2(1,i+1)=e2(4*i+1)+e2(4*i+2)+e2(4*i+3)+e2(4*i+4);
end
%计算余弦相似度
A=sqrt(sum(sum(m1.^2)));
B=sqrt(sum(sum(m2.^2)));
C=sum(sum(m1.*m2));
cos1=C/(A*B);%计算余弦值
cos2=acos(cos1);%弧度
v=cos2*180/pi;%换算成角度
figure;
imshow(uint8([t1,t2]));
title(['余弦值为:',num2str(cos1),'       ','余弦夹角为:',num2str(v),'°']);


%http://blog.sina.com.cn/s/blog_b27f71160101gpep.html
clear;
close all;
clc;
srcDir=uigetdir('Choose source directory.'); 
cd(srcDir);
allnames=struct2cell(dir('*.jpg'));
[k,len]=size(allnames); 
FinalResult = {};for i=1:lenname=allnames{1,i};
picture1 = imread(name);
picture2 = imread('006.jpg');
v=tineyesearch_hist(picture1,picture2);
FinalResult{i,1} = char('006.jgp');
FinalResult{i,2} = name;
FinalResult{i,3} = v;
endfigure('NumberTitle', 'off', 'Name', '统计表');
columnname =   {'ReferenceImage','TestImage', 'SimilarScore'};     %各列的名称
% columnformat = {'numeric', 'bank', 'numeric'};  %各列的数据类型
columneditable =  [false true true];                %各列是否是可编辑的,true是可以编辑,false是不可编辑
t = uitable('Units','normalized','Position',...[0.1 0.1 0.9 0.9], 'Data', FinalResult,...'ColumnName', columnname,...'ColumnEditable', columneditable);


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246998.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

灰度共生矩阵(GLCM)并计算能量、熵、惯性矩、相关性(matlab)(待总结)

关于灰度共生矩阵的介绍可参考 http://blog.csdn.net/chuminnan2010/article/details/22035751 http://blog.csdn.net/xuezhisd/article/details/8908824 http://blog.csdn.net/xuexiang0704/article/details/8713204 http://cn.mathworks.com/help/images/ref/imlincomb.h…

机器学习之 weka学习(一)weka介绍,安装和配置环境变量

本部分详情可查看博客http://blog.csdn.net/u011067360/article/details/20844443 数据挖掘开源软件:WEKA基础教程 Weka简介: Weka是由新西兰怀卡托大学开发的智能分析系统(Waikato Environment for Knowledge Analysis) 。在怀…

第一章:线性空间和线性变换

转载于:https://www.cnblogs.com/invisible2/p/11514817.html

国外十大高校人工智能实验室及其代表性人物一览

本文转自: http://toutiao.com/a6293031494186107137/?tt_frommobile_qq&utm_campaignclient_share&appnews_article&utm_sourcemobile_qq&iid4325464459&utm_mediumtoutiao_android 高校建立的实验室与大公司有所不同,其研究项目…

java 基础安装和Tomcat8配置

初识 java,基础安装的说明。 下载 在oracle官网一般在同一个java版本会提供2个版本, 一个是Java SE Development Kit 7u80,此版本包含JDK开发环境版本; 另外一个是 Java SE Runtime Environment 7u80,此为只包含JR…

matlab内存溢出的解决方案

(1) 增加虚拟内存:cmd -> taskmgr 打开任务管理器,查看物理内存和虚拟内存,可观察matlab在运行过程中是否超过物理内存和虚拟内存。若超过,增加虚拟内存的方法是不可行的。物理内存不足的时候可以通过将…

c++MMMMM:oo

1.union,struct和class的区别 转载于:https://www.cnblogs.com/invisible2/p/11524465.html

matlab调用Java程序时出现 Java.lang.OutOfMemoryErrot: GC overhead limit exceeded

matlab调用Java程序时出现 java.lang.OutOfMemoryError: GC overhead limit exceeded JDK1.6.0_37和JDK_1.7.0_60版本,这2个版本中JVM默认启动的时候-XX:UseGCOverheadLimit,即启用了该特性。这其实是JVM的一种推断,如果垃圾回收耗费了98%的…

第94:受限玻尔兹曼机

转载于:https://www.cnblogs.com/invisible2/p/11565179.html

安装完Ubuntu桌面后要做的(待续)

1. 为了快速而顺畅的更新,打开终端并输入以下命令来让系统使用新软件库: $ sudo apt-get update 2. 更改系统外观和行为 如果你想要更改桌面背景或图标大小,依次打开System Settings –> Appearance –> Look,并对桌面进…

算法第二章上机实践报告

一、实践题目 改写二分搜索算法 二、问题描述 这道题目主要是考验同学们在熟练掌握二分搜索法的前提下,对二分搜索的结构和运用有一个更加深刻的掌握。首先是要了解二分搜索的结构,其次,要了解二分搜索中的分治方法每一个步骤的用意&#xff…

windows远程登录 ubuntu Linux 系统及互连共享桌面

预备工作 #开启防火墙端口 sudo ufw allow 3389#安装ssh sudo apt-get install openssh-server一、windows直连Ubuntu16.04共享桌面 1、打开终端,安装xrdp,vncserver sudo apt-get install xrdp vnc4server xbase-clients2、安装desktop sharing(Ubuntu…

RAID详解

一、raid什么意思? RAID是“Redundant Array of Independent Disk”的缩写,中文翻译过来通俗的讲就是磁盘阵列的意思,也就是说RAID就是把硬盘做成一个阵列,而阵列也就是把硬盘进行组合配置起来,做为一个整体进行管理&a…

装windows和Linux系统时找不到硬盘,pe安装系统没有出现磁盘,不能识别磁盘

装win7的时候,我们使用U盘装系统,找不到硬盘, 或者使用光盘装系统时 会出现 缺少所需的CD/DVD驱动器设备驱动程序 然后找遍整个硬盘/光盘也找不到合适的驱动,安装无法继续。 解决方法: ACHI模式下,PE里…

Linux 服务器上建立用户并分配权限

查看用户 whoami #要查看当前登录用户的用户名 who am i #表示打开当前伪终端的用户的用户名 who mom likes who 命令其它常用参数 参数 说明 -a 打印能打印的全部 -d 打印死掉的进程 -m 同am i,mom likes -q 打印当前登录用户数及用户名 -u 打印当前登录用户登录信…

LSTM

具体推导公式为: https://zybuluo.com/hanbingtao/note/581764 转载于:https://www.cnblogs.com/invisible2/p/11593270.html

查看windows显卡内存详细信息

方式一: 打开运行(WindowsR),输入DXDIAG,打开DirectX诊断工具 可以看到显卡是AMD Radeon HD 7000 series,但是没有具体显卡信息,这个型号是AMD在2013年主推的ATI的入门级独显系列,其…

从服务器上传和下载文件方法

1. ssh 安装SSH Secure Shell Client客户端 下载链接 http://download.csdn.net/detail/jiandanjinxin/9755684 使用方法参考主页 http://www.cnblogs.com/wxjnew/archive/2013/06/05/3118808.html http://www.cnblogs.com/pingzhanga/p/5126885.html 导致此问题的原因是…

Deep Learning运行所需的硬件配置(转)

A Full Hardware Guide to Deep Learning 2015-03-09 by Tim Dettmers 304 Comments 转自:http://timdettmers.com/2015/03/09/deep-learning-hardware-guide/ Deep Learning is very computationally intensive, so you will need a fast CPU with many cores, ri…

DBUtils

概述 DBUtils是Java编程中的数据库操作实用工具,小巧简单实用。 DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。 DBUtils三个核心功能介绍 QueryRunner中提供对sql语句操作的APIResultSetHandler接口,用于定义select操…