iOS / OpenCV 3.0 で画像の特徴点を検出する（AKAZE, SIFT, SURF, ORB）

##局所特徴量とは / SIFT, SURF 特徴量

下記スライドが超わかりやすかったです。

- [画像認識の初歩、SIFT,SURF特徴量](http://www.slideshare.net/lawmn/siftsurf)

で、SIFT (Scale-invariant feature transform)、SURF (Speed-Upped Robust Feature) というのは、<b>拡大縮小・回転・照明変化に強いロバストな特徴量</b>、としてよく知られているようです。


SURF の方が軽量で、その代わり認識精度は SIFT の方が良い、とのこと。


##特徴量の用途

###複数写真からのパノラマ写真合成

<img src="http://f.st-hatena.com/images/fotolife/s/shu223/20150511/20150511154137.png" width="400">

<span class="deco" style="font-size:small;">（上述のスライドより）</span>


###AR のマーカー認識

下記画像は<span class="deco" style="color:#FF0000;">ARのマーカー認識とは違いますが、そういう使い方ができそうだ</span>、ということは汲んでいただけるかと。。


<img src="http://f.st-hatena.com/images/fotolife/s/shu223/20150511/20150511154236.jpg" width="400">

<span class="deco" style="font-size:small;">（http://docs.opencv.org/3.0-rc1/d7/dff/tutorial_feature_homography.html より）</span>


###物体認識

<img src="http://f.st-hatena.com/images/fotolife/s/shu223/20150511/20150511154255.png" width="400">

<span class="deco" style="font-size:small;">（上述のスライドより）</span>


##ORB（Oriented-BRIEF）特徴量

SIFT, SURF 同様に<b>回転や拡大縮小にロバストでありながら、高速（SURFの10倍、SIFTの100倍）、かつライセンス的にも使いやすい</b>特徴量。

- 参考：[ORB使ってみた](http://pukulab.blog.fc2.com/blog-entry-41.html)


ちなみに下記記事のコメント欄で教えていただきました。

- [OpenCV for iOS でウォーリーを探すカメラアプリをつくる](http://qiita.com/shu223/items/c91019281fa660d1fa6d#comment-0a75b498347592a834d6)


##AKAZE 特徴量

<b><span class="deco" style="color:#FF0000;">OpenCV 3.0 に新たに組み込まれた</span></b>特徴点抽出アルゴリズム。


AKAZE は <b>Accelerated KAZE</b> の略で、KAZE という SIFT や SURF の欠点を解決した手法をもとに、さらにそのロバスト性の向上と高速化を図ったもの、とのこと。

- 参考：[AKAZE特徴量の紹介と他特徴量との比較](http://poly.hatenablog.com/entry/2014/01/06/063012)


上記記事では他手法と比較したベンチマークもとっていて、

> 画像変化のロバスト性については、スケール・回転・輝度・Blur全てにおいて、AKAZEが最も良い数値を示していることが分かります。実際使ってみた感じでも、かなり正確にトラッキングしている感じがします。

> スピードに関しても、SIFTやSURFに比べて同程度、もしくはそれ以上です。

とのことで、かなりおいしいとこ取りな感じです。


さらにいろんなところで言われてることですが、SIFT や SURF はライセンス的に商用利用しづらいものらしく、一方でこの AKAZE は<span class="deco" style="font-weight:bold;">ライセンス的にも使いやすくなっている</span>とのこと。（自分はまだ個人でお試し利用中なので詳細未確認）


##AKAZE vs ORB

OpenCV 公式の[こちらのページ](http://docs.opencv.org/3.0-rc1/dc/d16/tutorial_akaze_tracking.html)にて、AKAZE と ORB による planar tracking（平面トラッキング）の比較が行われています。


<iframe width="420" height="315" src="https://www.youtube.com/embed/LWY-w8AGGhE" frameborder="0" allowfullscreen></iframe>


AKAZEの方がより強力にトラッキングしているようです。


##iOSにおける実装

というわけでここからが本題ですが、iOS で SURF, SIFT, ORB, AKAZE を用いて特徴点検出を行ってみます。

###contrib の利用

SIFT, SURF は（ライセンスの都合上？）contrib という別枠で管理されています。詳しくは下記記事にまとめたのでご参照ください。

- [OpenCV 3.0 の拡張モジュール群 opencv_contrib を iOS で利用する](http://qiita.com/shu223/items/5a517bc10503278d1ff1)


###コード

こんな感じで実装しました。

```objc
typedef NS_ENUM(NSUInteger, CVFeatureDetectorType) {
    CVFeatureDetectorTypeSURF,
    CVFeatureDetectorTypeSIFT,
    CVFeatureDetectorTypeORB,
    CVFeatureDetectorTypeAKAZE,
};
```

```objc
+ (UIImage *)detectKeypoints:(UIImage *)srcImage withFeatureDetector:(CVFeatureDetectorType)detectorType
{
    cv::Mat srcMat = [OpenCVHelper cvMatFromUIImage:srcImage];

    // FeatureDetector 生成
    Ptr<Feature2D> detector;
    switch (detectorType) {
        case CVFeatureDetectorTypeSURF:
        default:
            detector = SURF::create();
            break;
        case CVFeatureDetectorTypeSIFT:
            detector = SIFT::create();
            break;
        case CVFeatureDetectorTypeORB:
            detector = ORB::create();
            break;
        case CVFeatureDetectorTypeAKAZE:
            detector = AKAZE::create();
            break;
    }
    
    // 特徴点抽出
    std::vector<KeyPoint> keypoints;
    detector->detect(srcMat, keypoints);
    
    printf("%lu keypoints are detected.\n", keypoints.size());
    
    // 特徴点を描画
    cv::Mat dstMat;
    
    dstMat = srcMat.clone();
    for(int i = 0; i < keypoints.size(); i++) {
        
        KeyPoint *point = &(keypoints[i]);
        cv::Point center;
        int radius;
        center.x = cvRound(point->pt.x);
        center.y = cvRound(point->pt.y);
        radius = cvRound(point->size*0.25);
        
        cv::circle(dstMat, center, radius, Scalar(255,255,0));
    }
    
    return [OpenCVHelper UIImageFromCVMat:dstMat];
}
```


呼び出し側はこんな感じ。

```objc
- (IBAction)segmentChanged:(UISegmentedControl *)sender {
    
    UIImage *srcImage = [UIImage imageNamed:@"image_filename"];
    
    self.imageView.image = [OpenCVHelper detectKeypoints:srcImage
                                     withFeatureDetector:(CVFeatureDetectorType)sender.selectedSegmentIndex];
}
```


##実行結果

実行時間は iPhone 6 で計測。（特徴点の描画も含む）


###SURF

<img src="http://f.st-hatena.com/images/fotolife/s/shu223/20150511/20150511154256.png" width="375">

- 特徴点数： 5127
- 実行時間： 0.610362[s]


###SIFT

<img src="http://f.st-hatena.com/images/fotolife/s/shu223/20150511/20150511155311.png" width="375">

- 特徴点数： 3175
- 実行時間： 2.116536[s]


###ORB

<img src="http://f.st-hatena.com/images/fotolife/s/shu223/20150511/20150511155312.png" width="375">

- 特徴点数： 500
- 実行時間： 0.120371[s]


###AKAZE

<img src="http://f.st-hatena.com/images/fotolife/s/shu223/20150511/20150511155313.png" width="375">

- 特徴点数： 5651
- 実行時間： 1.260507[s]


##所感＆今後の展望

あれ、AKAZEが意外と遅い。。処理時間の計測範囲には特徴点の描画まで含めたのでその影響かな、とも思いましたがSURFは同じぐらいの特徴点数なのに速いのでそこではないようです。


上でも参照した[こちらの記事](http://poly.hatenablog.com/entry/2014/01/06/063012)によると、

> AKAZE/KAZEのコードはOpenMPでの並列化に対応しているのですが、このベンチマークではOpenMPを利用していません。Pablo氏からの指摘によれば、AKAZEのロジックは並列化に向いているため、OpenMPの利用により大幅なスピードアップが見込めるとのことです。OpenCVのSIFTとSURFのコードを見たところ、SIFTのコードは並列化していませんが、SURFのコードは並列化しています。上記ベンチマークのSURFの速さは、それも起因していると思われます。

とのこと。SURFの並列化がiOSでも効いているのかどうか、AKAZE の OpenCV の実装がどうなってるのかまでは理解してやっていないので、上記結果はほんの参考程度にどうぞ。。


また、処理速度以前に、<span class="deco" style="color:#FF0000;font-weight:bold;">検出された特徴点が手法によって全然違う</span>、というのも上の結果から見て取れます。アルゴリズムの中身を理解して、用途に応じて適切なものを選ぶ必要がありそう。今回使用した画像はベクター的なオブジェクトの集合体ですが、写真的な画像に適用したらどうなのか、とか。



次ステップとしては DescriptorExtractor, DescriptorMatcher を使って特徴点のマッチングまでやってみたいと思っています。


##おまけ：OpenCV 3.0 で使える FeatureDetector

- FAST，ORB，BRISK，MSER，GFTT，HARRIS，SimpleBlob，KAZE，AKAZE
- <b>要contrib</b>： STAR，SIFT，SURF

（参考：[OpenCV3.0.0-alphaの特徴抽出・マッチングまとめ](http://whoopsidaisies.hatenablog.com/entry/2014/08/20/200215)）


##その他参考ページ

- [Feature Detection and Description — OpenCV 3.0.0-dev documentation](http://docs.opencv.org/3.0-alpha/modules/features2d/doc/feature_detection_and_description.html#akaze)・・・リファレンス
- [OpenCV: 2D Features framework (feature2d module)](http://docs.opencv.org/3.0-rc1/d9/d97/tutorial_table_of_content_features2d.html)・・・チュートリアル
  - http://docs.opencv.org/3.0-rc1/d7/d66/tutorial_feature_detection.html
  - http://docs.opencv.org/3.0-rc1/db/d70/tutorial_akaze_matching.html
  - http://docs.opencv.org/3.0-rc1/dd/dd4/tutorial_detection_of_planar_objects.html
  - http://docs.opencv.org/3.0-rc1/d7/dff/tutorial_feature_homography.html
- http://opencv.jp/opencv2-x-samples/surf_extraction （古い）

###ブログ

- [OpenCV3.0.0-alphaの特徴抽出・マッチングまとめ](http://whoopsidaisies.hatenablog.com/entry/2014/08/20/200215)
- [OPENCV 3.0.0-ALPHAで特徴点検出 C++11](http://www.yasutomo57jp.com/2014/08/20/opencv-3-0-0-alpha%E3%81%A7%E7%89%B9%E5%BE%B4%E7%82%B9%E6%A4%9C%E5%87%BA-c11/)
- [3日で作る高速特定物体認識システム (2) SIFT特徴量の抽出](http://aidiary.hatenablog.com/entry/20091024/1256384360)
- [3日で作る高速特定物体認識システム (3) SURFの抽出](http://aidiary.hatenablog.com/entry/20091030/1256905218)
- [OpenCVで画像の特徴抽出・マッチングを行う](http://whoopsidaisies.hatenablog.com/entry/2013/12/07/135810)
- [OpenCV2でSIFT, SURFによる画像の対応付け](http://kesin.hatenablog.com/entry/20120810/1344582180)

##こちらもどうぞ

- [OpenCV for iOS で画像内の不要部分を取り除き、修復する - Over&Out その後](http://d.hatena.ne.jp/shu223/20150512/1431341853)
- [OpenCV for iOS でウォーリーを探すカメラアプリをつくる - Qiita](http://qiita.com/shu223/items/c91019281fa660d1fa6d)
- [iOS - 「顔以外」のものを画像認識する - Qiita](http://qiita.com/shu223/items/ffd2202eaf92d342f83d)
- [OpenCV 3.0 の拡張モジュール群 opencv_contrib を iOS で利用する - Qiita](http://qiita.com/shu223/items/5a517bc10503278d1ff1)