谷歌百度以圖搜圖 "感知雜湊演算法" C#簡單實現
阿新 • • 發佈:2019-01-24
/// <summary> /// 感知雜湊演算法 /// </summary> public class ImageComparer { /// <summary> /// 獲取圖片的Hashcode /// </summary> /// <param name="imageName"></param> /// <returns></returns> public static string GetImageHashCode(string imageName) { int width = 8; int height = 8; // 第一步 // 將圖片縮小到8x8的尺寸,總共64個畫素。這一步的作用是去除圖片的細節, // 只保留結構、明暗等基本資訊,摒棄不同尺寸、比例帶來的圖片差異。 Bitmap bmp = new Bitmap(Thumb(imageName)); int[] pixels = new int[width * height]; // 第二步 // 將縮小後的圖片,轉為64級灰度。也就是說,所有畫素點總共只有64種顏色。 for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { Color color = bmp.GetPixel(i, j); pixels[i * height + j] = RGBToGray(color.ToArgb()); } } // 第三步 // 計算所有64個畫素的灰度平均值。 int avgPixel = Average(pixels); // 第四步 // 將每個畫素的灰度,與平均值進行比較。大於或等於平均值,記為1;小於平均值,記為0。 int[] comps = new int[width * height]; for (int i = 0; i < comps.Length; i++) { if (pixels[i] >= avgPixel) { comps[i] = 1; } else { comps[i] = 0; } } // 第五步 // 將上一步的比較結果,組合在一起,就構成了一個64位的整數,這就是這張圖片的指紋。組合的次序並不重要,只要保證所有圖片都採用同樣次序就行了。 StringBuilder hashCode = new StringBuilder(); for (int i = 0; i < comps.Length; i += 4) { int result = comps[i] * (int)Math.Pow(2, 3) + comps[i + 1] * (int)Math.Pow(2, 2) + comps[i + 2] * (int)Math.Pow(2, 1) + comps[i + 2]; hashCode.Append(BinaryToHex(result)); } bmp.Dispose(); return hashCode.ToString(); } /// <summary> /// 計算"漢明距離"(Hamming distance)。 /// 如果不相同的資料位不超過5,就說明兩張圖片很相似;如果大於10,就說明這是兩張不同的圖片。 /// </summary> /// <param name="sourceHashCode"></param> /// <param name="hashCode"></param> /// <returns></returns> public static int HammingDistance(String sourceHashCode, String hashCode) { int difference = 0; int len = sourceHashCode.Length; for (int i = 0; i < len; i++) { if (sourceHashCode[i] != hashCode[i]) { difference++; } } return difference; } /// <summary> /// 縮放圖片 /// </summary> /// <param name="imageName"></param> /// <returns></returns> private static Image Thumb(string imageName) { return Image.FromFile(imageName).GetThumbnailImage(8, 8, () => { return false; }, IntPtr.Zero); } /// <summary> /// 轉為64級灰度 /// </summary> /// <param name="pixels"></param> /// <returns></returns> private static int RGBToGray(int pixels) { int _red = (pixels >> 16) & 0xFF; int _green = (pixels >> 8) & 0xFF; int _blue = (pixels) & 0xFF; return (int)(0.3 * _red + 0.59 * _green + 0.11 * _blue); } /// <summary> /// 計算平均值 /// </summary> /// <param name="pixels"></param> /// <returns></returns> private static int Average(int[] pixels) { float m = 0; for (int i = 0; i < pixels.Length; ++i) { m += pixels[i]; } m = m / pixels.Length; return (int)m; } private static char BinaryToHex(int binary) { char ch = ' '; switch (binary) { case 0: ch = '0'; break; case 1: ch = '1'; break; case 2: ch = '2'; break; case 3: ch = '3'; break; case 4: ch = '4'; break; case 5: ch = '5'; break; case 6: ch = '6'; break; case 7: ch = '7'; break; case 8: ch = '8'; break; case 9: ch = '9'; break; case 10: ch = 'a'; break; case 11: ch = 'b'; break; case 12: ch = 'c'; break; case 13: ch = 'd'; break; case 14: ch = 'e'; break; case 15: ch = 'f'; break; default: ch = ' '; break; } return ch; } }