一、前言
目前很多公司都会在微信上进行运营,公司的系统是独立系统,有自己的账号体系,微信也可以登录自己的系统
如果自己的系统并没有是通过手机号注册的账号的情况下,用户使用微信上登录系统并购买,运营人员加用户微信进行销售和介绍
就需要一个方法将小程序的登录用户和运营好友列表进行对应,方便运营人员查看好友中哪些人买了哪些东西,在系统中的账号是谁。
但是,在拿到用户微信上的好友列表数据和小程序授权收拿到的用户数据之后,发现这两中渠道的数据无法对应谁是谁。
通过两种渠道获取的数据内容分别是:
(1)微信好友列表拿到的数据包括:头像、昵称、微信号、地区、性别
(2)小程序授权拿到的数据包括:头像、昵称、openId、地区、性别,手机号(需要二次授权获取)
微信没有能直接将两者联系上的唯一字段。
二、对应数据的办法
从前言的描述来看,已经无法通过唯一键来进行账号和微信之间的对应,所以需要别的办法
目前业界普遍的办法就是通过进行头像、昵称、地区、性别等进行数据对比,猜测人和人的对应关系
这里最重要的对比项就是头像的相似度对比,那么下面就进行头像的对比方法算法阐述。
三、算法简述
目前主流的比较图片相似度的算法总共有以下几种大类:关键点匹配, 直方图统计,哈希算法(Hash)等等,
关键点匹配等方法考虑到在java语言角度且依赖OpenCV等,该算法的适用面且此类算法过于复杂。
目前仅需要对小程序和微信好友进行头像对比相似度,那么直方图和Hash算法是比较简单、快速易用的算法。
且哈希算法可以对同一图片的不同尺寸有着比较良好的比较效果,故考虑使用哈希算法进行图片的相似度比较。
四、哈希算法的细分
图片相似度的几种哈希算法目的都是为了将图片生成一个“指纹”(使用0和1组成的64位字符串),用于区分每一张图片,
那么比较两个指纹的差异,也就是为了比较字符的距离,有三种方法:欧几里得距离,余弦相似度,汉明距离。其中汉明距离是最快最简单的距离计算方法。
相关资料可自行搜索:https://blog.csdn.net/akadiao/article/details/79767113
1.AHash(均值哈希算法)
平均哈希算法是三种Hash算法中最简单的一种,它通过下面几个步骤来获得图片的Hash值,这几个步骤分别是
(1) 缩放图片8*8;
(2) 转灰度图;
(3) 算像素均值;
(4)根据相似均值计算指纹。具体算法如下所示:
步骤 | 具体内容 |
---|---|
步骤 | 具体内容 |
缩放图片 | 输入图片大小尺寸各异,为了统一图片的输入,统一将图片尺寸缩放为8*8,一共得到了64个像素点。 |
转灰度图 | 输入图片有些为单通道灰度图,有些RGB三通道彩色图,有些为RGBA四通道彩色图。也为了统一下一步输入标准,将非单通道图片都转为单通道灰度图。 其中RGB三通道转单通道算法有下面几种: 1.浮点算法:Gray=R0.3+G0.59+B0.11 2.整数方法:Gray=(R30+G59+B11)/100 3.移位方法:Gray =(R76+G151+B*28)>>8; 4.平均值法:Gray=(R+G+B)/3; 5.仅取绿色:Gray=G; |
算像素均值 | 通过上一步可得一个8x8的整数矩阵G,计算这个矩阵中所有元素的平均值,假设其值为a |
据像素均值计算指纹 | 初始化输入图片的ahash = "" 从左到右一行一行地遍历矩阵G每一个像素如果第i行j列元素G(i,j) >= a,则ahash += "1"如果第i行j列元素G(i,j) <a, 则ahash += "0" |
2.PHash(感知哈希算法)
感知哈希算法是三种Hash算法中较为复杂的一种,它是基于DCT(离散余弦变换)来得到图片的hash值,其算法几个步骤分别是
(1) 缩放图片32*32;
(2) 转灰度图;
(3) 计算DCT;
(4) 缩小DCT;
(5) 算平均值;
(6) 计算指纹。具体算法如下所示:
步骤 | 具体内容 |
---|---|
缩放图片 | 统一将图片尺寸缩放为32*32,一共得到了1024个像素点。 |
转灰度图 | 统一下一步输入标准,将非单通道图片都转为单通道灰度图。 |
计算DCT | 计算32x32数据矩阵的离散余弦变换后对应的32x32数据矩阵 |
缩小DCT | 取上一步得到32x32数据矩阵左上角8x8子区域 |
算平均值 | 通过上一步可得一个8x8的整数矩阵G, 计算这个矩阵中所有元素的平均值,假设其值为a |
计算指纹 | 初始化输入图片的phash = "" 从左到右一行一行地遍历矩阵G每一个像素 如果第i行j列元素G(i,j) >= a,则phash += "1" 如果第i行j列元素G(i,j) <a, 则phash += "0" |
关于DCT变换的作用:https://blog.csdn.net/qq_29573053/article/details/78436325
3.DHash(差异哈希算法)
相比pHash,dHash的速度要快的多,相比aHash,dHash在效率几乎相同的情况下的效果要更好,它是基于渐变实现的。其算法几个步骤分别是
(1) 缩放图片9*8;
(2) 转灰度图;
(3) 算差异值;
(4) 计算指纹。具体算法如下所示
步骤 | 具体内容 |
---|---|
小图片 | 统一将图片尺寸缩放为9x8,一共得到了72个像素点 |
转灰度图 | 统一下一步输入标准,将非单通道图片都转为单通道灰度图。 |
算差异值 | 当前行像素值-前一行像素值, 从第二到第九行共8行,又因为矩阵有8列,所以得到一个8x8差分矩阵G |
计算指纹 | 初始化输入图片的dhash = "" 从左到右一行一行地遍历矩阵G每一个像素 如果第i行j列元素G(i,j) >= a,则dhash += "1" 如果第i行j列元素G(i,j) <a, 则dhash += "0" |
五、算法结果
在三种哈希的算法下,在联系人有134人与小程序的187个人进行匹配,仅仅依靠头像的相似且相似度阈值设置在0.92的情况下,其中有少数错误匹配人
(1)aHash成功匹配的人数在约111人
(2)dHash成功匹配的人数在约111人
(3)pHash成功匹配的人数在约115人
在不同相似度阈值下
(1)aHash设置0.95,成功约99人
(2)dHash设置0.92,成功约111人
(3)pHash设置0.90,成功约115个
三种方法各有优劣,选正确阈值即可相差无几
aHash相似度结果的变异系数是0.225599298
dHash相似度结果的变异系数是0.178757197
pHash相似度结果的变异系数是0.924572568
变异系数越大,数据的离散程度越高
四、应用在生产环境上的优化
小程序目前人数未知,但是好友列表接近60万人,故将一个人和60万人去匹配得到最优匹配同时考虑速度够快,需要考虑优化
1.全匹配的情况:
(1)在小程序和好友列表的人数据进库之前,将带头像的记录生成头像“指纹”,方便直接查询数据进行比较;
(2)考虑多线程,将60万人利用多线程分成多个部分进行比较,比如:10个线程,将60万拆成6万人一个线程执行;
(3)将小程序里的数据一次拿取多条,比如1000条,同时送入10个线程跟每个线程中的数据比较‘
(4)目的是为了查找最优匹配人,那么可以选择最优的10个,那么构建匹配率的大小为10的小根堆,每一个要匹配的人一个堆,小程序里有1000人,就1000个,考虑堆线程安全和线程共享的问题即可;
目前单机10线程情况下是1分左右执行一次60万匹配,那么1000人一次进行60万匹配也几乎1分钟
2.满足条件的匹配情况:
若将昵称,性别,地区最先进行完全匹配,那么挑选出来的数据将不会很多,应该不需要进行多线程进行头像匹配
从考虑生产环境不占用太多资源的考虑下,建议进行第二种匹配,没有匹配者可搭配页面进行手动选择