在谷歌百度以图搜索中, 用户可以上传一张图片, 谷歌百度显示因特网中与此图片相同或者相似的图片.
第一步:将图片缩小到8×8的尺寸,总共64个像素。这一步的作用是去除图片的细节, 只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
第二步:将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
第三步:计算所有64个像素的灰度平均值。
第四步:将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
第五步:将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序。
第六步:计算”汉明距离”(Hamming distance),如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace GlobalImageUpload
{
internal class ImageSimilarity
{
/// <summary>
/// 读取图片
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
private Image readImage(String filename)
{
try
{
Image sourceImage = null;
sourceImage=Image.FromFile(filename);
return sourceImage;
}
catch
{
}
return null;
}
/// <summary>
/// 灰度值计算
/// </summary>
/// <param name="posClr"></param>
/// <returns></returns>
private int rgbToGray(Color posClr)
{
return (posClr.R + posClr.G + posClr.B + posClr.A) / 4;
//return (posClr.R * 19595 + posClr.G * 38469 + posClr.B * 7472) >> 16;
}
/// <summary>
/// 计算平均值
/// </summary>
/// <param name="pixels"></param>
/// <returns></returns>
private 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;
}
/// <summary>
/// 计算汉明距离
/// </summary>
/// <param name="sourceHashCode">源hashCode</param>
/// <param name="hashCode">与之比较的hashCode</param>
/// <returns></returns>
public int hammingDistance(string sourceHashCode, string hashCode)
{
int difference = 0;
int len = sourceHashCode.Length;
for (int i = 0; i < len; i++)
{
if (sourceHashCode.ToCharArray()[i] != hashCode.ToCharArray()[i])
{
difference++;
}
}
return difference;
}
public string produceFinger(string filename)
{
string result = " ";
System.Drawing.Image source = null;
try
{
source = readImage(filename);
result = produceFinger(source);
}
catch(Exception ex)
{
}
finally
{
if (source != null)
{
source.Dispose();
}
}
return result;
}
/// <summary>
/// 形成图像指纹
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public string produceFinger(System.Drawing.Image file)
{
System.Drawing.Image source = (System.Drawing.Image)file.Clone();
// 读取文件
int width = 8;
int height = 8;
// 第一步,缩小尺寸。
// 将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
Bitmap b = new Bitmap(source, 8, 8);
System.Drawing.Image thumb = b;
// 第二步,简化色彩。
// 将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
int[] pixels = new int[width * height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
pixels[i * height + j] = rgbToGray(((Bitmap)thumb).GetPixel(i, j));
}
}
// 第三步,计算平均值。
// 计算所有64个像素的灰度平均值。
int avgPixel = average(pixels);
// 第四步,比较像素的灰度。
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;
}
}
// 第五步,计算哈希值。
string hashCode = "";
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 += (binaryToHex(result));
}
if (source != null)
{
source.Dispose();
}
return hashCode;
}
/// <summary>
/// 返回对应编码
/// </summary>
/// <param name="binary"></param>
/// <returns></returns>
private string binaryToHex(int binary)
{
string[] bintoHex = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"};
if (binary >= 0 && binary <= 15)
{
return bintoHex[binary];
}
return " ";
}
}
}
调用方法如下:
static void Main(string[] args)
{
GlobalImageUpload.ImageSimilarity similarity = new GlobalImageUpload.ImageSimilarity();
string hash1 = similarity.produceFinger(@"H:\1.jpg");
string hash2 = similarity.produceFinger(@"H:\2.jpg");
int diff=similarity.hammingDistance(hash1,hash2);
if (diff < 5) { Console.WriteLine("两张图很相似"); } if (diff >= 5 && diff <= 10) { Console.WriteLine("两张图有点相似"); } if (diff >10)
{
Console.WriteLine("两张图不相同");
}
Console.Read();
}
转载请注明:清风亦平凡 » 谷歌百度以图搜图感知哈希算法之C#简单实现