这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记。
感知哈希算法
使用哈希算法实现图片相似度的比较方法
均值哈希算法
步骤
- 缩小尺寸:将图像缩小到
Width x Height的尺寸,总共pixel_num个像素。 - 简化色彩:将缩小后的图片,转为64级灰度
- 计算平均值:计算所有
pixel_num个像素灰度平均值 - 比较像素灰度:将每个像素的灰度值,与平均值比较,大于或等于记为1,小于记为0
- 计算哈希值:将上一步的比较结果,组合在一起,就构成了一个
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
差值哈希算法
步骤
- 缩小尺寸
- 简化色彩
- 计算余弦相似
- 缩小余弦相似
- 计算哈希值
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,否则为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的工程基本都能分为三步:
- 得到向量库;
- 用faiss 构建index,并将向量添加到index中;
- 用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)
参考: