乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      特征點(diǎn)檢測(cè)學(xué)習(xí)_2(surf算法)

       Jack_WangChong 2016-06-06

        

        在上篇博客特征點(diǎn)檢測(cè)學(xué)習(xí)_1(sift算法) 中簡(jiǎn)單介紹了經(jīng)典的sift算法,sift算法比較穩(wěn)定,檢測(cè)到的特征點(diǎn)也比較多,其最大的確定是計(jì)算復(fù)雜度較高。后面有不少學(xué)者對(duì)其進(jìn)行了改進(jìn),其中比較出名的就是本文要介紹的surf算法,surf的中文意思為快速魯棒特征。本文不是專門介紹surf所有理論(最好的理論是作者的論文)的,只是對(duì)surf算法進(jìn)行了下整理,方便以后查閱。

        網(wǎng)上有些文章對(duì)surf做了介紹,比如:

        http://wuzizhang.blog.163.com/blog/static/78001208201138102648854/

        surf算法原理,有一些簡(jiǎn)單介紹.

        http://blog.csdn.net/andkobe/article/details/5778739

        對(duì)surf的某些細(xì)節(jié)做了通俗易懂的解釋.

        http://www./DancingWind/

        這篇文章名字叫做《surf原文翻譯》,寫得非常好,看完會(huì)對(duì)surf中采用的一些技術(shù)更加深入的理解,不過本文卻不是翻譯英文的,而是該作者自己的理解,對(duì)積分圖,Hessian矩陣等引入的原因都做了通俗的解釋,推薦一看。

       

       

          一、Surf描述子形成步驟

               1. 構(gòu)造高斯金字塔尺度空間

               其實(shí)surf構(gòu)造的金字塔圖像與sift有很大不同,就是因?yàn)檫@些不同才加快了其檢測(cè)的速度。Sift采用的是DOG圖像,而surf采用的是Hessian矩陣行列式近似值圖像。首先來看看圖像中某個(gè)像素點(diǎn)的Hessian矩陣,如下:

                

               即每一個(gè)像素點(diǎn)都可以求出一個(gè)Hessian矩陣。但是由于我們的特征點(diǎn)需要具備尺度無關(guān)性,所以在進(jìn)行Hessian矩陣構(gòu)造前,需要對(duì)其進(jìn)行高斯濾波。這樣,經(jīng)過濾波后在進(jìn)行Hessian的計(jì)算,其公式如下:

         

               公式中的符號(hào),估計(jì)有點(diǎn)數(shù)學(xué)基礎(chǔ)的朋友都能夠猜到,這里就不多解釋了。

               最終我們要的是原圖像的一個(gè)變換圖像,因?yàn)槲覀円谶@個(gè)變換圖像上尋找特征點(diǎn),然后將其位置反映射到原圖中,例如在sift中,我們是在原圖的DOG圖上尋找特征點(diǎn)的。那么在surf中,這個(gè)變換圖是什么呢?從surf的眾多資料來看,就是原圖每個(gè)像素的Hessian矩陣行列式的近似值構(gòu)成的。其行列式近似公式如下:

                

               其中0.9是作者給出的一個(gè)經(jīng)驗(yàn)值,其實(shí)它是有一套理論計(jì)算的,具體去看surf的英文論文。

               由于求Hessian時(shí)要先高斯平滑,然后求二階導(dǎo)數(shù),這在離散的像素點(diǎn)是用模板卷積形成的,這2中操作合在一起用一個(gè)模板代替就可以了,比如說y方向上的模板如下:

                  

               該圖的左邊即用高斯平滑然后在y方向上求二階導(dǎo)數(shù)的模板,為了加快運(yùn)算用了近似處理,其處理結(jié)果如右圖所示,這樣就簡(jiǎn)化了很多。并且右圖可以采用積分圖來運(yùn)算,大大的加快了速度,關(guān)于積分圖的介紹,可以去查閱相關(guān)的資料。

               同理,x和y方向的二階混合偏導(dǎo)模板如下所示:

          

       

        上面講的這么多只是得到了一張近似hessian行列式圖,這例比sift中的DOG圖,但是在金字塔圖像中分為很多層,每一層叫做一個(gè)octave,每一個(gè)octave中又有幾張尺度不同的圖片。在sift算法中,同一個(gè)octave層中的圖片尺寸(即大小)相同,但是尺度(即模糊程度)不同,而不同的octave層中的圖片尺寸大小也不相同,因?yàn)樗怯缮弦粚訄D片降采樣得到的。在進(jìn)行高斯模糊時(shí),sift的高斯模板大小是始終不變的,只是在不同的octave之間改變圖片的大小。而在surf中,圖片的大小是一直不變的,不同的octave層得到的待檢測(cè)圖片是改變高斯模糊尺寸大小得到的,當(dāng)然了,同一個(gè)octave中個(gè)的圖片用到的高斯模板尺度也不同。Surf采用這種方法節(jié)省了降采樣過程,其處理速度自然也就提上去了。其金字塔圖像如下所示:

        

          

       

        2. 利用非極大值抑制初步確定特征點(diǎn)

        此步驟和sift類似,將經(jīng)過hessian矩陣處理過的每個(gè)像素點(diǎn)與其3維領(lǐng)域的26個(gè)點(diǎn)進(jìn)行大小比較,如果它是這26個(gè)點(diǎn)中的最大值或者最小值,則保留下來,當(dāng)做初步的特征點(diǎn)。

       

        3. 精確定位極值點(diǎn)

        這里也和sift算法中的類似,采用3維線性插值法得到亞像素級(jí)的特征點(diǎn),同時(shí)也去掉那些值小于一定閾值的點(diǎn)。

       

        4. 選取特征點(diǎn)的主方向。

        這一步與sift也大有不同。Sift選取特征點(diǎn)主方向是采用在特征點(diǎn)領(lǐng)域內(nèi)統(tǒng)計(jì)其梯度直方圖,取直方圖bin值最大的以及超過最大bin值80%的那些方向做為特征點(diǎn)的主方向。而在surf中,不統(tǒng)計(jì)其梯度直方圖,而是統(tǒng)計(jì)特征點(diǎn)領(lǐng)域內(nèi)的harr小波特征。即在特征點(diǎn)的領(lǐng)域(比如說,半徑為6s的圓內(nèi),s為該點(diǎn)所在的尺度)內(nèi),統(tǒng)計(jì)60度扇形內(nèi)所有點(diǎn)的水平haar小波特征和垂直haar小波特征總和,haar小波的尺寸變長(zhǎng)為4s,這樣一個(gè)扇形得到了一個(gè)值。然后60度扇形以一定間隔進(jìn)行旋轉(zhuǎn),最后將最大值那個(gè)扇形的方向作為該特征點(diǎn)的主方向。該過程的示意圖如下:

          

       

        5. 構(gòu)造surf特征點(diǎn)描述算子

        在sift中,是在特征點(diǎn)周圍取16*16的鄰域,并把該領(lǐng)域化為4*4個(gè)的小區(qū)域,每個(gè)小區(qū)域統(tǒng)計(jì)8個(gè)方向梯度,最后得到4*4*8=128維的向量,該向量作為該點(diǎn)的sift描述子。

        在surf中,也是在特征點(diǎn)周圍取一個(gè)正方形框,框的邊長(zhǎng)為20s(s是所檢測(cè)到該特征點(diǎn)所在的尺度)。該框帶方向,方向當(dāng)然就是第4步檢測(cè)出來的主方向了。然后把該框分為16個(gè)子區(qū)域,每個(gè)子區(qū)域統(tǒng)計(jì)25個(gè)像素的水平方向和垂直方向的haar小波特征,這里的水平和垂直方向都是相對(duì)主方向而言的。該haar小波特征為水平方向值之和,水平方向絕對(duì)值之和,垂直方向之和,垂直方向絕對(duì)值之和。該過程的示意圖如下所示:

          

       

        這樣每個(gè)小區(qū)域就有4個(gè)值,所以每個(gè)特征點(diǎn)就是16*4=64維的向量,相比sift而言,少了一半,這在特征匹配過程中會(huì)大大加快匹配速度。

       

       

        二、特征點(diǎn)的匹配過程

        surf特征點(diǎn)的匹配過程和sift類似,這里不做詳細(xì)介紹

       

       

        三、實(shí)驗(yàn)部分

        本次實(shí)驗(yàn)采用網(wǎng)上流行的open surf,用c++完成的,用到了opencv庫(kù),下載地址為:http://www./computer-vision-opensurf.html

        該代碼的作者給出的主函數(shù)實(shí)現(xiàn)了6中功能,包括靜態(tài)圖片特征點(diǎn)的檢測(cè),視頻中特征點(diǎn)的檢測(cè),圖片之間的匹配,視頻與圖片之間的匹配,特征點(diǎn)聚類等6中功能。本次實(shí)驗(yàn)就簡(jiǎn)單的測(cè)試了圖片的檢測(cè),匹配和特征點(diǎn)聚類3個(gè)功能。并加入了簡(jiǎn)單的界面。

        開發(fā)環(huán)境為:opencv2.4.2+Qt4.8.2+open surf+windosxp

        實(shí)驗(yàn)分為下面3個(gè)部分來描述。

       

        Surf特征點(diǎn)檢測(cè)和描述

        打開軟件,單擊Open Image按鈕,選擇一張待檢測(cè)的圖片,效果如下:

        

       

        單擊Surf Detect按鈕,程序會(huì)對(duì)該圖片進(jìn)行特征點(diǎn)檢測(cè),并顯示特征結(jié)果,包括特征點(diǎn)的主方向,尺度等信息。效果如下:

        

        單擊Close 按鈕退出程序。

       

        Surf特征點(diǎn)匹配

        打開軟件,單擊Open Image 按鈕,依次打開2幅待匹配的圖片,這2幅圖片要有相同的內(nèi)容,只是尺度,旋轉(zhuǎn),光照等不同。打開圖片后如下:

        

       

        單擊Surf Detect按鈕,和上面類似,會(huì)先對(duì)圖片進(jìn)行檢測(cè),效果如下:

        

       

        單擊Surf Match 按鈕,程序會(huì)對(duì)檢測(cè)到的圖片進(jìn)行特征點(diǎn)匹配,效果如下:

        

        單擊Close 按鈕退出程序。

       

        Surf特征點(diǎn)聚類

        打開軟件,單擊Open Image 按鈕,選擇一張待特征點(diǎn)分類的圖片,如下所示:

        

       

        單擊Surf Detect按鈕,首先對(duì)該圖片進(jìn)行surf特征點(diǎn)檢測(cè),如下:

        

       

        單擊Kmeans Cluster按鈕,程序會(huì)對(duì)這些特征點(diǎn)集合進(jìn)行聚類,并顯示其結(jié)果,如下所示:

        

         單擊Close 按鈕退出程序。

         

        實(shí)驗(yàn)主要函數(shù)部分及代碼(附錄有工程code下載鏈接):

      opensurf.h:

      復(fù)制代碼
      #ifndef OPENSURF_H
      #define OPENSURF_H
      
      #include <QDialog>
      #include <opencv2/core/core.hpp>
      #include <opencv2/highgui/highgui.hpp>
      #include <opencv2/imgproc/imgproc.hpp>
      #include <opencv2/calib3d/calib3d.hpp>
      #include "ipoint.h"
      #include "kmeans.h"
      
      using namespace cv;
      
      namespace Ui {
      class OpenSurf;
      }
      
      class OpenSurf : public QDialog
      {
          Q_OBJECT
          
      public:
          explicit OpenSurf(QWidget *parent = 0);
          ~OpenSurf();
          
      private slots:
          void on_openButton_clicked();
      
          void on_detectButton_clicked();
      
          void on_matchButton_clicked();
      
          void on_closeButton_clicked();
      
          void on_clusterButton_clicked();
      
      private:
          Ui::OpenSurf *ui;
          IplImage *img1, *img2, *img_match1, *img_match2;
          IpVec ipts, ipts1, ipts2;
          IpPairVec matches;
          Kmeans km;
          int open_image_num;
      
      };
      
      #endif // OPENSURF_H
      復(fù)制代碼

       

      opensurf.cpp:

      復(fù)制代碼
      #include "opensurf.h"
      #include "ui_opensurf.h"
      #include <QtGui>
      #include <QtCore>
      #include "surflib.h"
      
      using namespace std;
      
      OpenSurf::OpenSurf(QWidget *parent) :
          QDialog(parent),
          ui(new Ui::OpenSurf)
      {
          open_image_num = 0;
          ui->setupUi(this);
      }
      
      OpenSurf::~OpenSurf()
      {
          delete ui;
      }
      
      void OpenSurf::on_openButton_clicked()
      {
          QString img_name = QFileDialog::getOpenFileName(this, "Open Image", "../open_surf",
                                                                 tr("Image Files(*.png *.jpeg *.jpg *.bmp)"));
             if(0 == open_image_num)
                 ui->textBrowser->clear();
             open_image_num ++;
             if( 1 == open_image_num )
                 {
                     img1 = cvLoadImage(img_name.toAscii().data());
                     img_match1 = cvLoadImage(img_name.toAscii().data());
                     cvSaveImage("../open_surf/load_img1.jpg", img1);
                     ui->textBrowser->setFixedSize(img1->width, img1->height);
                     ui->textBrowser->insertHtml("<img src=../open_surf/load_img1.jpg>");
                 }
             else if(2 == open_image_num)
                 {
                     img2 = cvLoadImage(img_name.toAscii().data());
                     img_match2 = cvLoadImage(img_name.toAscii().data());
                     cvSaveImage("../open_surf/load_img2.jpg", img2);
                     ui->textBrowser->setFixedSize(img1->width+img2->width, std::max(img1->height, img2->height));
                     //取消自動(dòng)換行模式,讓2幅圖片水平顯示
                     ui->textBrowser->setWordWrapMode (QTextOption::NoWrap);
                     ui->textBrowser->insertHtml("<img src=../open_surf/load_img2.jpg>");
                 }
             else if(3 == open_image_num)
                 {
                     open_image_num = 0;
                     ui->textBrowser->clear();
                 }
      }
      
      void OpenSurf::on_detectButton_clicked()
      {
          if( 1 == open_image_num )
              {
                  //用surf對(duì)特征點(diǎn)進(jìn)行檢測(cè)
                  surfDetDes(img1, ipts, false, 5, 4, 2, 0.0004f);
                  //在圖像中將特征點(diǎn)畫出來
                  drawIpoints(img1, ipts);
                  cvSaveImage("../open_surf/detect_img1.jpg", img1);
                  ui->textBrowser->clear();
                  ui->textBrowser->setFixedSize(img1->width, img1->height);
                  ui->textBrowser->insertHtml("<img src=../open_surf/detect_img1.jpg>");
              }
          else if (2 == open_image_num)
              {
                  //用surf對(duì)特征點(diǎn)進(jìn)行檢測(cè)
                  surfDetDes(img1, ipts1, false, 5, 4, 2, 0.0004f);
                  //在圖像中將特征點(diǎn)畫出來
                  drawIpoints(img1, ipts1);
                  cvSaveImage("../open_surf/detect_img1.jpg", img1);
                  //用surf對(duì)特征點(diǎn)進(jìn)行檢測(cè)
                  surfDetDes(img2, ipts2, false, 5, 4, 2, 0.0004f);
                  //在圖像中將特征點(diǎn)畫出來
                  drawIpoints(img2, ipts2);
                  cvSaveImage("../open_surf/detect_img2.jpg", img2);
                  ui->textBrowser->clear();
                  ui->textBrowser->insertHtml("<img src=../open_surf/detect_img1.jpg>");
      
                  ui->textBrowser->setFixedSize(img1->width+img2->width, std::max(img1->height, img2->height));
                  //取消自動(dòng)換行模式,讓2幅圖片水平顯示
                  ui->textBrowser->setWordWrapMode (QTextOption::NoWrap);
                  ui->textBrowser->insertHtml("<img src=../open_surf/detect_img2.jpg>");
              }
      }
      
      void OpenSurf::on_matchButton_clicked()
      {
          if(2 == open_image_num)
              {
                  getMatches(ipts1,ipts2,matches);
                  for (unsigned int i = 0; i < matches.size(); ++i)
                    {
                      drawPoint(img_match1,matches[i].first);
                      drawPoint(img_match2,matches[i].second);
      
                      const int & w = img1->width;
                      const int & h1 = img1->height;
                      const int & h2 = img2->height;
                      //這里因?yàn)槲沂孪纫呀?jīng)知道了圖片的相對(duì)打開后顯示的位置,所以在畫匹配的直線時(shí)加了點(diǎn)常識(shí)
                      //因此該方法不通用,只是適合本例中給的圖片,最好的方法就像Rob Hess的sift算法那樣
                      //把2張圖片合成一張,然后在一張圖片上畫匹配直線
                      cvLine(img_match1,cvPoint(matches[i].first.x,matches[i].first.y),
                             cvPoint(matches[i].second.x+w,matches[i].second.y+std::abs(h1-h2)),
                             cvScalar(255,255,255),1);
                      cvLine(img_match2,cvPoint(matches[i].first.x-w,matches[i].first.y-std::abs(h1-h2)),
                             cvPoint(matches[i].second.x,matches[i].second.y),
                             cvScalar(255,255,255),1);
                    }
                  cvSaveImage("../open_surf/match_img1.jpg", img_match1);
                  cvSaveImage("../open_surf/match_img2.jpg", img_match2);
                  ui->textBrowser->clear();
                  ui->textBrowser->insertHtml("<img src=../open_surf/match_img1.jpg>");
      
                  ui->textBrowser->setFixedSize(img1->width+img2->width, std::max(img1->height, img2->height));
                  //取消自動(dòng)換行模式,讓2幅圖片水平顯示
                  ui->textBrowser->setWordWrapMode (QTextOption::NoWrap);
                  ui->textBrowser->insertHtml("<img src=../open_surf/match_img2.jpg>");
              }
      }
      
      
      void OpenSurf::on_clusterButton_clicked()
      {
          for (int repeat = 0; repeat < 10; ++repeat)
            {
      
              km.Run(&ipts, 5, true);
              drawPoints(img1, km.clusters);
      
              for (unsigned int i = 0; i < ipts.size(); ++i)
              {
                cvLine(img1, cvPoint(ipts[i].x,ipts[i].y), cvPoint(km.clusters[ipts[i].clusterIndex].x ,km.clusters[ipts[i].clusterIndex].y),cvScalar(255,255,255));
              }
              cvSaveImage("../open_surf/kmeans_img1.jpg", img1);
              ui->textBrowser->clear();
              ui->textBrowser->setFixedSize(img1->width, img1->height);
              ui->textBrowser->insertHtml("<img src=../open_surf/kmeans_img1.jpg>");
            }
      }
      
      
      void OpenSurf::on_closeButton_clicked()
      {
          close();
      }
      復(fù)制代碼

       

         

        總結(jié):

           Surf在速度上比sift要快許多,這主要得益于它的積分圖技術(shù),已經(jīng)Hessian矩陣的利用減少了降采樣過程,另外它得到的特征向量維數(shù)也比較少,有利于更快的進(jìn)行特征點(diǎn)匹配。

       

       

       附錄一:

        1、和RobHesson運(yùn)行時(shí)一樣,這里的open surf運(yùn)行時(shí)出現(xiàn)如下錯(cuò)誤:

        ipoint.obj:-1: error: LNK2019: 無法解析的外部符號(hào) _cvFindHomography,該符號(hào)在函數(shù) "int __cdecl translateCorners(class std::vector<struct std::pair<class Ipoint,class Ipoint>,class std::allocator<struct std::pair<class Ipoint,class Ipoint> > > &,struct CvPoint const * const,struct CvPoint * const)" (?translateCorners@@YAHAAV?$vector@U?$pair@VIpoint@@V1@@std@@V?$allocator@U?$pair@VIpoint@@V1@@std@@@2@@std@@QBUCvPoint@@QAU3@@Z) 中被引用

        不過這次的原因是沒有opencv_calib3d242d.lib庫(kù),因?yàn)楸緊pen surf在進(jìn)行特征匹配時(shí)用到了opencv中的3維重建有關(guān)的函數(shù)cvFindHomography(該函數(shù)是求2個(gè)圖像間的單應(yīng)矩陣),所以很多人都會(huì)忘了添加這個(gè)庫(kù)文件,就會(huì)導(dǎo)致這個(gè)錯(cuò)誤。

       

        2、如果用了Qt或MFC等界面設(shè)計(jì)代碼時(shí),編譯該程序會(huì)報(bào)如下錯(cuò)誤:

        moc_open_surf.obj:-1: error: LNK2005: "public: void __thiscall Kmeans::SetIpoints(class std::vector<class Ipoint,class std::allocator<class Ipoint> > *)" (?SetIpoints@Kmeans@@QAEXPAV?$vector@VIpoint@@V?$allocator@VIpoint@@@std@@@std@@@Z) 已經(jīng)在 main.obj 中定義

        其實(shí)是Open Surf的作者可能沒有考慮周全,它在kmeans.h文件中把Kmeans這個(gè)類的成員函數(shù)方法在頭文件中實(shí)現(xiàn)了,其實(shí)這在標(biāo)準(zhǔn)c++中是不支持的。解決方法就是把kmeans.h改造成kemans.hpp(該方法我沒有去試過);另外一種方法就是新建一個(gè)kmeans.cpp文件,把成員函數(shù)的實(shí)現(xiàn)過程放在cpp文件中實(shí)現(xiàn),我這次試驗(yàn)就是采用的這個(gè)方法。

       

       

        附錄二:

        實(shí)驗(yàn)工程code下載

       

       

       

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多