画像内の不要部分を取り除き、修復する（OpenCV / iOS）

<b>写真に意図せず写りこんでしまった物体等を取り除き、それによって欠損した領域を自動修復</b>する技術を、画像修復／画像補間／インペインティング（Inpainting）と呼びます。

で、OpenCV にその機能があったので iOS で実装してみました。

> 関数 inpaint は，選択された画像領域を，その領域境界付近のピクセルを利用して再構成します．この関数は，スキャンされた写真からごみや傷を除去したり，静止画や動画から不要な物体を削除したりするために利用されます．
> （[opencv documentation](http://opencv.jp/opencv-2svn/cpp/miscellaneous_image_transformations.html)より）


##実装方法

基本的には `inpaint()` 関数を呼ぶだけです。OpenCV 3.0.0 における [inpaint 関数のリファレンス](http://docs.opencv.org/3.0-beta/modules/photo/doc/inpainting.html) によると、次のように定義されています。

```
C++: void inpaint(InputArray src, InputArray inpaintMask, OutputArray dst, double inpaintRadius, int flags)
```

> Parameters:	
>
- src – Input 8-bit 1-channel or 3-channel image.
- inpaintMask – Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that needs to be inpainted.
- dst – Output image with the same size and type as src .
- inpaintRadius – Radius of a circular neighborhood of each point inpainted that is considered by the algorithm.
- flags – Inpainting method that could be one of the following:
  - INPAINT_NS Navier-Stokes based method [Navier01]
  - INPAINT_TELEA Method by Alexandru Telea [Telea04].```


第２引数に渡すマスク画像というのは、**修復したい部分＝除去したい部分を示す画像**です。1チャンネル・8ビットのみ受け付けるとのこと。


iOS 向けに次のように実装しました。

```objc
+ (UIImage *)inpainting:(UIImage *)srcImage maskImage:(UIImage *)maskImage {

    cv::Mat srcMat;
    cvtColor([OpenCVHelper cvMatFromUIImage:srcImage], srcMat, CV_BGRA2BGR);
    cv::Mat maskMat;
    cvtColor([OpenCVHelper cvMatFromUIImage:maskImage], maskMat, CV_BGR2GRAY);
    cv::Mat dstMat(srcImage.size.height, srcImage.size.width, srcMat.type());
    
    // 入力画像, マスク, 出力画像, 修正時に考慮される近傍範囲を表す半径, 手法
    //     INPAINT_NS    = 0, // Navier-Stokes algorithm
    //     INPAINT_TELEA = 1 // A. Telea algorithm
    cv::inpaint(srcMat, maskMat, dstMat, 3, cv::INPAINT_NS);
    
    return [OpenCVHelper UIImageFromCVMat:dstMat];
}
```


呼び出し側はこんな感じになります。

```objc
UIImage *srcImage  = [UIImage imageNamed:@"src_filename"];
UIImage *maskImage = [UIImage imageNamed:@"mask_filename"];
self.imageView.image = [OpenCVHelper inpainting:srcImage
                                      maskImage:maskImage];
```


##試してみる

もうすぐ WWDC、ということで[昨年のWWDC](http://d.hatena.ne.jp/shu223/20140610/1402368875) に行ったときの記念写真から、「8」の文字を消してみました。


- 元画像

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


- マスク画像

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

マスク画像は、元画像を下に敷いて、トラックパッドで操作しつつ適当になぞって作成しました。

###実行結果

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

なかなかいい感じに修復されているのではないでしょうか。


##続・試してみる

Apple感を消すため、後ろの看板のリンゴマークも消してみます。

- マスク画像その2

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

###実行結果

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

こちらは全然ダメでしたね。。こういう細かいパターンが続く箇所の修復は苦手なのかもしれません。


##こちらもどうぞ

- [iOS / OpenCV 3.0 で画像の特徴点を検出する（AKAZE, SIFT, SURF, ORB） - Over&Out その後](http://d.hatena.ne.jp/shu223/20150511/1431327125)
- [OpenCV for iOS でウォーリーを探すカメラアプリをつくる - Qiita](http://qiita.com/shu223/items/c91019281fa660d1fa6d)
- [iOS - 「顔以外」のものを画像認識する - Qiita](http://qiita.com/shu223/items/ffd2202eaf92d342f83d)