相似圖片搜索

一、顏色分佈法
每張圖片都可以生成顏色分佈的直方圖(color histogram)。如果兩張圖片的直方圖很接近,就可以認爲它們很相似。
這裏寫圖片描述
任何一種顏色都是由紅綠藍三原色(RGB)構成的,所以上圖共有4張直方圖(三原色直方圖 + 最後合成的直方圖)。
如果每種原色都可以取256個值,那麼整個顏色空間共有1600萬種顏色(256的三次方)。針對這1600萬種顏色比較直方圖,計算量實在太大了,因此需要採用簡化方法。可以將0~255分成四個區:0~63爲第0區,64~127爲第1區,128~191爲第2區,192~255爲第3區。這意味着紅綠藍分別有4個區,總共可以構成64種組合(4的3次方)。
任何一種顏色必然屬於這64種組合中的一種,這樣就可以統計每一種組合包含的像素數量。

這裏寫圖片描述

上圖是某張圖片的顏色分佈表,將表中最後一欄提取出來,組成一個64維向量(7414, 230, 0, 0, 8, …, 109, 0, 0, 3415, 53929)。這個向量就是這張圖片的特徵值或者叫”指紋”。
於是,尋找相似圖片就變成了找出與其最相似的向量。這可以用皮爾遜相關係數或者餘弦相似度算出。

二、內容特徵法
除了顏色構成,還可以從比較圖片內容的相似性入手。
首先,將原圖轉成一張較小的灰度圖片,假定爲50x50像素。然後,確定一個閾值,將灰度圖片轉成黑白圖片。
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
如果兩張圖片很相似,它們的黑白輪廓應該是相近的。於是,問題就變成了,第一步如何確定一個合理的閾值,正確呈現照片中的輪廓?
顯然,前景色與背景色反差越大,輪廓就越明顯。這意味着,如果我們找到一個值,可以使得前景色和背景色各自的”類內差異最小”(minimizing the intra-class variance),或者”類間差異最大”(maximizing the inter-class variance),那麼這個值就是理想的閾值。
1979年,日本學者大津展之證明了,”類內差異最小”與”類間差異最大”是同一件事,即對應同一個閾值。他提出一種簡單的算法,可以求出這個閾值,這被稱爲”大津法”(Otsu’s method)。下面就是他的計算方法。
假定一張圖片共有n個像素,其中灰度值小於閾值的像素爲 n1 個,大於等於閾值的像素爲 n2 個( n1 + n2 = n )。w1 和 w2 表示這兩種像素各自的比重。

  w1 = n1 / n
 w2 = n2 / n

再假定,所有灰度值小於閾值的像素的平均值和方差分別爲 μ1 和 σ1,所有灰度值大於等於閾值的像素的平均值和方差分別爲 μ2 和 σ2。於是,可以得到

   類內差異 = w1(σ1的平方) + w2(σ2的平方)
  類間差異 = w1w2(μ1-μ2)^2

可以證明,這兩個式子是等價的:得到”類內差異”的最小值,等同於得到”類間差異”的最大值。不過,從計算難度看,後者的計算要容易一些。
下一步用”窮舉法”,將閾值從灰度的最低值到最高值,依次取一遍,分別代入上面的算式。使得”類內差異最小”或”類間差異最大”的那個值,就是最終的閾值。

有了50x50像素的黑白縮略圖,就等於有了一個50x50的0-1矩陣。矩陣的每個值對應原圖的一個像素,0表示黑色,1表示白色。這個矩陣就是一張圖片的特徵矩陣。
兩個特徵矩陣的不同之處越少,就代表兩張圖片越相似。這可以用”異或運算”實現(即兩個值之中只有一個爲1,則運算結果爲1,否則運算結果爲0)。對不同圖片的特徵矩陣進行”異或運算”,結果中的1越少,就是越相似的圖片。

/ Calculate histogram
int ptr = 0;
while (ptr < srcData.length) {
   int h = 0xFF & srcData[ptr];
   histData[h] ++;
   ptr ++;
}

// Total number of pixels
int total = srcData.length;

float sum = 0;
for (int t=0 ; t<256 ; t++) sum += t * histData[t];

float sumB = 0;
int wB = 0;
int wF = 0;

float varMax = 0;
threshold = 0;

for (int t=0 ; t<256 ; t++) {
   wB += histData[t];               // Weight Background
   if (wB == 0) continue;

   wF = total - wB;                 // Weight Foreground
   if (wF == 0) break;

   sumB += (float) (t * histData[t]);

   float mB = sumB / wB;            // Mean Background
   float mF = (sum - sumB) / wF;    // Mean Foreground

   // Calculate Between Class Variance
   float varBetween = (float)wB * (float)wF * (mB - mF) * (mB - mF);

   // Check if new maximum found
   if (varBetween > varMax) {
      varMax = varBetween;
      threshold = t;
   }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章