背景

情头(情侣头像)一般指成双成对的头像,可以是真人照片,也可以是卡通人物等图片。衍生出去还有闺密头像和基友头像等等。

社交媒体上有一个非常令人费解的现象是,如果你去即刻、豆瓣、百度贴吧、微博,会发现有大量的人在上面贴了一个头像,然后寻找和它匹配的另一半头像,例如这样:

市面上的情侣头像大多是一些社区里的大佬自己制作出来的,然后发到社区里,再慢慢流传开来。这种传播方式导致了很多情头在传播时候早就被拆散了。很多人可能只找到了其中一个,但是想要找到和它配对的另一半。

还有一个问题是,当你看到一个头像时,没有人能够确定这个头像是否在制作时候就有另外一半。

一些程序员朋友最早看到这个问题时,总会天真地想去搜索引擎里识图一下就行了。但这里有两个非常有趣的问题:

第一个是目前搜索引擎的识别图片能力其实并不强,比如以 All in AI 著称的百度:

这还是在图片是原图的情况下,经常一些小朋友会在情头上自己二次创作,比如裁剪,比如压缩,比如贴上什么爱心。那样基本上识图就废了。

第二个问题更加有趣,情头的特点就是大家都在用,所以在最理想的情况下,即便搜索引擎能够识别出所有有这个头像的网站,出来的结果也并没有什么用处,无非是找到了也在用这个头像的别的网站的用户。

只有一种情况是真正能够帮助到寻找情侣头像这件事情的,那就是搜索引擎出来的结果里是专门搜集匹配好的情头的站点。那样姑且用户还能点进网页里去找到另外一半。

从上述阐述里不难发现,指望一个未成年小女生使用一系列高级互联网骚操作找一个头像是有多么不现实,何况技术上可行性还很低。

需求

讲清楚了上面这个事情,我们可以来看看如何通过技术手段来解决这个需求。

需求抽象

从上面的描述中我们可以抽象出一个简单的需求:

在数据库里存有大量成对的头像,输入一个头像图片(可能进行小范围PS处理的), 输出匹配到的对应情头。

可行性分析

数据库里存有大量成对头像

这个事情我们可以通过爬虫去爬取一些收集好了的头像站点以及去购买一些头像资源包实现。但是还有一个问题是,情侣头像是一个增量市场,每天都有源源不断新出来的情头,而且情头还有一个流行度的问题,有的时候一套情头会突然火起来,从而产生热点效应。关于这个,我们可以写几个简单的爬虫去监听各个社区的实时动态流,如果他们发的是两个配对好的情头,就加入到我们的数据库中,从而实现数据库的自我成长。

输入一个头像

我们需要给每个头像一个类似指纹的hash值,以此来定义一个头像,但普通 hash 的坏处是,当进行小范围修改后,hash 值就会完全不一样。所以我们期望图片的指纹 hash 值在数据进行小范围变化时,hash 值也只会小范围变化。有一个叫做 phash 的算法就能够实现这个过程,并且它能够容忍图片的放大和缩小,当图片像素出现变化时它的 phash 值也只会轻微变化,还可以通过计算前后变化图片的 phash 值的汉明距离,算出图片变化的差异度有多少。

输出匹配到的对应情头

当每个头像有了自己一个 phash 指纹以后,通过计算输入头像的 phash 值,找出和它 phash 值汉明距离最小的那个头像,当然 phash 值超出了一定大小基本上可以直接排除了,找不到说明数据库里还没收录。如果有的话,直接从数据库里拿到它对应的头像即可。

实现

当把需求拆解分析后,我们发现其实这个问题在技术上变得非常简单了。

  1. 数据库需要支持从 phash 值按汉明距离排序搜索
  2. 找到一个合适的 phash 汉明距离值,超过该距离就不能算同一张图片了

数据库上选择了 ElastiSearch , 并且写了一个 elasticsearch-hamming-plugin 来计算 phash 字段的汉明距离。

由于我一直找不到一个合适的汉明距离值,另外我必须确保自己找的结果一定是正确的,如果我告诉他找到了最后找的结果却是错的,给人的体验非常不好。所以我现在是只有百分百匹配到了 phash 值才会告诉他找到了,否则会推荐给他相似的头像,例如:

上面示例里我使用的图片的长宽比例是被我调整过的,所以数据库里不会有严格匹配到的头像,但是通过 phash 汉明距离能够轻易找到相似图片,所以能够在猜你喜欢里呈现出结果。这个是好的 case,在坏的 case 里,由于本来就是猜你喜欢所以就算结果不正确,也完全可以接受。事实上大部分时候都是正确的。

可以使用 https://qq.okjike.com/#/ 在线体验。

产品化

仅仅完成上面那个网站并不能使得这个工具被大规模使用,工程师的一个典型误区是认为大众都需要工具来提高效率。仅就我的一点观察,对效率的追求只会存在于少数时间不够用的人身上,而大部分人的问题在于每天要发愁如何打发时间。小朋友们在社区里发帖寻找头像的过程中,更多的是社交而非提问。一个工具只能够解决他们的即时性需求,而他们深层次的需求其实是想交友。

所以在产品化的时候,我把这个工具的呈现方式封装成了一个叫"情侣头像小助手"的用户,例如下面这种效果:

这个机器人已经运行了近一年的时间,我花了一个周末写完以后也没有时间去优化过它,目前它已经累积学习到了20万个情侣头像,积累了4669个粉丝。有无数人和她对话和说感谢。这听起来就非常赛博朋克。

最后

由于我本身也有其它工作要忙,加上本身它只是一个个人作品,而非产品决策。所以这个"机器人"只是作为一个彩蛋镶砌在社区里,并没有也不打算作为一个完整产品功能呈现。

之所以写下这个过程,是觉得实现过程还算是蛮有趣好玩的。之后在其上也发生过许多有趣的故事。

因为这个后来还诞生过一个想法,如果一个社区里拥有大量带有特定功能的机器人,人可以和人交流,也可以和机器人交互,那样的社区一定未来感十足吧。

祝大家与机器人相处愉快 ~