php中文网 | cnphp.com

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 651|回复: 0

[computer vision] Bag of Visual Word (BOW)

[复制链接]

3142

主题

3152

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

UID
1
威望
0
积分
7956
贡献
0
注册时间
2021-4-14
最后登录
2024-11-22
在线时间
763 小时
QQ
发表于 2022-4-2 08:23:47 | 显示全部楼层 |阅读模式
BoW 是传统的计算机视觉方法,用一些特征(一些向量)来表示一个图像。BoW的核心思想是利用一组较为通用的特征,将图像用这些特征来表示,不同图像对于同一个特征的响应也是不同的,最终一个图像可以转化成关于这一组特征的一个频率直方图(向量)。这里有个挺清晰的介绍。BoW 常常用在 content-based image retrieval (CBIR) 任务上。
例如下面这张图(来源 Brown Computer Vision 2021 )形象的介绍了BoW的,首先有一堆图片,然后提取这些图片中的特征,然后提取具有代表性的通用特征,然后计算不同图像对于这些特征的响应,从而将图像转换成关于这组特征的一个特征向量。
image.png
本文不过多的介绍理论部分,主要使用opencv来进行一些实践操作。

本文使用的是一个比较老的数据集是 ZuBuD 数据集,是苏黎世联邦理工构建的数据集,开放下载。数据集是苏黎世城市内的一些建筑,训练集有1005张图像,包含201个建筑,测试集有115张图像,用来测试 image retrieval,有ground truth信息,即指定来哪些图像是对应的,如下随便找了两张图片。
image.png
image.png
以下是 ground truth 的部分信息,例如第一行代表测试集中编号为 1 的图像对应到训练集中,应该是编号 100。
TEST        TRAIN
001        100
002        102
003        104
004        105
005        107
006        109
...
...
对每个图像提取sift特征
将训练集的所有特征放在一起进行聚类
对训练集中的图像计算直方图
对测试集中的图像计算直方图
从训练集中找和测试图像直方图最接近的图像作为结果
计算正确率
有了上述思路后,代码的逻辑也比较清晰了,下面给出所有的代码,详细的解释在注释里。
[mw_shl_code=applescript,true]#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)[/mw_shl_code]
本文一共测试了6组聚类的类别,随着类别增多,准确的逐渐上升,但是太对类别准确度反而会下降,这是因为在实验中发现每张图像平均也就能提取1000~1500个特征点,2000个类别太多啦。下面是绘制的准确度折线图,因为1000 - 2000之间没有测试,因此可能准确率还会有所提升。600个类别的准确率为 75.65%, 1000个 准确率为 78.26%。
image.png
关于耗时,2020年 mac pro:

提取所有图像 SIFT 特征,耗时 55s 左右。
聚类 600 类,耗时 191s 左右,聚类 1000 类,耗时 251s 左右
计算频率直方图,600 类大概 6s,1000 类 9s
预测耗时基本都是 1.5s





上一篇:centos下用ffmpeg推流宇视科技摄像头rtsp流到前端播放(无flash)
下一篇:C语言 之 多线程编程
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|php中文网 | cnphp.com ( 赣ICP备2021002321号-2 )

GMT+8, 2024-11-22 09:30 , Processed in 0.283490 second(s), 36 queries , Gzip On.

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2020, Tencent Cloud.

申明:本站所有资源皆搜集自网络,相关版权归版权持有人所有,如有侵权,请电邮(fiorkn@foxmail.com)告之,本站会尽快删除。

快速回复 返回顶部 返回列表