Google总裁Eric Schmidt表示,Google图片的建立,是由于他想要看"珍妮弗·洛佩兹的绿色范思哲礼服"。
2001年,Google图片编入了2.5亿张图片。2005年,这一数字增长到了十亿。到了2010年,已可搜索100亿张图片。截至2010年7月,该做事每天被访问超过一亿次。
感知哈希是利用指纹算法天生各种形式的多媒体的片段、哈希或指纹。

感知哈希是一种位置敏感哈希,如果多媒体的特色相似,则它是类似的。
这与加密哈希相反,加密哈希依赖于输入值的眇小变革造成输出值的巨大变革的雪崩效应。
感知散列函数广泛用于查找在线版权侵权案件以及数字取证,由于散列之间具有干系性,因此可以找到相似的数据(例如具有不同的水印)。
它是基于DCT(离散余弦变换)来得到图片的hash值,详细步骤如下:
1、缩小图片:3232是一个较好的大小,这样方便DCT打算;
2、灰度化:转换为256阶灰度图;
3、打算DCT:DCT把图片分离身分率的凑集,DCT(离散余弦变换);
4、缩小DCT:DCT打算后的矩阵是32 32,保留左上角的8 8,这些代表的图片的最低频率;
5、打算均匀值:打算缩小DCT后的所有像素点的均匀值;
6、比较均匀值:大于均匀值记录为1,反之记录为0,得到phash值。
02、发展历史Marr 和 Hildreth 1980 年的事情是该领域的首创性论文。
Christoph Zauner 2010 年 7 月的论文对该主题进行了精彩的先容。
2016 年 6 月,Azadeh Amir Asgari 揭橥了有关鲁棒图像哈希欺骗的研究成果。Asgari 指出,感知哈希函数像任何其他算法一样随意马虎出错。
研究职员在 2017 年 12 月表示,谷歌图像搜索基于感知哈希。
PHA是一类比较哈希方法的统称。图片所包含的特色被用来天生一组指纹(不过它不是唯一的),而这些指纹是可以进行比较的。
这个算法非常奥妙,无论你改变图片的高宽、亮度乃至颜色,都不会改变哈希值。
03、实现示意04、核心代码利用差分哈希算法打算图像的64位哈希 /// <summary> /// 利用差分哈希算法打算图像的64位哈希。 /// </summary> /// <param name="image">读取到的图片流</param> /// <returns>256位hash值</returns> public ulong[] DifferenceHash256(Image<Rgba32> image) { var pixels = _transformer.TransformImage(image, 17, 16); // 遍历像素,如果左侧像素比右侧像素亮,则将哈希设置为1。 var hash = new ulong[4]; var hashPos = 0; var hashPart = 0; for (var i = 0; i < 16; i++) { var rowStart = i 17; for (var j = 0; j < 16; j++) { if (pixels[rowStart + j] > pixels[rowStart + j + 1]) { hash[hashPart] |= 1UL << hashPos; } if (hashPos == 63) { hashPos = 0; hashPart++; } else { hashPos++; } } } return hash; }
构建索引
private async void btnIndex_Click(object sender, EventArgs e) { if (IndexRunning) { IndexRunning = false; btnIndex.Text = "更新索引"; return; } if (string.IsNullOrEmpty(txtDirectory.Text)) { MessageBox.Show("请先选择文件夹"); return; } IndexRunning = true; btnIndex.Text = "停滞索引"; cbRemoveInvalidIndex.Hide(); var imageHasher = new ImageHasher(new ImageSharpTransformer()); int? filesCount = null; Task.Run(() => filesCount = Directory.EnumerateFiles(txtDirectory.Text, "", SearchOption.AllDirectories).Except(_index.Keys).Count(s => Regex.IsMatch(s, "(jpg|png|bmp)$", RegexOptions.IgnoreCase))).ConfigureAwait(false); var local = new ThreadLocal<int>(true); await Task.Run(() => { var sw = Stopwatch.StartNew(); long size = 0; Directory.EnumerateFiles(txtDirectory.Text, "", SearchOption.AllDirectories).Except(_index.Keys).Where(s => Regex.IsMatch(s, "(jpg|png|bmp)$", RegexOptions.IgnoreCase)).Chunk(Environment.ProcessorCount 2).AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount 2).ForAll(g => { foreach (var s in g) { if (IndexRunning) { if (lblProcess.InvokeRequired) { local.Value++; lblProcess.Invoke(() => lblProcess.Text = $"{local.Values.Sum()}/{filesCount}"); } try { _index.GetOrAdd(s, _ => imageHasher.DifferenceHash256(s)); size += new FileInfo(s).Length; } catch { LogManager.Info(s + "格式禁绝确"); } } else { break; } } }); lbSpeed.Text = $"索引速率: {Math.Round(local.Values.Sum() 1.0 / sw.Elapsed.TotalSeconds)} items/s({size 1f / 1048576 / sw.Elapsed.TotalSeconds:N}MB/s)"; if (cbRemoveInvalidIndex.Checked) { foreach (var (key, _) in _index.AsParallel().WithDegreeOfParallelism(32).Where(s => !File.Exists(s.Key))) { _index.TryRemove(key, out _); } } lbIndexCount.Text = _index.Count + "文件"; cbRemoveInvalidIndex.Show(); var json = JsonSerializer.Serialize(_index); File.WriteAllText("index.json", json, Encoding.UTF8); MessageBox.Show("索引创建完成,耗时:" + sw.Elapsed.TotalSeconds + "s"); }).ConfigureAwait(false); IndexRunning = false; btnIndex.Text = "更新索引"; }
索引构造如下:
05、末了效果
匹配度100%:
匹配度74.21%: