草庐IT

[CV] Bag of Visual Word (BOW)

芒果和小猫 2023-03-28 原文

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
...
...

总体思路

  1. 对每个图像提取sift特征
  2. 将训练集的所有特征放在一起进行聚类
  3. 对训练集中的图像计算直方图
  4. 对测试集中的图像计算直方图
  5. 从训练集中找和测试图像直方图最接近的图像作为结果
  6. 计算正确率

代码部分

有了上述思路后,代码的逻辑也比较清晰了,下面给出所有的代码,详细的解释在注释里。

#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

有关[CV] Bag of Visual Word (BOW)的更多相关文章

  1. opencv 边缘检测 cv2.Canny()详解 - 2

    👨‍💻个人简介:深度学习图像领域工作者🎉总结链接:            链接中主要是个人工作的总结,每个链接都是一些常用demo,代码直接复制运行即可。包括:                    📌1.工作中常用深度学习脚本                    📌2.torch、numpy等常用函数详解                    📌3.opencv图片、视频等操作                    📌4.个人工作中的项目总结(纯干活)              链接:https://blog.csdn.net/qq_28949847/article/details/128

  2. opencv(12):cv::rectangle学习与代码演示,使用opencv画矩形/矩形框 - 2

    1cv::rectangle介绍1.1功能:        绘制一个简单的、粗的或填充的直角矩形或直角矩形框。1.2c++代码形式rectangle()[1/2]#includevoidcv::rectangle ( InputOutputArray img, Point pt1, Point pt2, constScalar& color, int thickness=1, int lineType=LINE_8, int shift=0 ) img  图像。pt1     矩形的顶点。pt2     与pt1相对的矩形的顶点。意思是pt1和pt2是对角顶点color  颜色或亮

  3. OpenCV——Canny边缘检测(cv2.Canny()) - 2

    Canny边缘检测Canny边缘检测是一种使用多级边缘检测算法检测边缘的方法。1986年,JohnF.Canny发表了著名的论文AComputationalApproachtoEdgeDetection,在该论文中详述了如何进行边缘检测。Canny()边缘检测步骤Canny边缘检测分为如下几个步骤:步骤1:去噪。噪声会影响边缘检测的准确性,因此首先要将噪声过滤掉。步骤2:计算梯度的幅度与方向。步骤3:非极大值抑制,即适当地让边缘“变瘦”。步骤4:确定边缘。使用双阈值算法确定最终的边缘信息。下面对上述步骤分别进行简单的介绍。1.应用高斯滤波去除图像噪声由于图像边缘非常容易受到噪声的干扰,因此为了

  4. xml - Open CV 计算机视觉中的 haar 级联分类器内部是什么? - 2

    我需要将.xmlOpenCVhaar级联转换为txt文件。(OpenCV有一个基于Haar特征的级联分类器用于目标检测。)所以我需要了解xml。我想知道什么是“阶段”和“树”。树代表弱分类器吗?同一阶段的树是否组合成一个强分类器?这些阶段是级联的吗???在haarcascade_frontalface_alt.xml的树中,它说:37144-1.391422.04.0141958743333817e-0030.03379419073462490.8378106951713562我想知道数字代表什么。 最佳答案 我将尝试解释级联xml

  5. python - 导入 CV2 : DLL load failed (Python in Windows 64bit) - 2

    ImportError:DLLloadfailed:%1isnotavalidWin32application有人知道怎么解决吗?当我尝试导入cv2时会出现此问题。我的笔记本电脑是64位的,安装了64位的python,我也把cv2.pyd文件放到了Python的site-packages文件夹下。我的PYTHONPATH值=C:\Python35;C:\Python35\DLLs;C:\Python35\Lib;C:\Python35\libs;C:\Users\CV\OpenCV\opencv\build\python\2.7\x64;%OPENCV_DIR%\bin;我的OPENC

  6. windows - 使用 Mingw 创建 OpenCV 3.1 的共享版本时未定义对 cv::redirectError 的引用 - 2

    程序和版本:Windows10、Mingw32withgcc6.1.0、Cmake3.6.1、(Code::blocks16.01)首先,我应该提到构建静态构建确实可以完美地工作,仅构建共享构建是行不通的,而且我没有找到针对我的特定问题的解决方法。我尝试使用上述程序作为共享构建来构建OpenCV3.1。使用cmake的默认设置,出现以下错误:[34%]LinkingCXXexecutable..\..\bin\opencv_test_core.exe../../lib/libopencv_ts310.a(ts.cpp.obj):ts.cpp:(.text$_ZN6cvtest2TS4i

  7. c++ - 未处理的异常 Microsoft C++ 异常:cv::Exception at memory location - 2

    我刚开始使用OpenCV。我下载了OpenCV2.4.9,并安装了MSVS2010。我的Windows是X64。我遵循了以下步骤:一个。在ConfigurationProperties下,单击Debugging->Environment并复制粘贴:PATH=C:\opencv\build\x86\vc10\binVC++目录->包含目录并添加条目:C:\opencv\build\includeVC++目录->库目录并添加条目:C:\opencv\build\x86\vc10\libLinker->Input->AdditionalDependencies并添加以下内容:opencv_c

  8. python,windows 7,无法导入cv2 - 2

    我知道之前有人提出过这个问题,我已经尝试了所有发现的修复方法,但都无济于事。当我尝试导入cv2时,我收到消息:DLLloadfailed:%1isnotavalidWin32application.我在windows7,64bit上运行python2.7。我尝试使用以下方法安装cv2:python-mpipinstallcv2以及安装.whl文件(opencv_python-2.4.12-cp27-none-win_amd64.whl)。两次,cmd提示都告诉我它们已正确安装,但我仍然无法导入cv2。接下来我尝试的是下载最新版本的opencv并按照说明进行操作here安装它。我还尝试在

  9. python - uri 警告 : Error opening file with Python OpenCV cv2. VideoCapture() 中缺少端口 - 2

    当我尝试流式传输ipcam时,我遇到了如下所示的错误"[tcp@000000000048c640]Portmissinginuriwarning:Erroropeningfile(/build/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp:901)"importnumpyasnpimportcv2cv2.__file__cap=cv2.VideoCapture('http://admin:password@http://192.168.1.***/')#cap=cv2.VideoCapture('https://www.youtube.c

  10. c++ - OpenCV 3 错误 'CV_FOURCC' : identifier not found - 2

    刚刚在PC上使用VisualStudio2013构建了OpenCV3,现在我正在尝试编写代码,但遗憾的是我不知道出了什么问题?#include"opencv2/opencv.hpp"#includeusingnamespacestd;usingnamespacecv;intmain(){VideoCapturevcap(0);if(!vcap.isOpened()){cout>frame;video.write(frame);imshow("Frame",frame);charc=(char)waitKey(33);if(c==27)break;}return0;1>------Bui

随机推荐