2016.11. 9.
2017.10. 1. OpenCV3.3.0とVisual C++ 2017で確認済み、このままのプログラムで使用可
石立 喬

OpenCV3.1.0とVisual C++ 2015による画像処理と認識(21)

----- ホモグラフィー変換やStitcherを用いて二枚の画像を合成する -----

 「OpenCVとVisual C++による画像処理と認識(21)」で行ったのと同じホモグラフィー変換とStitcherによる画像の合成を、OpenCV3.1.0を用いて試みた。特徴点検出・記述器(feature detector/descriptor)には、ORBの他に、AKAZE、BRISK、KAZEも用いた。原理的な詳細については、前記資料を参照されたい。

OpenCV3.1.0で変わったところ
◎Feature2D基底クラスを継承する子クラスに、特徴点検出・記述器(feature detector/descriptor)AKAZE、KAZEが追加になった。
◎画像から、特徴点と記述子が同時に簡単に得られるようになった。
 従来、
 1)画像→特徴点:FeatureDetector::create("ORB")やOrbFeatureDetectorで特徴点検出器を生成してから、detectメソッドを使用
 2)特徴点→記述子:DescriptorExtractor::create("ORB")やOrbDescriptorExtractorで記述器を生成してから、computeメソッドを使用
と二段階に分かれていたのが、
 OpenCV3.1.0では、Feature2D基底クラスのdetectAndComputeメソッドで、一度に実行できるようになった。AKAZE、BRISK、KAZE、ORBの各クラスは、Feature2Dを継承しているので、これらのクラスから使用できる。
◎DMatchクラスの場所が変わった
 従来、OpenCV2.4では、features2d/2D Features FrameworkモジュールのCommon Interfaces of Descriptor Matchersサブモジュールの中に構造体としてあったが、OpenCV3.1.0では、core/Core FunctionalityモジュールのBasic Structuresサブモジュールの中のクラスになった。
◎StitcherクラスがImages stitchingモジュールの直接属するようになった
 従来、Stitcherクラスは、stitching/Images stitichingモジュールのHigh Level Functionalityサブモジュールにあったが、OpenCV3.1.0では、stitching/Images stitichingモジュールに直接配置された。
◎KeyPointクラスの場所が移り、convertメソッドができた
 従来、KeyPointクラスは、features2d/2D Features FrameworkモジュールのCommon Interfaces of Feature Detectorにあり、メンバーメソッドはなかった。OpenCV3.1.0になって、KeyPointクラスが、core/Core FunctionalityモジュールのBasic Structuresサブモジュールに移り、convertメソッドができた。

ホモグラフィー変換を用いて、二枚の画像を合成する
 このプログラムは、下記から成る(ただし、1)〜3)は図1、4)は図2、5)〜10)は図3を参照)。
 特徴点検出・記述器(feature detector/descriptor)の一例としてORBを取り上げる。
 1)原画像を読み込み、表示する。
     右画像をimage_src1(#1画像、query問合せ画像)、左画像をimage_src2(#2画像、train訓練画像)とする。
     ただし、逆にしても同じ結果が得られる。
 2)ORBを生成し、detectAndCompute関数で、特徴点keypoints1、keypoints2、記述子descriptors1、descriptors2を求める。
 3)BFMatcherでマッチングを採り、結果をベクトルmatchesに入れる。
 4)matchesを順次読み取り、パラメータのマッチング距離distanceが一定値(試行錯誤で25と決定)未満を良好なマッチングと判定し、
    ・そのmatchesをgood_matchesにコピーする
    ・そのmatchesのqueryIdxパラメータが指し示すkeypoints1を、good_keypoints1にコピーする
    ・そのmatchesのtrainIdxパラメータが指し示すkeypoints2を、good_keypoints2にコピーする
 5)good_matcesにしたがって、keypoints1とkeypoints2を使って、マッチングを描画する(結果を図5に示す)。
    good_matchesの内容は、matchesの内容を引き継いでいるので、keypoints1,2を使用する。
 6)good_keypoints1,2はKeyPoint型のベクトルなので、findHomographyメソッド用に、Point2f型ベクトルに変換する。
 7)findHompgraphyメソッドを実行して、ホモグラフィー行列homographyを求める。
 8)warpPerspectiveメソッドで、image_src1を射影変換して、Sizeで指定したimage_dstに描画する。
 9)image_dstの、Rectで指定した部分に、image_src2をコピーする。
 10)image_dstを表示する。

使用したOpenCVクラスメソッドの説明
◎Feature2DクラスのdetectAndComputeメソッド
 特徴点の検出と記述子の計算を一挙に行うメソッドである。
  Feature2D::detectAndCompute(
    image ------- InputArray、入力画像
    mask ------- InputArray、noArray()で良い、デフォルトはないので、設定が必要
    keypoints ---- vector<KeyPoint>&、キーポイント(特徴点)の出力ベクトル
    descriptors --- OutputArray、記述子の出力配列
    useProvidedKeypoints = false ---- bool、デフォルトで良い
  )
◎DMatchクラス
 マッチング結果を格納するクラスで、core/Core FunctionalityモジュールのBasic Structuresサブモジュールにある。
パラメータには下記がある。
  queryIdx ------- int、query問合せ画像の特徴点の番号、ここでは右画像に対応する。
  trainIdx -------- int、train訓練画像の特徴点の番号、ここでは左画像に対応する。
  distance ------- float、マッチングの距離、良くマッチしていると小さくなる
◎BFMatcherクラス
 DescriptorMatcherを継承し、BruteForceアルゴリズムを使用する。features2d/2D Features FrameworkモジュールのDescriptor Matchersサブモジュールにある。
 コンストラクタは、
  BFMatcher(
    normType = NORM_L2 ------ int、マッチングの度合いを測る基準で、KAZE用、
                 AKAZE、ORB、BRISK、BRIEFにはNORM_HAMMINGを用いる
    crossCheck = false --------- クロスチェックの有無、trueにした方が良い
  )
である。
 メソッドとしては、親クラスDescriptorMatcherのmatchをBFMatcherから使用する。
  match(
    queryDescriptors ---- InputArray、query問合せ画像の特徴点の記述子
    trainDescriptors ----- InputArray、train訓練画像の特徴点の記述子
    matches ----------- vector<DMatche>&、 DMatche型のベクトル
    mask = noArray() ---- Mat、一般には必要ない
  )
◎KeyPointクラスのconvertメソッド
 KeyPoint型のベクトルをPoint2f型のベクトルに変換したり、またはその逆を行ったりするメソッドで、OpenCV3.1.0で、core/Core functionalityモジュールのBasic structuresサブモジュールのKeyPointクラスにできた。
 ここで使用する例のように、KeyPoint → Point2fの場合には、
     convert(
    keypoints ------ vector<KeyPoint>&、ORBなどで得た特徴点検出結果のキーポイントのベクトル
    points2f ------- vector<Point2f>&、各キーポイントの二次元座標のベクトル
    keypointsIndexes = vector<int>() --- vector<int>&、マスクで、一般にはデフォルトで良い
  )
を使用する。逆変換のオーバーロードメソッドもある。
◎findHomographyメソッド
 二つの画像の特徴点からホモグラフィー行列(perspective transformation)を求めるメソッドである。calib3d/Camera Calibration and 3D Reconstruction.モジュールにある。サブモジュールはない。
  findHomography(
    srcPoints -------- InputArray、vector<Point2f>型の原画像の特徴点ベクトル
    dstPoints -------- InputArray、vector<Point2f>型の変換後画像の特徴点のベクトル
    method = 0 ------ int、算出方法で、デフォルトは、すべての点を使用した単純な最小二乗法による。他に、下記がある。
        OpenCV2.4は頭にCV_が付いていたが、それが無くなった。
        RANSAC ----------- RANSAC法
        LMEDS ------------ Least-median法
        PHO -------------- OpenCV3.1.0で追加になった、PROSAC法
    ransacReprojThreshold = 3 ---- int、RANSACの時のみ使用、はずれ点と見なすエラーの最大許容値、1〜10が良いとされる。
    mask = noArray() --- OutputArray、一般的には使用しない
  )
戻り値はMat型のホモグラフィー行列である。
 OpenCV3.1.0になって追加されたオーバーロード関数があり、
    maxIters = 2000 -------- int、最大繰り返し回数
    confidence = 0.995 ----- float、信頼度
が最後に追加されている。しかし、これらはデフォルト値を持っているので、どちらが使われるかわからない。
◎warpPerspectiveメソッド
 入力原画像を、ホモグラフィー行列に応じて変換し、新しい画像を出力するメソッドである。OpenCV2.4と同様に、imgproc/Image ProcessingモジュールのGeometric Image Transformationsサブモジュールにある。
  warpPerspective(
    src ------ InputArray、入力原画像
    dst ------ OutputArray、原画像と同タイプで、サイズはdsizeで指定する、変換後出力画像
    M ------- InputArray、3 x 3 の変換マトリックス(ここではホモグラフィー行列を与える)
    dsize ---- Size、出力画像のサイズ
    flags = INTER_LINEAR ---- int、補間方法を指定する。デフォルト以外にINTER_NEARESTがある
    borderMode = BORDER_CONSTANT --- int、変換画像が存在しない周辺の処理、外にBORDER_REPLICATEがある。
    borderValue = Scalar() -------------- Scalar&、BORDER_CONSTANTの場合の周辺の色、デフォルトは黒
  )
戻り値はない。in-placeでは使用できないので、dstにsrcを兼用させることはできない。


図1 原画像を読み込み、マッチングを採る



図2 マッチング結果の中で、良好なものを選ぶ



図3 マッチングを表示し、ホモグラフィー行列を求め、画像変換して合成する


ホモグラフィー変換を用いた合成結果
 図4は、参考のために原画像をそのまま並べて表示したもので、パノラマ合成時に右側にくる画像を#1問合せ画像、左を#2訓練画像としてある。逆にしても同じ結果が得られた。
 図5は、良好な特徴点のマッチング結果を示したもので、並びの順序は左から、#1画像、#2画像となっている。この画面を見ながら、はずれ点(outlier)がなくなるように、図2の32行にあるしきい値25を決めた。
 図6は、最終的なパノラマ合成結果で、原画像image_src1のサイズを横に二つ並べた大きさにしたため、右側の画像の上下が一部切れている。


 図4 使用した原画像



図5 マッチングをとり、良好なマッチング点のみを選んだところ



図6 ホモグラフィー変換による合成結果


ORB以外の特徴点検出・記述器を用いた結果
 ORBで使用したプログラムにおいて、20行〜22行で特徴点検出・記述器のタイプを変更し、KAZEに関しては、25行で評価方法をNORM_HAMMINGからNORM_L2に変更して、同様に画像の合成を試みた。
 良好なマッチング点の選択は、32行を試行錯誤で変更した。
 表1は、満足できる合成結果が得られた設定条件である。得られた合成画像には、ほとんど差がなかった。

表1 各種特徴点検出・記述器の設定条件



Stitcherクラスを用いてパノラマ画像を合成する
 使用したプログラムを図8に示し、図9はその結果である。
 プログラムは下記から成る。
 1)原画像を読み込む。左右どちらの画像をimage_src1にしても、結果は同じである(左右を気にすることがない)。
 2)複数(ここでは二枚)の画像をベクトル化してimages_srcを用意する。
 3)createDefaultメソッドで、Stitcherクラスのインスタンスsttを生成する。
    引数なしのデフォルトでは、GPUを使用しない。
 4)stitchメソッドで、images_srcを合成し、得られた合成画像image_dstを描画する。

使用したStitcherの説明
 Stitcherクラスを使用するには、opencv2/stitching.hppをインクルードする必要がある。
 Stitcherクラスのメソッドは、OpenCV3.1.0になっても変わりがなく、念のために紹介すると、Stitcherクラスを生成するには、
  createDefaut(
    try_use_gpu = false ------ bool、GNUを使わない場合は、デフォルトのまま
  )
を用い、画像合成の実行には、
  stitch(
    images ------ InputArrayOfArrays、vector<Mat>型の複数の入力画像
    pano ------- OutputArray、合成した出力画像
  )
を用いる。
  stitch(images_src, image_dst);
の代わりに、
  estimateTransform(images_src);
  composePanorama(images_src, image_dst);
を用いても、同様の合成結果が得られるが、あえて面倒なことをする必要はない。


図7 Stitcherを使用した画像合成のプログラム


Stitcherクラスを用いた合成結果
 図9は得られたパノラマ合成結果で、#1画像、#2画像のいずれも切れることのないように外枠が決められているので、#2画像(右)はいっぱいに、#1画像(左)は、それに合わせて縮小されている。継ぎ目が目立たたなく、きれいに合成されている。処理時間は、やや長く感じられた。


図8 Stitcherによる合成結果


結 論
 OpenCVに備わっているfindHomographyメソッドを用いてのホモグラフィー行列の推定と、これを用いて作成した合成画像は、十分良好であった。ただし、事前に、良好なマッチング点の選別を試行錯誤でしておく必要があった。
 OpenCV3.1.0になって、Feature2D基底クラスのdetectAndComputeメソッドができ、Feature2Dを継承する、AKAZE、BRISK、KAZE、ORBの特徴点検出・記述器クラスから使えるので、画像から特徴点と記述子を一度に求めることが可能になった。
 最初にORBでプログラムを作成し、続いて、他の特徴点検出・記述器も試みたが、いずれも満足できる結果が得られた。
 Stitchクラスを用いた画像合成は、容易に良好な結果が得られた。ただし、処理時間は、やや長めであった。




「Visual C++の勉強部屋」(目次)へ