差值哈希(DHash)

上一篇讲了四种哈希算法的概述和每种算法的基本步骤,这篇详细讨论下 DHash:

  1. 缩小尺寸,建议 9*8(col = row + 1)
  2. 灰度化
  3. 计算灰度差值(做差变为 8*8 的矩阵)
  4. 计算哈希值

缩小尺寸

这一步用 Android 的系统方法就简单了:

Bitmap bitmap = ThumbnailUtils.extractThumbnail(bitmap, destW, destH);

但为了能和服务端(Java)兼容,找到了一个纯数据结构的算法:

双线性插值法,参加:https://blog.csdn.net/u012679980/article/details/49449647。

修改了部分代码,计算效率有很大的提升。

差值哈希(DHash)_第1张图片
差值哈希(DHash)_第2张图片
差值哈希(DHash)_第3张图片
通过上面的算法进行图片缩小,不管是 Android 端还是服务端,得到的结果都是相同的。

这里先说一下数组存放图片信息的方式:

  1. 二维:二维比较容易理解,图片是 x、y 两个方向的像素点组成,对应到一个二维数组即可。
  2. 一维:二维转一维,以一个方向为基准,一般是 x 方向,存完第一行存第二行,就将二维数组转化为一维了,比如 9*9 的二维数组,变为一维数组,第 10 个元素对应的是二维数组第二行的第一个元素。
  3. 三维:像素点是有三种颜色组成的,因此三维的 z 方向就是存储的 ARGB,透明度、红、绿、蓝。

上面代码的思路还是很好理解的:

假设 x 轴有 800 个像素点,但我们要缩小到 9 个,那个距离就是 100,即每隔 100 个像素点取出一个。取出的 9 个像素点就是桩子。y 方向同理。

但实际操作没有这么简单,会出现从 700 个像素点中取 9 个的现象,间隔不是整数,因此会通过取整后的目标像素点周围相邻的四个点,按权重来计算出取出的像素点的具体值。大家可以参见上面给出的博客地址具体看下思路。

其中第三步骤 convertToOneDim 中的 ABGR --> ARGB 也与文章不同,但不增加算法复杂度,只是将色值的表达方式转换成比较常用的形式。

灰度化

差值哈希(DHash)_第4张图片
灰度化就是将彩色图片转化成 64 阶黑白图片,当然红绿蓝是按一定的系数缩小。

这个地方就体现到 缩小尺寸 里讲到的 ABGR --> ARGB 的好处了,通用方法的顺序都是按 R、G、B 排列的。

计算灰度差值

差值哈希(DHash)_第5张图片
这一步是 DHash 的核心步骤,所有 hash 算法一般最终都是采样 64 个点,但 DHash 之所以采样 9*8 个像素点是基于其比较差值的逻辑。

DHash 是将每一行后一个元素和前一个元素做差,差值大于等于 0 则记 1,小于 0 则记 0。

假设 9*8 的数组是:a1 ~ a9、b1 ~ b9、c1 ~ c9 ······ h1 ~ h9

即 a2-a1 记录,a3-a2 记录,a4-a3 记录 ······ a9-a8 记录;b、c ······ 以此类推,一共记录 64 个01 值。

得到的这个 64 位的二进制值就是这张图片的指纹。

计算哈希值

将这个 64 位的二进制值转为 16 位的十六进制值就是这张图片的 DHash 值。

本文原创发布于公众号 习习立 ,关注公众号回复 hash 获取源码。

你可能感兴趣的:(图片哈希,Android)