写真に意図せず写りこんでしまった物体等を取り除き、それによって欠損した領域を自動修復する技術を、画像修復/画像補間/インペインティング(Inpainting)と呼びます。
で、OpenCV にその機能があったので iOS で実装してみました。
関数 inpaint は,選択された画像領域を,その領域境界付近のピクセルを利用して再構成します.この関数は,スキャンされた写真からごみや傷を除去したり,静止画や動画から不要な物体を削除したりするために利用されます.
(opencv documentation より)
実装方法
基本的には `inpaint()` 関数を呼ぶだけです。OpenCV 3.0.0 における inpaint 関数のリファレンスによると、次のように定義されています。
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].```
第2引数に渡すマスク画像というのは、修復したい部分=除去したい部分を示す画像です。1チャンネル・8ビットのみ受け付けるとのこと。
iOS 向けに次のように実装しました。
+ (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]; }
呼び出し側はこんな感じになります。
UIImage *srcImage = [UIImage imageNamed:@"src_filename"]; UIImage *maskImage = [UIImage imageNamed:@"mask_filename"]; self.imageView.image = [OpenCVHelper inpainting:srcImage maskImage:maskImage];
試してみる
もうすぐ WWDC、ということで昨年のWWDC に行ったときの記念写真から、「8」の文字を消してみました。
- 元画像
- マスク画像
※マスク画像は、元画像を下に敷いて、トラックパッドで操作しつつ適当になぞって作成しました。
続・試してみる
Apple感を消すため、後ろの看板のリンゴマークも消してみます。
- マスク画像その2