海量数据下以图搜图实现方案


  • Global Moderator

    来源:极链科技

    作者:吴宏伟

    以图搜图原理

    0_1555899565974_2.jpg
    图文无关

    首先介绍一下以图搜图的实现原理,弄明白我们是怎样将一张图片转化为可以量化计算的哈希值。

    我们知道图片本身是二进制数据,是一系列像素值的集合,一张彩色图片可以用[h,w,3]的三维数组来表示,要直接比较两个三维数组的相似度,非常困难,我们要对这个数组进行简化,以便于我们计算。

    缩小尺寸
    通常图片的h和w在800-1200之间,我们需要缩小图片的尺寸,具体缩小到多大,需要根据具体情况而定,既不损失过多信息,也能减小计算量

    简化色彩
    一般而言,图片色彩对我们比较相似度来说,不会有影响,所以将三通道转化为单通道

    简化完之后,[h,w,3]的三维数组就变成h’*w’(h’和w’为缩小后的图片尺寸)个像素值,象素值取值范围为0-255之间的整数,继续简化成0/1

    计算所有像素平均值
    将每个像素的值与平均值进行比较,小于平均值标记为0,大于平均值标记为1
    转化完之后,我们就得到一个h’*w’位的二进制数值,这个值就是我们能够直接比较的哈希值,通过计算两个哈希值的汉明距离,就能计算出两张图片的相似度。

    在实际生产中,我们通过神经网络CNN,将一张图片转化为512位的哈希值,最大限度减少图片信息的损失。

    海量数据下的思考

    在了解完以图搜图原理之后,想要用到生产上,我们还需要解决很多其他问题,图片变成512位的二进制数值,怎样存储效率最高,图片本身还有一些描述信息,我们同样需要保存下来,这些描述信息可能会作为查询条件,生产环境图片总量在几千万的数量级,如何在这么大数据量的情况下,快速进行检索与哈希值的计算。

    在这里我们选取了Elasticsearch(以下简称ES)来作为存储数据库,主要原因有以下几点:

    能平行扩展
    ES集群扩展起来十分方便,随着数据量的不断增加,可以添加机器来满足存储查询的性能要求。

    支持自定义插件
    我们通过自定义插件,可以实现两个哈希值汉明距离的计算。

    快速检索
    ES检索速度非常快,能根据图片描述信息快速检索出我们需要的信息。

    实现

    我们将图片的512位二进制数值转为8个long类型数值存储到ES,每个long类型的存储空间为64bit,8*64刚好存下512位哈希值信息。

    ES插件实现方式如下:

    1、自定义我们自己的插件,继承Plugin然后实现接口ScriptPlugin

    public class HammingPlugin extends Plugin implements  ScriptPlugin {
    
        @Override
        public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
            return new MyScriptEngine();
        }
    }
    

    2、我们的HammingPlugin主要实现以下逻辑(读取参数,逐一与存储的特征值计算汉明距离,求和,计算相似度,把相似度作为score返回)

    public class HammingPlugin extends Plugin implements  ScriptPlugin {
    
        @Override
        public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
            return new MyScriptEngine();
        }
    }
    

    3、汉明距离计算函数:

    public class HammingPlugin extends Plugin implements  ScriptPlugin {
    
        @Override
        public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
            return new MyScriptEngine();
        }
    }
    

    4、查询结果展示

    public class HammingPlugin extends Plugin implements  ScriptPlugin {
    
        @Override
        public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
            return new MyScriptEngine();
        }
    }
    

    这个就是ES返回给我们的数据格式,took代表查询耗时247毫秒,shards里面信息表示我们总共有5个分片,total表示本次查询总共命中38700条数据,max_score表示最大得分(512/512=100%,其实就是我拿了其中一张图片的哈希值作为查询条件,相似度为100%),hits为json数组,每一条就是我们存储的一张图片的所有信息,_score就是该图片与我们给的查询条件的相似度得分。