[computer vision] Bag of Visual Word (BOW)
Bag of Visual Word (BoW, BoF, 词袋)
简介
BoW 是传统的计算机视觉方法,用一些特征(一些向量)来表示一个图像。BoW的核心思想是利用一组较为通用的特征,将图像用这些特征来表示,不同图像对于同一个特征的响应也是不同的,最终一个图像可以转化成关于这一组特征的一个频率直方图(向量)。这里有个挺清晰的介绍。BoW 常常用在 content-based image retrieval (CBIR) 任务上。
例如下面这张图(来源 Brown Computer Vision 2021 )形象的介绍了BoW的,首先有一堆图片,然后提取这些图片中的特征,然后提取具有代表性的通用特征,然后计算不同图像对于这些特征的响应,从而将图像转换成关于这组特征的一个特征向量。

实践
本文不过多的介绍理论部分,主要使用opencv来进行一些实践操作。
数据集
本文使用的是一个比较老的数据集是 ZuBuD 数据集,是苏黎世联邦理工构建的数据集,开放下载。数据集是苏黎世城市内的一些建筑,训练集有1005张图像,包含201个建筑,测试集有115张图像,用来测试 image retrieval,有ground truth信息,即指定来哪些图像是对应的,如下随便找了两张图片。


以下是 ground truth 的部分信息,例如第一行代表测试集中编号为 1 的图像对应到训练集中,应该是编号 100。
TEST TRAIN
001 100
002 102
003 104
004 105
005 107
006 109
...
...
总体思路
- 对每个图像提取sift特征
- 将训练集的所有特征放在一起进行聚类
- 对训练集中的图像计算直方图
- 对测试集中的图像计算直方图
- 从训练集中找和测试图像直方图最接近的图像作为结果
- 计算正确率
代码部分
有了上述思路后,代码的逻辑也比较清晰了,下面给出所有的代码,详细的解释在注释里。
#1.对每个图像提取sift特征
#2.将训练集合的所有特征放在一起进行聚类
#3.对每个图像计算直方图
#4.对测试图像计算直方图
#5.从训练集中寻找和测试图像直方图最近接近的图像作为结果
#6.计算正确率
import cv2
import os
import matplotlib.pyplot as plt
import numpy as np
import time
from sklearn.cluster import MiniBatchKMeans
DataPath = "../Dataset/ZuBuD" #数据集的根目录
TrainPath = os.path.join(DataPath, "png-ZuBuD") #训练集的根目录
TestPath = os.path.join(DataPath,"1000city","qimage") #测试集的根目录
trainList = os.listdir(TrainPath) #训练集图像的所有名字
TrainSIFTPath = "../Dataset/ZuBuD/Train_SIFT" #训练集图像SIFT保存的路径(保存在文件中时有用)
TestSIFTPath = "../Dataset/ZuBuD/Test_SIFT" #测试集图像SIFT保存的路径(保存在文件中时有用)
TrainSIFT = []#训练集的SIFT特征,为了后面numpy方便拼接
TestSIFT = []#测试集的SIFT特征
Train_SIFT_dict = {}#同上,只不过用名字来索引特征
Test_SIFT_dict = {}
#批量生成SIFT特征
def genSIFT(dataDir,outdir, outlist,outdict):
begin = time.time()
sift = cv2.SIFT_create()
imgList = os.listdir(dataDir)
if not os.path.exists(outdir):
os.mkdir(outdir)
count = 0
for name in imgList:
ext = os.path.splitext(name)[-1]
if ext!=".png" and ext!=".JPG" and ext!=".jpg" :
continue
#读取图片、转成灰度、提取描述子
path = os.path.join(dataDir,name)
imgdata = cv2.imread(path)
gray = cv2.cvtColor(imgdata,cv2.COLOR_BGR2GRAY)
_, des = sift.detectAndCompute(gray, None)
outlist.append(des)
outdict[name] = des
#np.save(os.path.join(outdir,name),des)
print(len(imgList),count)
count = count + 1
end = time.time()
#聚类,也是生成通用特征、词袋,这里用的是MiniBatchKMeans,这个比KMeans快,精度没有差很多
def cluster(featureList, n):
#将所有训练图片的SIFT特征放在一起进行聚类
begin = time.time()
X = np.concatenate(featureList)
kmeans = MiniBatchKMeans(n_clusters=n, random_state=0,verbose=1).fit(X)
end = time.time()
return kmeans
#计算余弦距离,为了计算相似度
def get_cos_similar(v1, v2):
num = float(np.dot(v1, v2))
denom = np.linalg.norm(v1) * np.linalg.norm(v2)
return 0.5 + 0.5 * (num / denom) if denom != 0 else 0
#读取groundtruth文件,生成数据对
def getGroundTruth(dataPath):
gtpair = {}
with open(os.path.join(dataPath,"zubud_groundtruth.txt")) as f:
gt = f.readlines()
for i, line in enumerate(gt):
if i == 0:
continue
test, train = line[:-1].split("\t")
gtpair[test] = train
return gtpair
#根据聚类的结果,也就是词袋生成频率向量,这里就将图像转成了一个向量表示
def getFeatureHistogram(dataDict,kmeans):
outDict = {}
for k in dataDict.keys():
feat = dataDict[k]
his = np.bincount(kmeans.predict(feat))
if his.shape[0] < kmeans.n_clusters:
diff = kmeans.n_clusters - his.shape[0]
for i in range(diff):
his = np.append(his,0)
outDict[k] = his
return outDict
#这里时进行测试,这里使用了一种比较朴素的方法,也就是测试图像
#和训练集里的图像挨个比较,取余弦距离最大的那个作为结果。
def predict(testHisDict, trainHisDict, gtpair):
predict = {}
for testk in testHisDict.keys():
testhis = testHisDict[testk]
score = 0.0
index = ""
for traink in trainHisDict.keys():
trainhis = trainHisDict[traink]
s = get_cos_similar(testhis,trainhis)
if s > score:
score = s
index = traink
predict[testk] = index
suc = 0
for k in predict.keys():
tk = k[5:8]
pk = predict[k][7:10]
if gtpair[tk] == pk:
suc = suc+1
return suc/len(predict)
#将以上步骤串起来,调整聚类的类别,来观察精度
def pipeline(n_list):
result = []
#1.对训练集、测试集提取sift特征
t0 = time.time()
genSIFT(TrainPath,TrainSIFTPath,TrainSIFT,Train_SIFT_dict)
genSIFT(TestPath,TestSIFTPath,TestSIFT,Test_SIFT_dict)
t1 = time.time()
#2.读取ground truth
gtpair = getGroundTruth(DataPath)
#3.对训练集提取的sift进行聚类,生成 visual word
for n in n_list:
t3 = time.time()
clu = cluster(TrainSIFT, n)
t4 = time.time()
#4.计算每个图像关于 visual word 的直方图
train_his = getFeatureHistogram(Train_SIFT_dict, clu)
test_his = getFeatureHistogram(Test_SIFT_dict, clu)
t5 = time.time()
#5.利用余弦距离计算相似度
acc = predict(test_his,train_his, gtpair)
t6 = time.time()
info = {"sift":t1-t0,"clu":t4-t3,"calvw":t5-t4,"predict":t6-t5,"acc":acc}
result.append(info)
print(info)
return result
result = pipeline([50,100,300,600,1000,2000])
print(result)
测试结果
本文一共测试了6组聚类的类别,随着类别增多,准确的逐渐上升,但是太对类别准确度反而会下降,这是因为在实验中发现每张图像平均也就能提取1000~1500个特征点,2000个类别太多啦。下面是绘制的准确度折线图,因为1000 - 2000之间没有测试,因此可能准确率还会有所提升。600个类别的准确率为 75.65%, 1000个 准确率为 78.26%。

关于耗时,2020年 mac pro:
- 提取所有图像 SIFT 特征,耗时 55s 左右。
- 聚类 600 类,耗时 191s 左右,聚类 1000 类,耗时 251s 左右
- 计算频率直方图,600 类大概 6s,1000 类 9s
- 预测耗时基本都是 1.5s
[computer vision] Bag of Visual Word (BOW)的更多相关文章
- 模式识别之检索---Bag of visual word(词袋模型)
visual words 视觉单词 http://blog.csdn.net/v_july_v/article/details/8203674 http://blog.csdn.net/pi9nc/a ...
- (转) WTF is computer vision?
WTF is computer vision? Posted Nov 13, 2016 by Devin Coldewey, Contributor Next Story Someon ...
- 计算机视觉和人工智能的状态:我们已经走得很远了 The state of Computer Vision and AI: we are really, really far away.
The picture above is funny. But for me it is also one of those examples that make me sad about the o ...
- Computer Vision Algorithm Implementations
Participate in Reproducible Research General Image Processing OpenCV (C/C++ code, BSD lic) Image man ...
- Graph Cut and Its Application in Computer Vision
Graph Cut and Its Application in Computer Vision 原文出处: http://lincccc.blogspot.tw/2011/04/graph-cut- ...
- Learning ROS for Robotics Programming Second Edition学习笔记(五) indigo computer vision
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- Computer Vision Resources
Computer Vision Resources Softwares Topic Resources References Feature Extraction SIFT [1] [Demo pro ...
- Computer Vision Tutorials from Conferences (3) -- CVPR
CVPR 2013 (http://www.pamitc.org/cvpr13/tutorials.php) Foundations of Spatial SpectroscopyJames Cogg ...
- Computer Vision Tutorials from Conferences (2) -- ECCV
ECCV 2012 (http://eccv2012.unifi.it/program/tutorials/) Vision Applications on Mobile using OpenCVGa ...
随机推荐
- LeetCode-031-下一个排列
下一个排列 题目描述:实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列. 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列). 必须 原地 修 ...
- C语言之判断质数算法
今天学校OJ的一题判断是质数和合数. 首先我们要弄明白质数和合数的概念:质数就是除了本身和1以外没有其他因数的数,合数就是除了本身和1以外还有其他因数的数.注意:1既不是质数也不是合数. 明白了概念, ...
- LGP5363题解
感觉博弈题都是高大上神秘结论... 感谢@KaiSuoShuTong 开锁疏通愿意教我这题的博弈部分/qq 考虑每次移动棋子,实际上是有一车 \(a_i\),每次操作相当于令 \(a_i-c,a_{i ...
- LGP7840题解
给出一种新的理解方式,本质上和正解是一致的. 首先我们现在已经有了一个森林,我们现在要给他加一条边,加哪一条边是最优的呢? 假设加的边是 \((u,v)\),那么 \(((d[u]+1)^2-d[u] ...
- STM32芯片去除读写保护 | 使用ST-Link Utility去除STM32芯片读写保护
1.使用ST-LINK V2下载器连接到STM32芯片, 点击Connect: 2.存在读保护: 3.修改选项字节(Option Bytes... ): 4.将读保护修改为Disabled. 5.打钩 ...
- 百度Paddle速查_CPU和GPU的mnist预测训练_模型导出_模型导入再预测_导出onnx并预测
需要做点什么 方便广大烟酒生研究生.人工智障炼丹师算法工程师快速使用百度PaddelPaddle,所以特写此文章,默认使用者已有基本的深度学习概念.数据集概念. 系统环境 python 3.7.4 p ...
- 讲一讲 kafka 的 ack 的三种机制 ?
request.required.acks 有三个值 0 1 -1(all) 0:生产者不会等待 broker 的 ack,这个延迟最低但是存储的保证最弱当 server 挂 掉的时候就会丢数据. 1 ...
- java-設計模式-生成器
生成器模式Bulider 使你能够分步骤创建复杂对象. 该模式允许你使用相同的创建代码生成不同类型和形式的对象. 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示. 将一个复杂的 ...
- Java如何声明变量?JS如何声明变量?
Java采用强类型变量检查,像C语言一样.所有变量在编译之前必须声明,而且不能使用没有赋值的变量.例如:int x;x=1234;char y='F';其中X=1234说明是一个整数,Y='F'说明是 ...
- Formatter和IStandardConversionService的使用方式
Thymeleaf 1.Formatter接口是当进行请求参数的封装时,会根据Formatter的泛型进行调用Parse方法进行格式化类型 2.IStandardConversionService是T ...