日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

特征點(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的中文意思為快速魯棒特征。本文不是專(zhuān)門(mén)介紹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原文翻譯》,寫(xiě)得非常好,看完會(huì)對(duì)surf中采用的一些技術(shù)更加深入的理解,不過(guò)本文卻不是翻譯英文的,而是該作者自己的理解,對(duì)積分圖,Hessian矩陣等引入的原因都做了通俗的解釋?zhuān)扑]一看。

 

 

    一、Surf描述子形成步驟

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

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

          

         即每一個(gè)像素點(diǎn)都可以求出一個(gè)Hessian矩陣。但是由于我們的特征點(diǎn)需要具備尺度無(wú)關(guān)性,所以在進(jìn)行Hessian矩陣構(gòu)造前,需要對(duì)其進(jìn)行高斯濾波。這樣,經(jīng)過(guò)濾波后在進(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的眾多資料來(lái)看,就是原圖每個(gè)像素的Hessian矩陣行列式的近似值構(gòu)成的。其行列式近似公式如下:

          

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

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

            

         該圖的左邊即用高斯平滑然后在y方向上求二階導(dǎo)數(shù)的模板,為了加快運(yùn)算用了近似處理,其處理結(jié)果如右圖所示,這樣就簡(jiǎn)化了很多。并且右圖可以采用積分圖來(lái)運(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é)省了降采樣過(guò)程,其處理速度自然也就提上去了。其金字塔圖像如下所示:

  

    

 

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

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

 

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

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

 

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

  這一步與sift也大有不同。Sift選取特征點(diǎn)主方向是采用在特征點(diǎn)領(lǐng)域內(nèi)統(tǒng)計(jì)其梯度直方圖,取直方圖bin值最大的以及超過(guò)最大bin值80%的那些方向做為特征點(diǎn)的主方向。而在surf中,不統(tǒng)計(jì)其梯度直方圖,而是統(tǒng)計(jì)特征點(diǎn)領(lǐng)域內(nèi)的harr小波特征。即在特征點(diǎn)的領(lǐng)域(比如說(shuō),半徑為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)的主方向。該過(guò)程的示意圖如下:

    

 

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

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

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

    

 

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

 

 

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

  surf特征點(diǎn)的匹配過(guò)程和sift類(lèi)似,這里不做詳細(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)聚類(lèi)等6中功能。本次實(shí)驗(yàn)就簡(jiǎn)單的測(cè)試了圖片的檢測(cè),匹配和特征點(diǎn)聚類(lèi)3個(gè)功能。并加入了簡(jiǎn)單的界面。

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

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

 

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

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

  

 

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

  

  單擊Close 按鈕退出程序。

 

  Surf特征點(diǎn)匹配

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

  

 

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

  

 

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

  

  單擊Close 按鈕退出程序。

 

  Surf特征點(diǎn)聚類(lèi)

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

  

 

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

  

 

  單擊Kmeans Cluster按鈕,程序會(huì)對(duì)這些特征點(diǎn)集合進(jìn)行聚類(lèi),并顯示其結(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)畫(huà)出來(lái)
            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)畫(huà)出來(lái)
            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)畫(huà)出來(lái)
            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ì)打開(kāi)后顯示的位置,所以在畫(huà)匹配的直線(xiàn)時(shí)加了點(diǎn)常識(shí)
                //因此該方法不通用,只是適合本例中給的圖片,最好的方法就像Rob Hess的sift算法那樣
                //把2張圖片合成一張,然后在一張圖片上畫(huà)匹配直線(xiàn)
                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矩陣的利用減少了降采樣過(guò)程,另外它得到的特征向量維數(shù)也比較少,有利于更快的進(jìn)行特征點(diǎn)匹配。

 

 

 附錄一:

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

  ipoint.obj:-1: error: LNK2019: 無(wú)法解析的外部符號(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) 中被引用

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

 

 

  附錄二:

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

 

 

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(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)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多