计算机视觉(五)

article/2025/9/22 15:16:58

Bag of features,简称Bof,中文翻译为“词袋”,是一种用于图像或视频检索的技术。而检索就要进行比对。两幅不同的图像如何比对,比对什么,这就需要提炼出每幅图像中精练的东西出来进行比较。

一、Bag of features算法基础流程
1、收集图片,对图像进行sift特征提取。

2、从每类图像中提取视觉词汇,将所有的视觉词汇集合在一起。
3、利用K-Means算法构造单词表。
K-Means算法是一种基于样本间相似性度量的间接聚类方法,此算法以K为参数,把N个对象分为K个簇,以使簇内具有较高的相似度,而簇间相似度较低。SIFT提取的视觉词汇向量之间根据距离的远近,可以利用K-Means算法将词义相近的词汇合并,作为单词表中的基础词汇,假定我们将K设为3,那么单词表的构造过程如下:

4、针对输入的特征集,根据视觉词典进行量化,把输入图像转化成视觉单词的频率直方图(统计直方图)。

5、构造特征到图像的倒排表,通过倒排表快速索引相关图像。
6、根据索引结果进行直方图匹配。
SIFT和K-means都是之前已经学习过的东西,就不再赘述了,这一章最重要的知识点就在于视觉词典的学习量化过程。

对于图像检索来说,最重要的就是要找到图像中最能表征该图像的特征,一些图像特征即使在一张图像中出现的频率很高,但同时它也会在多张图像中出现,这就导致该特征的图像分类能力很差,对图像分类起不到作用,类似于BOW方法中要剔除的文章中的高频但无效的单词:the、a、I 等。这就要用到TF-IDF的概念。

TF-IDF

TF-IDF(term frequency–inverse document frequency,词频-逆向文件频率)是一种用于信息检索(information retrieval)与文本挖掘(text mining)的常用加权技术。

TF

词频 (term frequency, TF) 指的是某一个给定的词语在该文件中出现的次数。这个数字通常会被归一化(一般是词频除以文章总词数), 以防止它偏向长的文件。(同一个词语在长文件里可能会比短文件有更高的词频,而不管该词语重要与否。)

但是, 需要注意, 一些通用的词语对于主题并没有太大的作用, 反倒是一些出现频率较少的词才能够表达文章的主题, 所以单纯使用是TF不合适的。

权重的设计必须满足:一个词预测主题的能力越强,权重越大,反之,权重越小。所有统计的文章中,一些词只是在其中很少几篇文章中出现,那么这样的词对文章的主题的作用很大,这些词的权重应该设计的较大。TF就是在完成这样的工作.TF计算公式:

903b2ae1eb594e4189f834a75209eede.png

IDF

逆向文件频率 (IDF) :某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到。如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。IDF计算公式:

7caa6bc57bbe47bc8db78d09500d71ba.png

某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。TF-IDF就是TF与IDF的乘积:TF−IDF=TF∗IDF

实验:

用爬虫爬数据集:

import re  #为了正则表达式import requests#请求网页urlimport os #操作系统num=0    #给图片名字加数字header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36',        'Cookie':'BAIDUID=4CF8D0A4EC34C5767054C91DBBA320BF:FG=1; HOSUPPORT=1; HOSUPPORT_BFESS=1; UBI=fi_PncwhpxZ~TaJc8E~o7AylVAKZjSR4uhv; HISTORY=439af17d00ab5d33faa401de40f7f8eb74194a; HISTORY_BFESS=439af17d00ab5d33faa401de40f7f8eb74194a; UBI_BFESS=fi_PncwhpxZ~TaJc8E~o7AylVAKZjSR4uhv; BIDUPSID=4CF8D0A4EC34C5767054C91DBBA320BF; PSTM=1655628504; H_PS_PSSID=31253_26350; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; delPer=0; PSINO=7; BA_HECTOR=8h802k840g840l0k201hatq5c14; ZFY=3H1nYUpCDFUH:A25KqGctlJ279sGwBdePORsKbLyIYLk:C; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm; BDRCVFR[8yR0uJE9nIf]=mk3SLVN4HKm; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; userFrom=www.baidu.com; ab_sr=1.0.1_OWIzNGM5OWI5NGVkY2E5ZDg3NzQ2ZTZkYTk1MzNjYWMyMmZlMDY2ZmQ5OWUwODI2ODQzYmUxYWJiYjY4ODQyMjJmMWI1MGI5Y2Q0OWEyMWUyNmNhNjI0NDdhNTY2Yjg0N2E2NjI5Y2IwN2NiZWE3MTNkMzJjNDRlNjhkMWI4Zjk4YzEzNjUyY2Q4YzI4MjM4YmYxYjIyZmUwYWFmM2UzZg==; pplogid=2664kDh7VOrMUky4o78I99kxlKQzcDm6gbz29vdXB9MgNbC44ABDCk1rC0It9nsOypWexVqw687HBue7o3IdWPQjH5WH9cSeGdQacgLvUsv/UMk=; pplogid_BFESS=2664kDh7VOrMUky4o78I99kxlKQzcDm6gbz29vdXB9MgNbC44ABDCk1rC0It9nsOypWexVqw687HBue7o3IdWPQjH5WH9cSeGdQacgLvUsv/UMk=',        'Accept':'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',        'Accept-Encoding':'gzip, deflate, br',        'Accept-Language':'zh-CN,zh;q=0.9'        }  #请求头,谷歌浏览器里面有,具体在哪里找到详见我上一条csdn博客#图片页面的urlurl='https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&sf=1&fmq=1655641574257_R&pv=&ic=&nc=1&z=&hd=&latest=&copyright=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&dyTabStr=MCwzLDIsNiw0LDUsMSw4LDcsOQ==&ie=utf-8&sid=&word=新之助'#通过requests库请求到了页面html=requests.get(url,headers=header)#防止乱码html.encoding='utf8'#打印页面出来看看print(html.text) html=html.textpachong_picture_path='C:\Python\imgs\pc'if not os.path.exists(pachong_picture_path):    os.mkdir(pachong_picture_path)   res=re.findall('"objURL":"(.*?)"',html)  #正则表达式,筛选出html页面中符合条件的图片源代码地址urlfor i in res:   #遍历    num=num+1       #数字加1,这样图片名字就不会重复了    picture=requests.get(i)       #得到每一张图片的大图    file_name='C:\Python\imgs\pc\蜡笔小新'+str(num)+".jpg"   #给下载下来的图片命名。加数字,是为了名字不重复    f=open(file_name,"wb")    #以二进制写入的方式打开图片    f.write(picture.content)   # 往图片里写入爬下来的图片内容,content是写入内容的意思     print(i)    #看看有哪些urlf.close()      #结束f文件操作

bc9cf88640964f0dac729ac2400d1a35.png

从图像中提取视觉词汇,将所有的视觉词汇集合在一起,利用K-Means算法构造单词表:

import picklefrom PCV.imagesearch import vocabularyfrom PCV.tools.imtools import get_imlistfrom PCV.localdescriptors import sift#获取图像列表imlist = get_imlist('C:\Python\imgs\pc5')nbr_images = len(imlist)#获取特征列表featlist = [imlist[i][:-3]+'sift' for i in range(nbr_images)]#提取文件夹下图像的sift特征for i in range(nbr_images):    sift.process_image(imlist[i], featlist[i])#生成词汇voc = vocabulary.Vocabulary('ukbenchtest')voc.train(featlist, 1000, 10)#保存词汇with open('C:\Python\imgs\pc5\vocabulary1000.pkl', 'wb') as f:    pickle.dump(voc, f)print ('vocabulary is:', voc.name, voc.nbr_words)

a387d436b1ba4021a600f2a63b91728d.png

 针对输入的特征集,根据视觉词典进行量化,把输入图像转化成视觉单词的频率直方图:

import picklefrom PCV.imagesearch import imagesearchfrom PCV.localdescriptors import siftimport sqlite3from PCV.tools.imtools import get_imlist# 获取图像列表imlist = get_imlist(r'C:Pythonimgspc5')nbr_images = len(imlist)# 获取特征列表featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]# 载入词汇with open(r'C:Pythonimgspc5ocabulary1000.pkl', 'rb') as f:    voc = pickle.load(f)# 创建索引indx = imagesearch.Indexer(r'C:Pythonimgspc5	estImaAdd.db', voc)indx.create_tables()# 遍历所有的图像,并将它们的特征投影到词汇上for i in range(nbr_images)[:51]:    locs, descr = sift.read_features_from_file(featlist[i])    indx.add_to_index(imlist[i], descr)# 提交到数据库indx.db_commit()con = sqlite3.connect(r'C:Pythonimgspc5	estImaAdd.db')print(con.execute('select count (filename) from imlist').fetchone())print(con.execute('select * from imlist').fetchone())

113519d1816444f3b0312023772b54fb.png

 构造特征到图像的倒排表,通过倒排表快速索引相关图像,根据索引结果进行直方图匹配:

import picklefrom PCV.imagesearch import imagesearchfrom PCV.geometry import homographyfrom PCV.tools.imtools import get_imlistfrom PCV.localdescriptors import siftimport warningswarnings.filterwarnings("ignore")# load image list and vocabulary# 载入图像列表imlist = get_imlist(r'C:Pythonimgspc5')nbr_images = len(imlist)# 载入特征列表featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]# 载入词汇with open(r'C:Pythonimgspc5ocabulary1000.pkl', 'rb') as f:    voc = pickle.load(f, encoding='iso-8859-1')src = imagesearch.Searcher(r'C:Pythonimgspc5	estImaAdd.db', voc)  # Searcher类读入图像的单词直方图执行查询# index of query image and number of results to return# 查询图像索引和查询返回的图像数q_ind = 0nbr_results = 51# regular query# 常规查询(按欧式距离对结果排序)res_reg = [w[1] for w in src.query(imlist[q_ind])[:nbr_results]]  # 查询的结果print('top matches (regular):', res_reg)# load image features for query image# 载入查询图像特征进行匹配q_locs, q_descr = sift.read_features_from_file(featlist[q_ind])fp = homography.make_homog(q_locs[:, :2].T)# RANSAC model for homography fitting# 用单应性进行拟合建立RANSAC模型model = homography.RansacModel()rank = {}# load image features for result# 载入候选图像的特征for ndx in res_reg[1:]:    #在报错行出添加try except语句    try:        locs,descr = sift.read_features_from_file(featlist[ndx])  # because 'ndx' is a rowid of the DB that starts at 1    except:        continue    # get matches    matches = sift.match(q_descr, descr)    ind = matches.nonzero()[0]    ind2 = matches[ind]    tp = homography.make_homog(locs[:, :2].T)    # compute homography, count inliers. if not enough matches return empty list    # 计算单应性矩阵    try:        H, inliers = homography.H_from_ransac(fp[:, ind], tp[:, ind2], model, match_theshold=4)    except:        inliers = []    # store inlier count    rank[ndx] = len(inliers)# sort dictionary to get the most inliers first# 对字典进行排序,可以得到重排之后的查询结果sorted_rank = sorted(rank.items(), key=lambda t: t[1], reverse=True)res_geom = [res_reg[0]] + [s[0] for s in sorted_rank]print('top matches (homography):', res_geom)# 显示查询结果imagesearch.plot_results(src, res_reg[:8])  # 常规查询imagesearch.plot_results(src, res_geom[:8])  # 重排后的结果

 检索图片为第一张:

聚类类别为1000:90277c7f054e4d089513ae5830ce6e40.png

常规查询:

d9f7c774780148e0a2be750ef4855706.png

 重排后:59e7c2b0bf56498c912545ca56d454cb.png

 聚类类别为500:

38fcc40b3d4144e491c71843e98ee41b.png

常规查询:

b1c0d8bd1af84045bcf3038e7bc9f1cd.png

重排后:

80871e9ed82b4dd1af6a31f0eaa03b80.png

聚类类别为100:

990483b7eea946e7ae5235f7482049b5.png

常规排序:

61cbc17da1e745d1a87dd02cc6ffbaf0.png

重排后:

8922363c16d64f9b99f38b16e05666ac.png

检索图片为第39张:

聚类类别为1000:

986cb32f9e104c4cbbccd5b784f53c9f.png

常规查询:

5ebd9f9e5af14d0d83e25f11ac47a47a.png

重排后:

e14fa3823cd6495c888226b6248d354a.png

聚类类别为500:

de758c9367bf4237b12cf43f2c59746a.png

常规查询:

fa4d14cd694b4eaab8a859f4d75bbf76.png

重排后:

628633ea8f4f4de9b001bd6c3cc4dba8.png

聚类类别为100:

5dbf52c7042c4db8a4b2dbd061607d94.png

常规排序:

3e210fcfd4f34b258f08831ea473cba5.png

重排后:

7e909ba9ff9645a88cae9838571431cd.png

检索图片为第48张:

聚类类别为1000:

5786ade027e444d38bdcb0073dac8145.png

常规排序:4f1bf958c5f440889b5a17ce68601798.png

重排后:

f3b9a51eec2546199054bd28660388d3.png

聚类类别为500:866940a24ace4263bc91fde4cc13b798.png

常规排序:

62d989e9853a4af297ad9f9bb38b999a.png

重排后:55de577f0f37434aa1b7b004952bead8.png

聚类类别为100:

474002ae9344424abf0665c0cce49337.png

常规排序:

e4e53f48a72a4cc6a108bf01c7aa5619.png

重排后:

e3e08da9391a465d881f7dc9642f8c77.png

  从上面的运行结果可以发现,随着聚类类别数量的增加,图像检索的结果越来越好,更加接近合理性。但是当类别数量K增加到某一个值得时候,图像检索的效果又开始下降,会有过拟合的现象。因此聚类类别数量能够很大程度的影响图像检索的效果。

 

 


http://chatgpt.dhexx.cn/article/SB9YfXNR.shtml

相关文章

Eastmount博客导读:专栏系统分类和博客归纳总结

为了更好地帮助博友学习作者的博客,方便作者自己归纳总结专栏,本文详细介绍了作者八年来,在CSDN写的各种专栏,各种系列文章。八年来,作者经历了从本科到硕士,到贵州教书成家,再到现在的博士。八…

Python编程实现用KNN算法对红酒分类功能

一、任务要求 导入红酒数据集(load_wine),编写Python代码,完成以下任务: 1、实现计算平均酒精含量的功能; 2、实现对数据的标准化; 3、使用kNN算法实现红酒分类功能 二、代码实现 from sklearn…

文本挖掘(四万字总结篇:爬虫 - 文本预处理 - 高频词统计 - 聚类 - 情感分析)

1 爬虫 1.1 爬虫原理 这部分内容可以跳过,掌握与否对后面内容的阅读影响并不大,但有兴趣的话可以看看呐~ 实现一个爬虫,一般需要经过两个步骤:处理请求和解析源码/数据。 处理请求方面,我们可以使用Python程序自动发送…

python卷积神经网络代码,python卷积神经网络分类

怎样用python构建一个卷积神经网络模型 上周末利用python简单实现了一个卷积神经网络,只包含一个卷积层和一个maxpooling层,pooling层后面的多层神经网络采用了softmax形式的输出。 实验输入仍然采用MNIST图像使用10个featuremap时,卷积和p…

用python实现基于自媒体数据的人群聚类分析

🍅程序员小王的博客:程序员小王的博客 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕 🍅java自学的学习…

二叉树三种非递归遍历以及代码实现

二叉树三种非递归遍历 1.二叉树前序非递归遍历实现,(采用栈) 思路:(用一个栈) 1.首先用cur标记树的根(root),当cur非空的时候; 2.就直接打印根,并且将cur(也就是root)入栈; 3.接着遍历根的左子…

c++练习(5):二叉树非递归遍历

二叉树遍历 二叉树有三种遍历方法:前序(跟左右)跟节点在前面、中序(左跟右)跟节点在中间、后续(左右跟)跟节点在后面 前序(跟左右):上图的二叉树,第一次跟左右对应ABC…

C++——二叉树OJ|二叉树非递归遍历

目录 二叉树的前序遍历 二叉树的中序遍历 二叉树的后续遍历 二叉树的前序遍历 144. 二叉树的前序遍历 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:vector<int> preorderTraversal(TreeNode* root) { TreeNode* curroot; stack<TreeNode*>…

数据结构与算法_二叉树非递归遍历

记录二叉树的前序&#xff0c;中序&#xff0c;后续&#xff0c;层序等非递归遍历。 1 二叉树非递归前序遍历 用栈实现二叉树非递归前序遍历&#xff0c;按照 V L R顺序进行遍历&#xff1b;每一个都按照V L R方式进行。 上图中&#xff0c;根节点先入栈&#xff0c;出栈并访…

二叉树遍历非递归C++

二叉树遍历非递归C 题目链接二叉树的前序遍历思路分析代码实现 二叉树的中序遍历思路分析代码实现 二叉树的后序遍历思路分析代码实现 一点点题外话 题目链接 二叉树的前序遍历 144. 二叉树的前序遍历 思路分析 既然要使用非递归的方式&#xff0c;那就必须要借助栈来进行处…

二叉树的遍历(非递归)

由于二叉树的递归方法实际上是系统在使用栈进行操作&#xff0c;因此我们的迭代&#xff08;非递归&#xff09;方法也就需要使用栈进行模拟。 一、先序遍历 我们需要明白&#xff0c;进栈的元素都是树的根节点 root。 所以我们需要先访问该节点&#xff0c;再将该节点进栈。…

二叉树非递归遍历算法分析

以前没有学习过树的相关算法&#xff0c;只是了解一些皮毛&#xff0c;最近开始认真学习它。看视频或者网上查资料&#xff0c;可以知道怎么去遍历一棵树&#xff0c;但是算法为什么是这样的呢&#xff1f;少有讲到。如果有一天&#xff0c;我忘记了这个算法&#xff0c;我需要…

c语言和c++实现二叉树非递归遍历

结合栈结构来实现二叉树的非递归遍历&#xff0c;首先将根节点入栈&#xff0c;然后对栈内元素进行循环&#xff0c;弹出栈顶元素&#xff0c;根据链表结点携带的标志位flag来判断是对于结点进行打印还是后续的入栈操作。入栈顺序决定着实际的遍历方式。 main.cpp #include&l…

【数据结构】二叉树的非递归遍历(完整代码)

默认给一棵树前序遍历的结果&#xff0c;按要求创建这棵树&#xff08;#表示空&#xff09;&#xff0c;并用非递归的方法对它进行遍历。 解题思路 1.递归遍历&#xff1a; 则将二叉树的遍历看作是分治问题&#xff0c;将每一棵树都分为根-左子树-右子树三部分&#xff0c;每…

数据结构--二叉树的遍历(非递归)

我们已经对二叉树的基本概念有了一定的了解&#xff0c;也对它的递归版本的前、中、后序遍历掌握的很好了&#xff0c;那么接下来就要介绍二叉树中遍历的非递归版本 >【数据结构】二叉树详解< 先看本篇文章效果更佳哦 目录 1、非递归遍历解析2、前序遍历(非递归)3、中序…

二叉树的非递归遍历 C语言版

文章作者&#xff1a;Slyar 文章来源&#xff1a;Slyar Home (www.slyar.com) 转载请注明&#xff0c;谢谢合作。 上周数据结构课在讲二叉树的遍历&#xff0c;老师只讲递归算法&#xff0c;没有什么技术含量&#xff0c;遂自己琢磨非递归算法实现... 前序遍历:先访问根节点&…

二叉树非递归遍历

1. 遍历一棵树 前、中、后序遍历的路径实际是相同的&#xff0c;都是以如上路径去游历。 前、中、后序访问的结果不同&#xff0c;实际是访问节点的时机不同&#xff1a; 前序&#xff1a;一遇到节点就去访问 中序&#xff1a;访问完左树再去访问 后序&#xff1a;访问完右子…

C语言数据结构七:二叉树非递归遍历

二叉树的非递归遍历&#xff0c;必须借助栈的辅助。必须采用栈的这种先进后出的特性。 算法实现思路&#xff1a; 我们先将根节点压入栈中。然后往外弹出元素。直到栈中元素弹完为止。1、将根节点压入栈中。&#xff08;但是压入栈中&#xff0c;并不知简单的将根节点压入栈中…

二叉树非递归遍历(先序、中序、后序)

首先是一个二叉树结构&#xff1a; class TreeNode{public int value;public TreeNode left;public TreeNode right;public TreeNode(){}public TreeNode(int v){valuev;} 非递归的二叉树遍历需要用到栈的先进后出。 先序遍历 步骤&#xff1a; 1、首先定义一个当前正在检索…

二叉树非递归遍历(c语言)

结果如下图&#xff1a; #号代表NULL&#xff0c;此时没有节点 一、在c语言中进行二叉树的非递归遍历需要用到栈&#xff0c;而在c语言中没有直接调用栈的接口&#xff0c;所以在实现非递归遍历时需要先实现一个栈&#xff0c;需要用到出栈&#xff0c;入栈&#xff0c;栈顶元…