搜索引擎中的以图搜图功能1 | 青训营笔记

730 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记。

感知哈希算法

使用哈希算法实现图片相似度的比较方法

均值哈希算法

步骤

  1. 缩小尺寸:将图像缩小到Width x Height的尺寸,总共pixel_num个像素。
  2. 简化色彩:将缩小后的图片,转为64级灰度
  3. 计算平均值:计算所有pixel_num个像素灰度平均值
  4. 比较像素灰度:将每个像素的灰度值,与平均值比较,大于或等于记为1,小于记为0
  5. 计算哈希值:将上一步的比较结果,组合在一起,就构成了一个pixel_num位的整数
def aHash(image):
    #缩放为8*8
    image=cv2.resize(image,(8,8),interpolation=cv2.INTER_CUBIC)
    #转换为灰度图
    image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    avreage = np.mean(image) 
    hash = [] 
    for i in range(image.shape[0]): 
        for j in range(image.shape[1]): 
            if image[i,j] > avreage: 
                hash.append(1) 
            else: 
                hash.append(0) 
    return hash

差值哈希算法

步骤

  1. 缩小尺寸
  2. 简化色彩
  3. 计算余弦相似
  4. 缩小余弦相似
  5. 计算哈希值
def dHash(image):
    #缩放9*8
    image=cv2.resize(image,(9,8),interpolation=cv2.INTER_CUBIC)
    #转换灰度图
    image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    #print(image.shape)
    hash=[]
    #每行前一个像素大于后一个像素为1,相反为0,生成哈希
    for i in range(8):
        for j in range(8):
            if image[i,j]>image[i,j+1]:
                hash.append(1)
            else:
                hash.append(0)
    return hash

感知哈希算法

步骤

  1. 缩小尺寸
  2. 简化色彩。
  3. 计算差异值
  4. 获得指纹:如果左边的像素比右边的更亮,则为1,否则为0
def pHash(image): 
    image = cv2.resize(image,(32,32), interpolation=cv2.INTER_CUBIC) 
    image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) 
    #cv2.imshow('image', image)
    #cv2.waitKey(0)
    #cv2.destroyAllWindows()
    # 将灰度图转为浮点型,再进行dct变换 
    dct = cv2.dct(np.float32(image))
    #print(dct)
    # 取左上角的8*8,这些代表图片的最低频率 
    # 这个操作等价于c++中利用opencv实现的掩码操作 
    # 在python中进行掩码操作,可以直接这样取出图像矩阵的某一部分 
    dct_roi = dct[0:8,0:8]  
    avreage = np.mean(dct_roi) 
    hash = [] 
    for i in range(dct_roi.shape[0]): 
        for j in range(dct_roi.shape[1]): 
            if dct_roi[i,j] > avreage: 
                hash.append(1) 
            else: 
                hash.append(0) 
    return hash

Faiss

简介

  • Faiss(Facebook AI Similarity Search)是Facebook AI团队开源的针对聚类和相似性搜索库,为稠密向量提供高效相似度搜索和聚类,支持十亿级别向量的搜索,是目前较成熟的近似近邻搜索库。
  • Faiss包含多种搜索任意大小向量集的算法,以及用于算法评估和参数调整的支持代码。

快速入门

Faiss检索相似向量TopK的工程基本都能分为三步:

  1. 得到向量库;
  2. 用faiss 构建index,并将向量添加到index中;
  3. 用faiss index 检索。

Faiss基础索引

  • Faiss之所以能加速,是因为它用的检索方式并非精确检索,而是模糊检索。用召回率来表示模糊检索相对于精确检索的损失。
  • 三种常用的索引:IndexFlatL2、IndexIVFFlat、IndexIVFPQ

Flat :暴力检索

  • 召回率最高
  • 速度慢,占内存大
dim, measure = 64, faiss.METRIC_L2
param =  'Flat'
index = faiss.index_factory(dim, param, measure)
index.is_trained                                   # 输出为True
index.add(xb)                                      # 向index中添加向量

IVFx Flat :倒排暴力检索

  • 利用倒排的思想
  • 可能损失精度,找到的是局部解,不是全局最优
  • 速度可能不太稳定
dim, measure = 64, faiss.METRIC_L2 
param =  'IVF100, Flat'                          # 代表k-means聚类中心为100,   
index = faiss.index_factory(dim, param, measure)
print(index.is_trained)                          # 此时输出为False,因为倒排索引需要训练k-means,
index.train(xb)                                  # 因此需要先训练index,再add向量
index.add(xb)                                     

PQx :乘积量化

  • 主要分为聚类和量化
  • 工业界大量使用此方法,各项指标都均可以接受
dim, measure = 64, faiss.METRIC_L2 
param =  'PQ16' 
index = faiss.index_factory(dim, param, measure)
print(index.is_trained)                          # 此时输出为False,因为倒排索引需要训练k-means,
index.train(xb)                                  # 因此需要先训练index,再add向量
index.add(xb)          

参考: