轉自:http://www.coding123.net/article/20120822/csharp-google-similar-image-search-algorithm.aspx
試試效果:
原理講解
參考Neal Krawetz博士的這篇文章, 實 現這種功能的關鍵技術叫做"感知雜湊演算法"(Perceptual Hash Algorithm), 意思是為圖片產生一個指紋(字串格式), 兩張 圖片的指紋越相似, 說明兩張圖片就越相似. 但關鍵是如何根據圖片計算出"指紋"呢? 下面用最簡單的步驟來說明一下原理:
第一步 縮小圖片尺寸
將圖片縮小到8x8的尺寸, 總共64個像素. 這一步的作用是去除各種圖片尺寸和圖片比例的差異, 只保留結構、明暗等基本資料.
第二步 轉為灰階圖片
將縮小後的圖片, 轉為64級灰階圖片.
第三步 計算灰階平均值
計算圖片中所有像素的灰階平均值
第四步 比較像素的灰階
將每個像素的灰階與平均值進行比較, 如果大於或等於平均值記為1, 小於平均值記為0.
第五步 計算雜湊值
將上一步的比較結果, 組合在一起, 就構成了一個64位的二進位整數, 這就是這張圖片的指紋.
第六步 對比圖片指紋
得到圖片的指紋後, 就可以對比不同的圖片的指紋, 計算出64位中有多少位是不一樣的. 如果不相同的資料位元數不超過5, 就說明兩張圖片很相似, 如果大於10, 說明它們是兩張不同的圖片.
代碼實現 (C#版本)
下面我用C#代碼根據上一節所闡述的步驟實現一下.
-收縮
C#代碼using System;
using System.IO;
using System.Drawing;
namespace SimilarPhoto
{
class SimilarPhoto
{
Image SourceImg;
public SimilarPhoto(string filePath)
{
SourceImg = Image.FromFile(filePath);
}
public SimilarPhoto(Stream stream)
{
SourceImg = Image.FromStream(stream);
}
public String GetHash()
{
Image image = ReduceSize();
Byte[] grayValues = ReduceColor(image);
Byte average = CalcAverage(grayValues);
String reslut = ComputeBits(grayValues, average);
return reslut;
}
// Step 1 : Reduce size to 8*8
private Image ReduceSize(int width = 8,int height = 8)
{
Image image = SourceImg.GetThumbnailImage(width, height, () => {
return false; }, IntPtr.Zero);
return image;
}
// Step 2 : Reduce Color
private Byte[] ReduceColor(Image image)
{
Bitmap bitMap = new Bitmap(image);
Byte[] grayValues = new Byte[image.Width * image.Height];
for(int x = 0; x<image.Width; x++)
for (int y = 0; y < image.Height; y++)
{
Color color = bitMap.GetPixel(x, y);
byte grayValue = (byte)((color.R * 30 + color.G * 59 + color.B * 11) / 100);
grayValues[x * image.Width + y] = grayValue;
}
return grayValues;
}
// Step 3 : Average the colors
private Byte CalcAverage(byte[] values)
{
int sum = 0;
for (int i = 0; i < values.Length; i++)
sum += (int)values[i];
return Convert.ToByte(sum / values.Length);
}
// Step 4 : Compute the bits
private String ComputeBits(byte[] values,byte averageValue)
{
char[] result = new
char[values.Length];
for (int i = 0; i < values.Length; i++)
{
if (values[i] < averageValue)
result[i] = '0';
else
result[i] = '1';
}
return new String(result);
}
// Compare hash
public static Int32 CalcSimilarDegree(string a,string b)
{
if (a.Length != b.Length)
throw new ArgumentException();
int count = 0;
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i])
count++;
}
return count;
}
}
}
Google伺服器裡的圖片數量是百億層級的, 我電腦裡的圖片數量當然沒法比, 但以前做過爬蟲程式, 電腦裡有40,000多人的頭像照片, 就拿它們作為對比結果吧! 我計算出這些圖片的"指紋", 放在一個txt文本中, 格式如下.
用ASP.NET寫一個簡單的頁面, 允許使用者上傳一張圖片, 後台計算出該圖片的指紋, 並與txt文本中各圖片的指紋對比, 整理出結果顯示在頁面中, 效果如下:
來源:http://www.cnblogs.com/technology/archive/2012/07/12/Perceptual-Hash-Algorithm.html