SIFT算法的应用--目标识别之Bag-of-words模型

时间:2022-09-21 13:46:32

原文:http://blog.csdn.net/v_JULY_v/article/details/6555899

SIFT算法的应用

-目标识别之用Bag-of-words模型表示一幅图像

作者:wawayu,July。编程艺术室出品。

出处:http://blog.csdn.net/v_JULY_v 

  • 引言

本blog之前已经写了四篇关于SIFT的文章,请参考九、图像特征提取与匹配之SIFT算法九(续)、sift算法的编译与实现九(再续)、教你一步一步用c语言实现sift算法、上,及九(再续)、教你一步一步用c语言实现sift算法、下

上述这4篇文章对SIFT算法的原理和C语言实现都做了详细介绍,用SIFT做图像匹配效果不错。现在考虑更为高层的应用,将SIFT算法应用于目标识别:发现图像中包含的物体类别,这是计算机视觉领域最基本也是最重要的任务之一。

且原经典算法研究系列可能将改名为算法珠玑--经典算法的通俗演义。改名考虑到三点:1、不求面面俱到所有算法,所以掏炼,谓之“珠玑”;2、突出本博客内算法内容的特色-通俗易懂、简明直白,谓之“通俗”;3、侧重经典算法的研究与实现,以及实际应用,谓之“演义”。

OK,闲话少说,上一篇我们介绍了六(续)、从KMP算法一步一步谈到BM算法。下面我们来介绍有关SIFT算法的目标识别的应用--Bag-of-words模型。

  • Bag-of-words模型简介

Bag-of-words模型是信息检索领域常用的文档表示方法。在信息检索中,BOW模型假定对于一个文档,忽略它的单词顺序和语法、句法等要素,将其仅仅看作是若干个词汇的集合,文档中每个单词的出现都是独立的,不依赖于其它单词是否出现。也就是说,文档中任意一个位置出现的任何单词,都不受该文档语意影响而独立选择的。例如有如下两个文档:

1:Bob likes to play basketball, Jim likes too.

2:Bob also likes to play football games.

基于这两个文本文档,构造一个词典:

Dictionary = {1:”Bob”, 2. “like”, 3. “to”, 4. “play”, 5. “basketball”, 6. “also”, 7. “football”, 8. “games”, 9. “Jim”, 10. “too”}。

这个词典一共包含10个不同的单词,利用词典的索引号,上面两个文档每一个都可以用一个10维向量表示(用整数数字0~n(n为正整数)表示某个单词在文档中出现的次数):

1:[1, 2, 1, 1, 1, 0, 0, 0, 1, 1]

2:[1, 1, 1, 1 ,0, 1, 1, 1, 0, 0]

向量中每个元素表示词典中相关元素在文档中出现的次数(下文中,将用单词的直方图表示)。不过,在构造文档向量的过程中可以看到,我们并没有表达单词在原来句子中出现的次序(这是本Bag-of-words模型的缺点之一,不过瑕不掩瑜甚至在此处无关紧要)。

  • Bag-of-words模型的应用

Bag-of-words模型的适用场合

现在想象在一个巨大的文档集合D,里面一共有M个文档,而文档里面的所有单词提取出来后,一起构成一个包含N个单词的词典,利用Bag-of-words模型,每个文档都可以被表示成为一个N维向量,计算机非常擅长于处理数值向量。这样,就可以利用计算机来完成海量文档的分类过程。

考虑将Bag-of-words模型应用于图像表示。为了表示一幅图像,我们可以将图像看作文档,即若干个“视觉词汇”的集合,同样的,视觉词汇相互之间没有顺序。

SIFT算法的应用--目标识别之Bag-of-words模型

图1 将Bag-of-words模型应用于图像表示

由于图像中的词汇不像文本文档中的那样是现成的,我们需要首先从图像中提取出相互独立的视觉词汇,这通常需要经过三个步骤:(1)特征检测,(2)特征表示,(3)单词本的生成,请看下图2:SIFT算法的应用--目标识别之Bag-of-words模型

图2 从图像中提取出相互独立的视觉词汇

通过观察会发现,同一类目标的不同实例之间虽然存在差异,但我们仍然可以找到它们之间的一些共同的地方,比如说人脸,虽然说不同人的脸差别比较大,但眼睛,嘴,鼻子等一些比较细小的部位,却观察不到太大差别,我们可以把这些不同实例之间共同的部位提取出来,作为识别这一类目标的视觉词汇。

而SIFT算法是提取图像中局部不变特征的应用最广泛的算法,因此我们可以用SIFT算法从图像中提取不变特征点,作为视觉词汇,并构造单词表,用单词表中的单词表示一幅图像。

Bag-of-words模型应用三步

接下来,我们通过上述图像展示如何通过Bag-of-words模型,将图像表示成数值向量。现在有三个目标类,分别是人脸、自行车和吉他。

Bag-of-words模型的第一步是利用SIFT算法,从每类图像中提取视觉词汇,将所有的视觉词汇集合在一起,如下图3所示:

SIFT算法的应用--目标识别之Bag-of-words模型

图3 从每类图像中提取视觉词汇

第二步是利用K-Means算法构造单词表。K-Means算法是一种基于样本间相似性度量的间接聚类方法,此算法以K为参数,把N个对象分为K个簇,以使簇内具有较高的相似度,而簇间相似度较低。SIFT提取的视觉词汇向量之间根据距离的远近,可以利用K-Means算法将词义相近的词汇合并,作为单词表中的基础词汇,假定我们将K设为4,那么单词表的构造过程如下图4所示:SIFT算法的应用--目标识别之Bag-of-words模型

图4 利用K-Means算法构造单词表

第三步是利用单词表的中词汇表示图像。利用SIFT算法,可以从每幅图像中提取很多个特征点,这些特征点都可以用单词表中的单词近似代替,通过统计单词表中每个单词在图像中出现的次数,可以将图像表示成为一个K=4维数值向量。请看下图5:SIFT算法的应用--目标识别之Bag-of-words模型

图5 每幅图像的直方图表示

上图5中,我们从人脸、自行车和吉他三个目标类图像中提取出的不同视觉词汇,而构造的词汇表中,会把词义相近的视觉词汇合并为同一类,经过合并,词汇表中只包含了四个视觉单词,分别按索引值标记为1,2,3,4。通过观察可以看到,它们分别属于自行车、人脸、吉他、人脸类。统计这些词汇在不同目标类中出现的次数可以得到每幅图像的直方图表示(我们假定存在误差,实际情况亦不外如此):

人脸:  [3,30,3,20]
自行车:[20,3,3,2]
吉他:  [8,12,32,7]

其实这个过程非常简单,就是针对人脸、自行车和吉他这三个文档,抽取出相似的部分(或者词义相近的视觉词汇合并为同一类),构造一个词典,词典中包含4个视觉单词,即Dictionary = {1:”自行车”, 2. “人脸”, 3. “吉他”, 4. “人脸类”},最终人脸、自行车和吉他这三个文档皆可以用一个4维向量表示,最后根据三个文档相应部分出现的次数画成了上面对应的直方图。

需要说明的是,以上过程只是针对三个目标类非常简单的一个示例,实际应用中,为了达到较好的效果,单词表中的词汇数量K往往非常庞大,并且目标类数目越多,对应的K值也越大,一般情况下,K的取值在几百到上千,在这里取K=4仅仅是为了方便说明。

下面,我们再来总结一下如何利用Bag-of-words模型将一幅图像表示成为数值向量:

  • 第一步:利用SIFT算法从不同类别的图像中提取视觉词汇向量,这些向量代表的是图像中局部不变的特征点;
  • 第二步:将所有特征点向量集合到一块,利用K-Means算法合并词义相近的视觉词汇,构造一个包含K个词汇的单词表;
  • 第三步:统计单词表中每个单词在图像中出现的次数,从而将图像表示成为一个K维数值向量。

下面我们按照以上步骤,用C++一步步实现上述过程。

  • C++逐步实现Bag-of-words模型表示一幅图像

在具体编码之前,我们需要事先搭配开发环境。

一. 搭建开发环境

使用的开发平台是windows xp sp3 + vs2010(windows xp sp3 + vc6.0的情况,请参考此文:九(续)、sift算法的编译与实现

1. 从Rob Hess的个人主页:http://blogs.oregonstate.edu/hess/code/sift/,下载最新版本的sift开源库源代码sift-latest_win.zip;

2. 由于sift-latest_win.zip 要求的opencv版本是2.0以上,也下载最新版本 OpenCV-2.2.0-win32-vs2010.exe,运行安装程序将opencv安装在本地某路径下。例如,我安装在D盘根目录下。SIFT算法的应用--目标识别之Bag-of-words模型

3. 运行vs2010,建立一个空的控制台应用程序,取名bow。

4. 配置opencv环境。在vs2010下选择project菜单下的bow property子菜单,调出bow property pages对话框,需要配置的地方有三处:在vc++ Directory选项里需要配置Include Directories和Library Directories,在Linker选项卡的Input选项里需要配置Additional Dependencies。

SIFT算法的应用--目标识别之Bag-of-words模型

至此,开发环境全部搭建并配置完毕。

二.创建c++类CSIFTDiscriptor

为了方便使用,我们将SIFT库用C++类CSIFTDiscriptor封装,该类可以计算并获取指定图像的特征点向量集合。类的声名在SIFTDiscriptor.h文件中,内容如下:

  1. #ifndef _SIFT_DISCRIPTOR_H_
  2. #define _SIFT_DISCRIPTOR_H_
  3. #include <string>
  4. #include <highgui.h>
  5. #include <cv.h>
  6. extern "C"
  7. {
  8. #include "../sift/sift.h"
  9. #include "../sift/imgfeatures.h"
  10. #include "../sift/utils.h"
  11. };
  12. class CSIFTDiscriptor
  13. {
  14. public:
  15. int GetInterestPointNumber()
  16. {
  17. return m_nInterestPointNumber;
  18. }
  19. struct feature *GetFeatureArray()
  20. {
  21. return m_pFeatureArray;
  22. }
  23. public :
  24. void SetImgName(const std::string &strImgName)
  25. {
  26. m_strInputImgName = strImgName;
  27. }
  28. int CalculateSIFT();
  29. public:
  30. CSIFTDiscriptor(const std::string &strImgName);
  31. CSIFTDiscriptor()
  32. {
  33. m_nInterestPointNumber = 0;
  34. m_pFeatureArray = NULL;
  35. }
  36. ~CSIFTDiscriptor();
  37. private:
  38. std::string m_strInputImgName;
  39. int m_nInterestPointNumber;
  40. feature *m_pFeatureArray;
  41. };
  42. #endif

成员函数实现在SIFTDiscriptor.cpp文件中,其中,CalculateSIFT函数完成特征点的提取和计算,其主要内部流程如下:

1) 调用OpenCV函数cvLoadImage加载输入图像;

2) 为了统一输入图像的尺寸,CalculateSIFT函数的第二步是调整输入图像的尺寸,这通过调用cvResize函数实现;

3) 如果输入图像是彩色图像,我们需要首先将其转化成灰度图,这通过调用cvCvtColor函数实现;

4) 调用SIFT库函数sift_feature获取输入图像的特征点向量集合和特征点个数。

  1. #include "SIFTDiscriptor.h"
  2. int CSIFTDiscriptor::CalculateSIFT()
  3. {
  4. IplImage *pInputImg = cvLoadImage(m_strInputImgName.c_str());
  5. if (!pInputImg)
  6. {
  7. return -1;
  8. }
  9. int nImgWidth = 320;    //训练用标准图像大小
  10. double dbScaleFactor = pInputImg->width / 300.0;    //缩放因子
  11. IplImage *pTmpImg = cvCreateImage(cvSize(pInputImg->width / dbScaleFactor, pInputImg->height / dbScaleFactor),
  12. pInputImg->depth, pInputImg->nChannels);
  13. cvResize(pInputImg, pTmpImg);    //缩放
  14. cvReleaseImage(&pInputImg);
  15. if (pTmpImg->nChannels != 1)    //非灰度图
  16. {
  17. IplImage *pGrayImg = cvCreateImage(cvSize(pTmpImg->width, pTmpImg->height),
  18. pTmpImg->depth, 1);
  19. cvCvtColor(pTmpImg, pGrayImg, CV_RGB2GRAY);
  20. m_nInterestPointNumber = sift_features(pGrayImg, &m_pFeatureArray);
  21. cvReleaseImage(&pGrayImg);
  22. }
  23. else
  24. {
  25. m_nInterestPointNumber = sift_features(pTmpImg, &m_pFeatureArray);
  26. }
  27. cvReleaseImage(&pTmpImg);
  28. return m_nInterestPointNumber;
  29. }
  30. CSIFTDiscriptor::CSIFTDiscriptor(const std::string &strImgName)
  31. {
  32. m_strInputImgName = strImgName;
  33. m_nInterestPointNumber = 0;
  34. m_pFeatureArray = NULL;
  35. CalculateSIFT();
  36. }
  37. CSIFTDiscriptor::~CSIFTDiscriptor()
  38. {
  39. if (m_pFeatureArray)
  40. {
  41. free(m_pFeatureArray);
  42. }
  43. }

三.创建c++类CImgSet,管理实验图像集合

Bag-of-words模型需要从多个目标类图像中提取视觉词汇,不同目标类的图像存储在不同子文件夹中,为了方便操作,我们设计了一个专门的类CImgSet用来管理图像集合,声明在文件ImgSet.h中:

  1. #ifndef _IMG_SET_H_
  2. #define _IMG_SET_H_
  3. #include <vector>
  4. #include <string>
  5. #pragma comment(lib, "shlwapi.lib")
  6. class CImgSet
  7. {
  8. public:
  9. CImgSet (const std::string &strImgDirName) : m_strImgDirName(strImgDirName+"//"), m_nImgNumber(0){}
  10. int GetTotalImageNumber()
  11. {
  12. return m_nImgNumber;
  13. }
  14. std::string GetImgName(int nIndex)
  15. {
  16. return m_szImgs.at(nIndex);
  17. }
  18. int LoadImgsFromDir()
  19. {
  20. return LoadImgsFromDir("");
  21. }
  22. private:
  23. int LoadImgsFromDir(const std::string &strDirName);
  24. private:
  25. typedef std::vector <std::string> IMG_SET;
  26. IMG_SET m_szImgs;
  27. int m_nImgNumber;
  28. const std::string m_strImgDirName;
  29. };
  30. #endif
  31. //成员函数实现在文件ImgSet.cpp中:
  32. #include "ImgSet.h"
  33. #include <windows.h>
  34. #include <Shlwapi.h>
  35. /**
  36. strSubDirName:子文件夹名
  37. */
  38. int CImgSet::LoadImgsFromDir(const std::string &strSubDirName)
  39. {
  40. WIN32_FIND_DATAA stFD = {0};
  41. std::string strDirName;
  42. if ("" == strSubDirName)
  43. {
  44. strDirName = m_strImgDirName;
  45. }
  46. else
  47. {
  48. strDirName = strSubDirName;
  49. }
  50. std::string strFindName = strDirName + "//*";
  51. HANDLE hFile = FindFirstFileA(strFindName.c_str(), &stFD);
  52. BOOL bExist = FindNextFileA(hFile, &stFD);
  53. for (;bExist;)
  54. {
  55. std::string strTmpName = strDirName + stFD.cFileName;
  56. if (strDirName + "." == strTmpName || strDirName + ".." == strTmpName)
  57. {
  58. bExist = FindNextFileA(hFile, &stFD);
  59. continue;
  60. }
  61. if (PathIsDirectoryA(strTmpName.c_str()))
  62. {
  63. strTmpName += "//";
  64. LoadImgsFromDir(strTmpName);
  65. bExist = FindNextFileA(hFile, &stFD);
  66. continue;
  67. }
  68. std::string strSubImg = strDirName + stFD.cFileName;
  69. m_szImgs.push_back(strSubImg);
  70. bExist = FindNextFileA(hFile, &stFD);
  71. }
  72. m_nImgNumber = m_szImgs.size();
  73. return m_nImgNumber;
  74. }

LoadImgsFromDir递归地从图像文件夹中获取所有实验用图像名,包括子文件夹。该函数内部通过循环调用windows API函数FindFirstFile和FindNextFile来找到文件夹中所有图像的名称。

四.创建CHistogram,生成图像的直方图表示

  1. //ImgHistogram.h
  2. #ifndef _IMG_HISTOGRAM_H_
  3. #define _IMG_HISTOGRAM_H_
  4. #include <string>
  5. #include "SIFTDiscriptor.h"
  6. #include "ImgSet.h"
  7. const int cnClusterNumber = 1500;
  8. const int ciMax_D = FEATURE_MAX_D;
  9. class CHistogram
  10. {
  11. public:
  12. void SetTrainingImgSetName(const std::string strTrainingImgSet)
  13. {
  14. m_strTrainingImgSetName = strTrainingImgSet;
  15. }
  16. int FormHistogram();
  17. CvMat CalculateImgHistogram(const string strImgName, int pszImgHistogram[]);
  18. CvMat *GetObservedData();
  19. CvMat *GetCodebook()
  20. {
  21. return m_pCodebook;
  22. }
  23. void SetCodebook(CvMat *pCodebook)
  24. {
  25. m_pCodebook = pCodebook;
  26. m_bSet = true;
  27. }
  28. public:
  29. CHistogram():m_pszHistogram(0), m_nImgNumber(0), m_pObservedData(0), m_pCodebook(0), m_bSet(false){}
  30. ~CHistogram()
  31. {
  32. if (m_pszHistogram)
  33. {
  34. delete m_pszHistogram;
  35. m_pszHistogram = 0;
  36. }
  37. if (m_pObservedData)
  38. {
  39. cvReleaseMat(&m_pObservedData);
  40. m_pObservedData = 0;
  41. }
  42. if (m_pCodebook && !m_bSet)
  43. {
  44. cvReleaseMat(&m_pCodebook);
  45. m_pCodebook = 0;
  46. }
  47. }
  48. private :
  49. bool m_bSet;
  50. CvMat *m_pCodebook;
  51. CvMat *m_pObservedData;
  52. std::string m_strTrainingImgSetName;
  53. int (*m_pszHistogram)[cnClusterNumber];
  54. int m_nImgNumber;
  55. };
  56. #endif
  57. #include "ImgHistogram.h"
  58. int CHistogram::FormHistogram()
  59. {
  60. int nRet = 0;
  61. CImgSet iImgSet(m_strTrainingImgSetName);
  62. nRet = iImgSet.LoadImgsFromDir();
  63. const int cnTrainingImgNumber = iImgSet.GetTotalImageNumber();
  64. m_nImgNumber = cnTrainingImgNumber;
  65. CSIFTDiscriptor *pDiscriptor = new CSIFTDiscriptor[cnTrainingImgNumber];
  66. int nIPNumber(0) ;
  67. for (int i = 0; i < cnTrainingImgNumber; ++i)  //计算每一幅训练图像的SIFT描述符
  68. {
  69. const string strImgName = iImgSet.GetImgName(i);
  70. pDiscriptor[i].SetImgName(strImgName);
  71. pDiscriptor[i].CalculateSIFT();
  72. nIPNumber += pDiscriptor[i].GetInterestPointNumber();
  73. }
  74. double (*pszDiscriptor)[FEATURE_MAX_D] = new double[nIPNumber][FEATURE_MAX_D];  //存储所有描述符的数组。每一行代表一个IP的描述符
  75. ZeroMemory(pszDiscriptor, sizeof(int) * nIPNumber * FEATURE_MAX_D);
  76. int nIndex = 0;
  77. for (int i = 0; i < cnTrainingImgNumber; ++i)  //遍历所有图像
  78. {
  79. struct feature *pFeatureArray = pDiscriptor[i].GetFeatureArray();
  80. int nFeatureNumber = pDiscriptor[i].GetInterestPointNumber();
  81. for (int j = 0; j < nFeatureNumber; ++j)  //遍历一幅图像中所有的IP(Interesting Point兴趣点
  82. {
  83. for (int k = 0; k < FEATURE_MAX_D; k++)//初始化一个IP描述符
  84. {
  85. pszDiscriptor[nIndex][k] = pFeatureArray[j].descr[k];
  86. }
  87. ++nIndex;
  88. }
  89. }
  90. CvMat *pszLabels = cvCreateMat(nIPNumber, 1, CV_32SC1);
  91. //对所有IP的描述符,执行KMeans算法,找到cnClusterNumber个聚类中心,存储在pszClusterCenters中
  92. if (!m_pCodebook)   //构造码元表
  93. {
  94. CvMat szSamples,
  95. *pszClusterCenters = cvCreateMat(cnClusterNumber, FEATURE_MAX_D, CV_32FC1);
  96. cvInitMatHeader(&szSamples, nIPNumber, FEATURE_MAX_D, CV_32FC1, pszDiscriptor);
  97. cvKMeans2(&szSamples, cnClusterNumber, pszLabels,
  98. cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 ),
  99. 1, (CvRNG *)0, 0, pszClusterCenters);  //
  100. m_pCodebook = pszClusterCenters;
  101. }
  102. m_pszHistogram = new int[cnTrainingImgNumber][cnClusterNumber];  //存储每幅图像的直方图表示,每一行对应一幅图像
  103. ZeroMemory(m_pszHistogram, sizeof(int) * cnTrainingImgNumber * cnClusterNumber);
  104. //计算每幅图像的直方图
  105. nIndex = 0;
  106. for (int i = 0; i < cnTrainingImgNumber; ++i)
  107. {
  108. struct feature *pFeatureArray = pDiscriptor[i].GetFeatureArray();
  109. int nFeatureNumber = pDiscriptor[i].GetInterestPointNumber();
  110. //      int nIndex = 0;
  111. for (int j = 0; j < nFeatureNumber; ++j)
  112. {
  113. //          CvMat szFeature;
  114. //          cvInitMatHeader(&szFeature, 1, FEATURE_MAX_D, CV_32FC1, pszDiscriptor[nIndex++]);
  115. //          double dbMinimum = 1.79769e308;
  116. //          int nCodebookIndex = 0;
  117. //          for (int k = 0; k < m_pCodebook->rows; ++k)//找到距离最小的码元,用最小码元代替原//来的词汇
  118. //          {
  119. //              CvMat szCode = cvMat(1, m_pCodebook->cols, m_pCodebook->type);
  120. //              cvGetRow(m_pCodebook, &szCode, k);
  121. //              double dbDistance = cvNorm(&szFeature, &szCode, CV_L2);
  122. //              if (dbDistance < dbMinimum)
  123. //              {
  124. //                  dbMinimum = dbDistance;
  125. //                  nCodebookIndex = k;
  126. //              }
  127. //          }
  128. int nCodebookIndex = pszLabels->data.i[nIndex++];   //找到第i幅图像中第j个IP在Codebook中的索引值nCodebookIndex
  129. ++m_pszHistogram[i][nCodebookIndex];   //0<nCodebookIndex<cnClusterNumber;
  130. }
  131. }
  132. //资源清理,函数返回
  133. //  delete []m_pszHistogram;
  134. //  m_pszHistogram = 0;
  135. cvReleaseMat(&pszLabels);
  136. //  cvReleaseMat(&pszClusterCenters);
  137. delete []pszDiscriptor;
  138. delete []pDiscriptor;
  139. return nRet;
  140. }
  141. //double descr_dist_sq( struct feature* f1, struct feature* f2 );
  142. CvMat CHistogram::CalculateImgHistogram(const string strImgName, int pszImgHistogram[])
  143. {
  144. if ("" == strImgName || !m_pCodebook || !pszImgHistogram)
  145. {
  146. return CvMat();
  147. }
  148. CSIFTDiscriptor iImgDisp;
  149. iImgDisp.SetImgName(strImgName);
  150. iImgDisp.CalculateSIFT();
  151. struct feature *pImgFeature = iImgDisp.GetFeatureArray();
  152. int cnIPNumber = iImgDisp.GetInterestPointNumber();
  153. //  int *pszImgHistogram = new int[cnClusterNumber];
  154. //  ZeroMemory(pszImgHistogram, sizeof(int)*cnClusterNumber);
  155. for (int i = 0; i < cnIPNumber; ++i)
  156. {
  157. double *pszDistance = new double[cnClusterNumber];
  158. CvMat iIP = cvMat(FEATURE_MAX_D, 1, CV_32FC1, pImgFeature[i].descr);
  159. for (int j = 0; j < cnClusterNumber; ++j)
  160. {
  161. CvMat iCode = cvMat(1, FEATURE_MAX_D, CV_32FC1);
  162. cvGetRow(m_pCodebook, &iCode, j);
  163. CvMat *pTmpMat = cvCreateMat(FEATURE_MAX_D, 1, CV_32FC1);
  164. cvTranspose(&iCode, pTmpMat);
  165. double dbDistance = cvNorm(&iIP, pTmpMat);  //计算第i个IP与第j个code之间的距离
  166. pszDistance[j] = dbDistance;
  167. cvReleaseMat(&pTmpMat);
  168. }
  169. double dbMinDistance = pszDistance[0];
  170. int nCodebookIndex = 0;  //第i个IP在codebook中距离最小的code的索引值
  171. for (int j = 1; j < cnClusterNumber; ++j)
  172. {
  173. if (dbMinDistance > pszDistance[j])
  174. {
  175. dbMinDistance = pszDistance[j];
  176. nCodebookIndex = j;
  177. }
  178. }
  179. ++pszImgHistogram[nCodebookIndex];
  180. delete []pszDistance;
  181. }
  182. CvMat iImgHistogram = cvMat(cnClusterNumber, 1, CV_32SC1, pszImgHistogram);
  183. return iImgHistogram;
  184. }
  185. CvMat *CHistogram::GetObservedData()
  186. {
  187. CvMat iHistogram;
  188. cvInitMatHeader(&iHistogram, m_nImgNumber, cnClusterNumber, CV_32SC1, m_pszHistogram);
  189. CvMat *m_pObservedData = cvCreateMat(iHistogram.cols, iHistogram.rows, CV_32SC1);
  190. cvTranspose(&iHistogram, m_pObservedData);
  191. return m_pObservedData;
  192. }
本文完。

版权所有,侵权必究。严禁用于任何商业用途,转载请注明出处。

SIFT算法的应用--目标识别之Bag-of-words模型的更多相关文章

  1. SIFT算法详解(转)

    http://blog.csdn.net/zddblog/article/details/7521424 目录(?)[-] 尺度不变特征变换匹配算法详解 Scale Invariant Feature ...

  2. 【转】 SIFT算法详解

    尺度不变特征变换匹配算法详解Scale Invariant Feature Transform(SIFT)Just For Fun zdd  zddmail@gmail.com 对于初学者,从Davi ...

  3. 《sift算法详解》阅读笔记

    原博客来自:http://blog.csdn.net/zddblog/article/details/7521424 定义: 尺度不变特征转化是一种计算机视觉算法,用于侦测和描述物体的局部性特征,在空 ...

  4. SIFT算法详解

    尺度不变特征变换匹配算法详解Scale Invariant Feature Transform(SIFT)Just For Fun zdd  zddmail@gmail.com or (zddhub@ ...

  5. 转:sift算法详解

    转自:http://blog.csdn.net/pi9nc/article/details/23302075 对于初学者,从David G.Lowe的论文到实现,有许多鸿沟,本文帮你跨越. 1.SIF ...

  6. SIFT算法详解&lpar;转&rpar;

    原文地址 http://blog.csdn.net/pi9nc/article/details/23302075 尺度不变特征变换匹配算法详解 Scale Invariant Feature Tran ...

  7. OpenCV计算机视觉学习(13)——图像特征点检测(Harris角点检测,sift算法)

    如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 前言 ...

  8. SIFT算法:特征描述子

    SIFT算法:DoG尺度空间生产  SIFT算法:KeyPoint找寻.定位与优化 SIFT算法:确定特征点方向  SIFT算法:特征描述子 目录: 1.确定描述子采样区域 2.生成描述子 2.1 旋 ...

  9. SIFT算法:确定特征点方向

    SIFT算法:DoG尺度空间生产  SIFT算法:KeyPoint找寻.定位与优化 SIFT算法:确定特征点方向  SIFT算法:特征描述子 目录: 1.计算邻域梯度方向和幅值 2.计算梯度方向直方图 ...

随机推荐

  1. visualstudio 2013 mysql entityframeword :实体模型无法添加,闪退

    发现电脑中安装的mysql-connector-net,版本为6.9.8 1.卸载此版本 2.重新安装mysql-connector-net 6.8.3 3.注意web.config中版本 4.注意项 ...

  2. Android安全开发之WebView中的地雷

    Android安全开发之WebView中的地雷 0X01 About WebView 在Android开发中,经常会使用WebView来实现WEB页面的展示,在Activiry中启动自己的浏览器,或者 ...

  3. 交换两个变量的值swap&lpar;a&comma;b&rpar;

    方法一:使用第三方变量 , b = , temp; temp = a; a = b; b = temp; 方法二:变量加减法(即121,加减减) , b = ; a = a + b; b = a - ...

  4. SharePoint 2013 App Remote Event Receivers

    当我们在使用App的时候,就会发现一些问题,比如那些网站部署.更新或者卸载了,我们很关心我们的App是否有人用,这就需要远程事件接收器了. 1.在我们的测试App的解决方案上选中鼠标,按F4弹出属性, ...

  5. dalvik虚拟内存管理之三——调试信息

    转载自http://www.miui.com/thread-75063-1-1.html 1. verbosegc一般Java虚拟机要求支持verbosegc选项,输出详细的垃圾收集调试信息.dalv ...

  6. 视频运动motion vector获取。

    http://victor.csie.org/blog/archives/362------------------------------------------------------------ ...

  7. 自己动手写Redis客户端(C&num;实现)4 - 整数回复

    整数回复 整数回复就是一个以 ":" 开头, CRLF 结尾的字符串表示的整数. 比如说, ":0\r\n" 和 ":1000\r\n" 都 ...

  8. FTP文件上传到HDFS上

    在做测试数据时,往往会有ftp数据上传到hdfs的需求,一般需要手动操作,这样做太费事,于是有了下边代码实现的方式: ftp数据上传到hdfs函数: import java.io.InputStrea ...

  9. 拍拍熊(APT-C-37),诱导方式、DNS、安卓远控

    诱导方式 1.含有正常APP功能的伪装 2.文件图标伪装 RAR 1.Android DroidJack SpyNote Windows njRAT njRAT[2]又称Bladabindi,通过控制 ...

  10. weixinShare&period;js &sol; 极简微信分享插件

    weixinShare.js / 极简微信分享插件 / 版本:0.1 这是一个很简单.很实用的微信分享插件,无需jQuery,只需要在网页里加入一行JS代码,即可自动识别微信浏览器并启动微信分享的提示 ...