上一篇讲了四种哈希算法的概述和每种算法的基本步骤,这篇详细讨论下 DHash:
- 缩小尺寸,建议 9*8(col = row + 1)
- 灰度化
- 计算灰度差值(做差变为 8*8 的矩阵)
- 计算哈希值
缩小尺寸
这一步用 Android 的系统方法就简单了:
Bitmap bitmap = ThumbnailUtils.extractThumbnail(bitmap, destW, destH);
但为了能和服务端(Java)兼容,找到了一个纯数据结构的算法:
双线性插值法,参加:https://blog.csdn.net/u012679980/article/details/49449647。
修改了部分代码,计算效率有很大的提升。
通过上面的算法进行图片缩小,不管是 Android 端还是服务端,得到的结果都是相同的。
这里先说一下数组存放图片信息的方式:
- 二维:二维比较容易理解,图片是 x、y 两个方向的像素点组成,对应到一个二维数组即可。
- 一维:二维转一维,以一个方向为基准,一般是 x 方向,存完第一行存第二行,就将二维数组转化为一维了,比如 9*9 的二维数组,变为一维数组,第 10 个元素对应的是二维数组第二行的第一个元素。
- 三维:像素点是有三种颜色组成的,因此三维的 z 方向就是存储的 ARGB,透明度、红、绿、蓝。
上面代码的思路还是很好理解的:
假设 x 轴有 800 个像素点,但我们要缩小到 9 个,那个距离就是 100,即每隔 100 个像素点取出一个。取出的 9 个像素点就是桩子。y 方向同理。
但实际操作没有这么简单,会出现从 700 个像素点中取 9 个的现象,间隔不是整数,因此会通过取整后的目标像素点周围相邻的四个点,按权重来计算出取出的像素点的具体值。大家可以参见上面给出的博客地址具体看下思路。
其中第三步骤 convertToOneDim 中的 ABGR --> ARGB 也与文章不同,但不增加算法复杂度,只是将色值的表达方式转换成比较常用的形式。
灰度化
灰度化就是将彩色图片转化成 64 阶黑白图片,当然红绿蓝是按一定的系数缩小。
这个地方就体现到 缩小尺寸 里讲到的 ABGR --> ARGB 的好处了,通用方法的顺序都是按 R、G、B 排列的。
计算灰度差值
这一步是 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 获取源码。