图像相似性搜索的原理
本文轉自:
相似圖片搜索的原理
相似圖片搜索的原理(二)
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
%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*8,32*32是最好的。這樣做的目的是簡化了DCT的計算,而不是減小頻率。
2.簡化色彩
將圖片轉化成灰度圖像,進一步簡化計算量。
3.計算DCT
DCT是把圖片分解頻率聚集和梯狀形,雖然JPEG使用8*8的DCT變換,在這里使用32*32的DCT變換。
4.縮小DCT
雖然DCT的結果是32*32大小的矩陣,但我們只要保留左上角的8*8的矩陣,這部分呈現了圖片中的最低頻率。
5.計算平均值
如同均值哈希一樣,計算DCT的均值,
6.進一步減小DCT
這是最主要的一步,根據8*8的DCT矩陣,設置0或1的64位的hash值,大于等于DCT均值的設為”1”,小于DCT均值的設為“0”。結果并不能告訴我們真實性的低頻率,只能粗略地告訴我們相對于平均值頻率的相對比例。只要圖片的整體結構保持不變,hash結果值就不變。能夠避免伽馬校正或顏色直方圖被調整帶來的影響。
7.構造hash值
將64bit設置成64位的長整型,組合的次序并不重要,只要保證所有圖片都采用同樣次序就行了。將32*32的DCT轉換成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),'°']);
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的图像相似性搜索的原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用CSS3动画,让页面动起来
- 下一篇: 文件重命名批量处理(Matlab)