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

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

原文: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.   
  7. extern “C”  
  8. {     
  9. #include “../sift/sift.h”     
  10. #include “../sift/imgfeatures.h”      
  11. #include “../sift/utils.h”    
  12. };  
  13.   
  14. class CSIFTDiscriptor  
  15. {     
  16. public:   
  17.     int GetInterestPointNumber()          
  18.     {         
  19.         return m_nInterestPointNumber;    
  20.     }     
  21.     struct feature *GetFeatureArray()         
  22.     {         
  23.         return m_pFeatureArray;       
  24.     }  
  25.     public :          
  26.         void SetImgName(const std::string &strImgName)        
  27.         {         
  28.             m_strInputImgName = strImgName;       
  29.         }     
  30.         int CalculateSIFT();  
  31.     public:   
  32.         CSIFTDiscriptor(const std::string &strImgName);   
  33.         CSIFTDiscriptor()         
  34.         {         
  35.             m_nInterestPointNumber = 0;  
  36.             m_pFeatureArray = NULL;       
  37.         }     
  38.         ~CSIFTDiscriptor();  
  39.     private:          
  40.         std::string m_strInputImgName;    
  41.         int m_nInterestPointNumber;   
  42.         feature *m_pFeatureArray;     
  43. };  
  44. #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.       
  16.     if (pTmpImg->nChannels != 1)    //非灰度图  
  17.     {  
  18.         IplImage *pGrayImg = cvCreateImage(cvSize(pTmpImg->width, pTmpImg->height),  
  19.             pTmpImg->depth, 1);  
  20.         cvCvtColor(pTmpImg, pGrayImg, CV_RGB2GRAY);  
  21.         m_nInterestPointNumber = sift_features(pGrayImg, &m_pFeatureArray);  
  22.         cvReleaseImage(&pGrayImg);  
  23.     }  
  24.     else  
  25.     {  
  26.         m_nInterestPointNumber = sift_features(pTmpImg, &m_pFeatureArray);  
  27.     }  
  28.     cvReleaseImage(&pTmpImg);  
  29.     return m_nInterestPointNumber;  
  30. }  
  31. CSIFTDiscriptor::CSIFTDiscriptor(const std::string &strImgName)  
  32. {  
  33.     m_strInputImgName = strImgName;  
  34.     m_nInterestPointNumber = 0;  
  35.     m_pFeatureArray = NULL;  
  36.     CalculateSIFT();  
  37. }  
  38. CSIFTDiscriptor::~CSIFTDiscriptor()  
  39. {  
  40.     if (m_pFeatureArray)  
  41.     {  
  42.         free(m_pFeatureArray);  
  43.     }  
  44. }  

 

三.创建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.   
  32. //成员函数实现在文件ImgSet.cpp中:  
  33. #include “ImgSet.h”  
  34. #include <windows.h>  
  35. #include <Shlwapi.h>  
  36. /** 
  37. strSubDirName:子文件夹名 
  38. */  
  39. int CImgSet::LoadImgsFromDir(const std::string &strSubDirName)  
  40. {  
  41.     WIN32_FIND_DATAA stFD = {0};  
  42.     std::string strDirName;  
  43.     if (“” == strSubDirName)  
  44.     {  
  45.         strDirName = m_strImgDirName;  
  46.     }  
  47.     else  
  48.     {  
  49.         strDirName = strSubDirName;  
  50.     }  
  51.     std::string strFindName = strDirName + “//*”;  
  52.     HANDLE hFile = FindFirstFileA(strFindName.c_str(), &stFD);  
  53.     BOOL bExist = FindNextFileA(hFile, &stFD);  
  54.       
  55.     for (;bExist;)  
  56.     {  
  57.         std::string strTmpName = strDirName + stFD.cFileName;  
  58.         if (strDirName + “.” == strTmpName || strDirName + “..” == strTmpName)  
  59.         {  
  60.             bExist = FindNextFileA(hFile, &stFD);  
  61.             continue;  
  62.         }  
  63.         if (PathIsDirectoryA(strTmpName.c_str()))  
  64.         {  
  65.             strTmpName += “//”;  
  66.             LoadImgsFromDir(strTmpName);  
  67.             bExist = FindNextFileA(hFile, &stFD);  
  68.             continue;  
  69.         }  
  70.         std::string strSubImg = strDirName + stFD.cFileName;  
  71.         m_szImgs.push_back(strSubImg);  
  72.         bExist = FindNextFileA(hFile, &stFD);  
  73.     }  
  74.     m_nImgNumber = m_szImgs.size();  
  75.     return m_nImgNumber;  
  76. }  

 

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

 

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

 

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

 

本文完。

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

转载于:https://www.cnblogs.com/zhizhan/p/4445330.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/109480.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • SMOTE算法代码实现

    SMOTE算法代码实现类别不平衡问题类别不平衡问题,顾名思义,即数据集中存在某一类样本,其数量远多于或远少于其他类样本,从而导致一些机器学习模型失效的问题。例如逻辑回归即不适合处理类别不平衡问题,例如逻辑回归在欺诈检测问题中,因为绝大多数样本都为正常样本,欺诈样本很少,逻辑回归算法会倾向于把大多数样本判定为正常样本,这样能达到很高的准确率,但是达不到很高的召回率。类别不平衡问题在很多场景中存在,例如欺诈检测,风控识…

  • 知识蒸馏(Knowledge Distillation)

    知识蒸馏(Knowledge Distillation)1、DistillingtheKnowledgeinaNeuralNetworkHinton的文章"DistillingtheKnowledgeinaNeuralNetwork"首次提出了知识蒸馏(暗知识提取)的概念,通过引入与教师网络(teachernetwork:复杂、但推理性能优越)相关的软目标(soft-target)作为totalloss的一部分,以诱导学…

  • 独立成分分析(ICA)

    独立成分分析(ICA)1.概念独立成分分析是从多元(多维)统计数据中寻找潜在因子或成分的一种方法.ICA与其它的方法重要的区别在于,它寻找满足统计独立和非高斯的成分。这里我们简要介绍ICA的基本概念、应用和估计原理。1.1 多元数据的线性表示统计数据处理及相关领域中的一个重要和需要长期研究的问题就…

  • 如果没人挖矿比特币会变成什么样[通俗易懂]

    如果没人挖矿比特币会变成什么样[通俗易懂]如果没人挖矿,比特币网络就无法出块,也无法转账,比特币网络将不复存在。当然这种情况并不会存在,因为出块意味着获得比特币奖励。只要比特币有价格就有人会投入成本来挖矿,并卖出挖到的比特币。有人使用比特币,从而进一步加强比特币的价值,且使用量越来越大。比特币网络就将一直存在。那么比特币网络有可能被摧毁吗。我们知道区块链是比特币的底层技术,而区块链具有不可篡改,数据可查的属性。那么摧毁比特币网络就只…

  • windows bat批处理基础命令学习教程「建议收藏」

    windows bat批处理基础命令学习教程「建议收藏」windowsbat批处理基础命令学习教程

  • java copyproperties_java中 BeanUtils.copyProperties的用法

    java copyproperties_java中 BeanUtils.copyProperties的用法BeanUtils提供了对java发射和自省API的包装,这里对BeanUtils.copyProperties的用法做一个小小的介绍。通过函数名可以知道,copyProperties函数是对属性进行拷贝,该函数有两个参数,一个是原始的数据,另一个是接收这些属性的数据。这里给大家介绍一个我遇到的一个坑:在不同的jar包中,该函数的两个参数的位置不一样,有一种是copyProperties(java…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号