【OpenCV】基于BoW词袋模型/HoG+SIFT特征提取的图像检索系统

2024-02-22 01:10

本文主要是介绍【OpenCV】基于BoW词袋模型/HoG+SIFT特征提取的图像检索系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

功能介绍

  • 图片检索结果 Image Retrieval
  • 精确度和召回率计算
  • 调试数据记录(处理效率、特征值、相似度等)

总体思路

  • 输入路径
  • 预处理样本库、提取特征值(HoG/SIFT/LBP/Haar-like)
  • 读取待检索图片、提取特征值
  • 计算TF/IDF词频(Term Frequency/Inverse Document Frequency)
  • 遍历样本库、计算相似度
  • 排序、取最相似的前topN个结果
  • 重排序(Re-ranking)
  • 扩展查询(Query Expansion)
  • 输出结果

伪代码

% TF Term Frequency
% IDF Inverse Document Frequency%读取图像->提取特征(HOG)->编码(kmeans)->得到特征向量
->欧式距离匹配计算->筛选结果->计算IDF->Re-rank->评价实现->QEqueryImageAddr = 'D:/vehicle.jpg';
DataSetAddr = 'D:/Data/';% image parameters
imgHeight = 500;
imgWidth = 500;% feature parameters
featType = 'HOG' % SIFT, HOG, LBP, Haar-like
cellSize = 3 % each cell contains cellSize * cellSize pixels% Embedding parameters
clusterAlgo = 'k-means';  % k-means, graph-based, GMM
clusterNum = 5;% retrieval parameters
topN = 10; % number of image to be return% get words
for imgIndex = 1 : length(DataSetImgNames)datasetImg = imgRead(DataSetImgNames[imgIndex]);datasetImg = ImgResize(datasetImg, [imgHeight imgWidth]);datasetImgFeat[imgIndex] = FeatureExtraction(datasetImg);
endwords = cluster(datasetImgFeat); % K-means% get query Embedding
queryImg = imgRead(queryImageAddr);
queryImg = ImgResize(queryImg, [imgHeight imgWidth]); %resize image to fixed size
queryImgFeat = FeatureExtraction(queryImg);  % SIFT, HOG, LBP, Haar-like
queryEmbedding = Encoding(queryImgFeat, words);
queryf = TF_IDFEncoding(queryEmbedding);% get TF and IDF
tfs = CalcuTF(queryEmbedding);  % h_j / sum_i h_i
idfs = CalcuIDF(datasetEmbedding);  % log(N/f_j)
queryTF_IDFEmbedding = tfs * idfs;% get dataset Embedding
DataSetImgNames = imgNameRead(DataSetAddr);for imgIndex = 1 : length(DataSetImgNames)  % each image in the DataSetdataEmbedding[imgIndex] = Encoding(datasetImgFeat, words);tfs = CalcuTF(dataEmbedding[imgIndex]);dataTF_IDFEmbedding = tfs * idfs;dist[imgIndex] = EuclideanDist(queryTF_IDFEmbedding, dataTF_IDFEmbedding);  % sim[imgIndex] = CosSim(queryImg, datasetImg)
endrank = Recorder(dist); % rank the order in dataset according to the Euclidean distance or cosine similirity[recall, precision] = Measure(rank, topN);  % way to calculate recall and precision

重点解析

Bag of Words 词袋模型

  • 概念很容易理解,BoW模型即对于训练集中的每一幅图像提取其局部特征,然后将所有图像所提取出来的局部特征点作为一个整体进行聚类;

  • BoW/BoF的关键思路在于提取一张图片的特征作为视觉单词/视觉特征(Words/Feature),然后一个个单词聚类/编码成视觉词典/码本(Vocabulary/Codebook);
    1

  • 除BoW模型之外还有如FV(Fisher Vector),著名的VLAD(Vector of locally aggregated descriptors);

  • 这篇文章对该模型做了很详细的解释 袁勇:BoW图像检索原理与实战

Kmeans 聚类

  • 那么提取到多张图片的多组单词之后怎么办呢?接下来就得开始进行聚类;
  • 理解了kmeans的算法原理,也就理解BoW模型是怎样实现的了;
  • Kmeans本质是一种迭代算法,即对于 n 个待处理的特征点,先根据给定的簇的个数 k,初始化 k 个簇中心点,然后对于其他的每一个点,计算其到各个中心点的距离,并将其归入距离自己最近的中心点所在的簇中,如此重复这样一个过程;
选择K个点作为初始质心 
repeat 将每个点指派到最近的质心,形成K个簇 重新计算每个簇的质心 
until 簇不发生变化或达到最大迭代次数
  • 最后我们用到的是一个K维直方图,即统计单词表中每个单词在图像中出现的次数,从而将图像表示成为一个K维数值向量(这里的K是自己取的);
    2

  • 实际上BoW与FV、VLAD相比效率较低,内存占用比较高,用离特征点最近的聚类中心去代替该特征点,这个过程中损失了较多的信息。

HoG 形状特征提取

  • HoG全称是Histogram of Oriented Gradient,即方向梯度直方图;

  • 知道全程之后,原理也非常好理解,即计算和统计图像局部区域的梯度方向直方图;
    3

  • 首先将图像分成大区域block,每个block又分成小区域cell,然后采集cell中各像素点梯度或边缘方向直方图,最后把这些直方图组合起来构成特征描述;

SIFT 纹理特征提取

  • 与HoG不同的是,HoG得出的是一个大的多维特征向量,这是我们计算相似度所乐意得到的,而SIFT提取的则是一张图片中的大量特征点,而不同的图片提取出的特征点数量是不同的,所以有效的聚类显得很重要,这也是为什么SIFT往往和kmeans一起出没;
1.构建尺度空间
2.构造高斯差分尺度空间
3.DoG尺度空间极值点检测
4.特征点精确定位
5.去除不稳定点

4

  • SIFT算法的原理更为复杂,我们这里可以直接调用OpenCV库提供的SIFT方法detectAndCompute(),有关cv调用失败的版权问题可以参考我的这篇博客 2020年关于SIFT/SURF专利版权问题的解决办法

倒排索引&词频 TF-IDF

  • TF即Term Frequency,IDF即Inverse Document Frequency;
    TF
    IDF

  • 在文本检索中,TF-IDF技术常用以评估词语对于一个文件数据库中的其中一份文件的重要程度。词语的重要性随着它在文件中出现的频率成正比增加,但同时会随着它在文件数据库中出现的频率成反比下降。

  • 引入这两个概念是为了用IDF对BoW模型得出的结果进行加权处理,减少其他不重要的视觉单词所占的权重,其中h为得到的一张图片的直方图向量,IDF公式即1

相似度计算

  • 一般方法有两种,一种是直接计算欧式距离,一种是计算余弦相似度;
  • 直接上代码:
# 欧式距离
def EuclideanDist(A, B, length, vecNum):result = 0for i in range(vecNum):sum = 0for index in range(length):sum += np.power((A[i][index]-B[i][index]),2)sum = np.power(sum, 1/2)result += sum / vecNumreturn result
# 余弦相似度
def CosinDist(A, B)A = np.mat(A)B = np.mat(B)num = float(A * B.T)denom = np.linalg.norm(A) * np.linalg.norm(B)cos = num / denomsim = 0.5 + 0.5 * cosreturn sim

Re-rank 重排序

  • 将初始检索得到的图像进行分析和管理,即第二次排序;
    12

  • 常用的手段有两种,一种是基于内容的重排序(如上图),一种是基于文本的重排序,前者准确率较高但效率较低,后者则相反;

  • 我这里用了两种最简单的重排序方法,分别是:

  • 基于线性组合:先利用某一个特征得到排序效果,对多次排序效果进行线性融合,然后按照降序排列得到最终的排序结果。
    12

  • 基于聚类:对检索的图像结果进行聚类,首先对图像集群进行排序,然后在集群内部进行排序,为了得到最准确的相关聚类,将图像检索结果中最相关的集群图像聚在一起。
    32

Query Expansion 拓展查询

  • 对返回的前topN个结果,包括查询样本本身,对它们的特征求和取平均,再做一次查询,此过程称为拓展查询。很好理解也很容易实现。
  • 通过Query Expansion,以达到提高检索召回率的目的。

RootSIFT 概念

  • 一开始也是在袁勇大神的文章里接触到这个词,好像网上对这个概念的解释文献较少,我谷歌了一下当时提出这个概念的文献即 Three things everyone should know to improve object retrieval ,目的是找出一种比较欧氏距离更为精确的度量方法,对SIFT进行优化:
    123

  • 发现和SIFT的区别就是多了一个L1归一化然后取平方根(square-root)的过程。

核心代码

HoG特征提取

def get_HoG_feature(img_src, resize_width = img_width, resize_height = img_height):# 读取图片,以灰度图的形式img = cv2.imread(img_src, cv2.IMREAD_GRAYSCALE)img = cv2.resize(img, (img_width, img_height))# 采用Gamma校正对图像的颜色空间归一化img = np.sqrt(img / float(np.max(img)))# 计算每个像素的梯度值# 计算图像横坐标和纵坐标方向的梯度,并据此计算每个像素位置的梯度方向值;# 求导操作不仅能够捕获轮廓,人影和一些纹理信息,还能进一步弱化光照的影响。# 在求出输入图像中像素点(x,y)处的水平方向梯度、垂直方向梯度和像素值,从而求出梯度幅值和方向。height, width = img.shapegradient_values_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)gradient_values_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)gradient_magnitude = cv2.addWeighted(gradient_values_x, 0.5, gradient_values_y, 0.5, 0)gradient_angle = cv2.phase(gradient_values_x, gradient_values_y, angleInDegrees=True)# print ('gradient_magnitude.shape, gradient_angle.shape:')# print (gradient_magnitude.shape, gradient_angle.shape)# 为每个细胞单元构建梯度方向直方图angle_unit = 360 / bin_size # 每个梯度方向块的角度gradient_magnitude = abs(gradient_magnitude)# print ('cell_gradient_vector:')cell_gradient_vector = np.zeros((height // cell_size, width // cell_size, bin_size)) # print (cell_gradient_vector.shape)for i in range(cell_gradient_vector.shape[0]):for j in range(cell_gradient_vector.shape[1]):cell_magnitude = gradient_magnitude[i * cell_size:(i + 1) * cell_size,j * cell_size:(j + 1) * cell_size]cell_angle = gradient_angle[i * cell_size:(i + 1) * cell_size,j * cell_size:(j + 1) * cell_size]cell_gradient_vector[i][j] = cell_gradient(cell_magnitude, cell_angle)# 可视化Cell梯度直方图hog_image= np.zeros([height, width])cell_width = cell_size / 2max_mag = np.array(cell_gradient_vector).max()for x in range(cell_gradient_vector.shape[0]):for y in range(cell_gradient_vector.shape[1]):cell_grad = cell_gradient_vector[x][y]cell_grad /= max_magangle = 0angle_gap = angle_unitfor magnitude in cell_grad:angle_radian = math.radians(angle)x1 = int(x * cell_size + magnitude * cell_width * math.cos(angle_radian))y1 = int(y * cell_size + magnitude * cell_width * math.sin(angle_radian))x2 = int(x * cell_size - magnitude * cell_width * math.cos(angle_radian))y2 = int(y * cell_size - magnitude * cell_width * math.sin(angle_radian))cv2.line(hog_image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))angle += angle_gap# plt.imshow(hog_image, cmap=plt.cm.gray)# plt.show()# 统计Block的梯度信息hog_vector = []for i in range(cell_gradient_vector.shape[0] - 1):for j in range(cell_gradient_vector.shape[1] - 1):block_vector = []block_vector.extend(cell_gradient_vector[i][j])block_vector.extend(cell_gradient_vector[i][j + 1])block_vector.extend(cell_gradient_vector[i + 1][j])block_vector.extend(cell_gradient_vector[i + 1][j + 1])mag = lambda vector: math.sqrt(sum(i ** 2 for i in vector))magnitude = mag(block_vector)if magnitude != 0:normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector]block_vector = normalize(block_vector, magnitude)hog_vector.append(block_vector)# print ('np.array(hog_vector).shape')# print (np.array(hog_vector).shape)return np.array(hog_vector)

SIFT特征提取

def get_SIFT_feature(img_src, resize_width = img_width, resize_height = img_height):img = cv2.imread(img_src)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)gray = cv2.resize(img, (img_width, img_height))# 实例化函数sift = cv2.SIFT_create()# 找出图像中的关键点kp = sift.detect(gray, None)# 绘制关键点# ret = cv2.drawKeypoints(gray, kp, img)# cv2.imshow('ret', ret)# cv2.waitKey(0)# cv2.destroyAllWindows()# 计算关键点对应的sift特征kp, des = sift.compute(gray, kp)# 这里用了cv的库一步到位,返回Descriptorreturn des

Kmeans聚类

def kmeans_open(dataSet,k):# 随机取k个聚类中心 # 对每个点求最近k个聚类中心,# 更新每个特征点到聚类中心的距离,# 求每个聚类中心的特征点集的平均值m = np.shape(dataSet)[0]  #行的数目# 第一列存样本属于哪一簇# 第二列存样本的到簇的中心点的误差clusterAssment = np.mat(np.zeros((m,2)))clusterChange = True# 第1步 初始化centroidscentroids = randCent(dataSet,k)while clusterChange:clusterChange = False# 遍历所有的样本(行数)num = 0for i in range(m):minDist = 100000.0minIndex = -1# 遍历所有的质心#第2步 找出最近的质心for j in range(k):# 计算该样本到质心的欧式距离distance = distEclud(centroids[j,:],dataSet[i,:])if distance < minDist:minDist = distanceminIndex = j# 第 3 步:更新每一行样本所属的簇if clusterAssment[i,0] != minIndex:num = num + 1clusterChange = TrueclusterAssment[i,:] = minIndex,minDist**2#第 4 步:更新质心for j in range(k):pointsInCluster = dataSet[np.nonzero(clusterAssment[:,0].A == j)[0]]  # 获取簇类所有的点 .A:matrix->arraycentroids[j,:] = np.mean(pointsInCluster,axis=0)   # 对矩阵的列求均值,最后得到一个质心坐标print(num)return clusterAssment.A[:,0], centroids

查询框架

# 首次查询
queryRetrieval(datasetCombine, topN)# 检索评价实现
[recall, precision] = retrievalEvaluation(datasetCombine)print('首次查询成功!')
print('Percision:%f' %precision)
print('Recall:%f' %recall)# 扩展查询
print('扩展查询开始!')avgFeat = averageFeat(datasetCombine, topN)
datasetCombine = similarityImg(datasetImg, avgFeat)
showImg(datasetCombine, topN)[recallEQ, precisionEQ] = retrievalEvaluation(datasetCombine)print('扩展查询成功!')
print('PercisionEQ:%f' %precisionEQ)
print('RecallEQ:%f' %recallEQ)

检索效果

  • 我这里用的汽车样本库,通过搜索图片文件名中的汽车号衡量精确度和召回率;
    car
  • 可以看到使用QE和不使用QE的情况下效果差距很明显,这种差距在样本量增大的情况下会更明显;
    result1
    result

总结

  • BoW模型下的图像检索系统作为视觉方向的典中典项目以及CNN的经典入门项目之一(虽然并没有用到),其中牵扯到的原理知识并不是太复杂,但真正上手写代码的时候,还是容易出问题,整套系统的流程衔接一定得吃透,而如何处理从图片中提取到的特征、如何进行匹配是这个项目中最有意思、也是最具挑战性最关键的一环;
  • 最开始做的时候用C++来写,碍于各种环境配置的麻烦(主要就是SIFT的专利问题,折腾了很久)和C对向量操作的不友好,后来慢慢转成了较为生疏的python,这也是一种挑战吧;
  • 线代一定得好好学!线代一定得好好学!线代一定得好好学!

这篇关于【OpenCV】基于BoW词袋模型/HoG+SIFT特征提取的图像检索系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/733683

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

AI Toolkit + H100 GPU,一小时内微调最新热门文生图模型 FLUX

上个月,FLUX 席卷了互联网,这并非没有原因。他们声称优于 DALLE 3、Ideogram 和 Stable Diffusion 3 等模型,而这一点已被证明是有依据的。随着越来越多的流行图像生成工具(如 Stable Diffusion Web UI Forge 和 ComyUI)开始支持这些模型,FLUX 在 Stable Diffusion 领域的扩展将会持续下去。 自 FLU