画像間の特徴点対応付け~マッチング方式とオプティカルフロー方式~
最終出力として2画像間の特徴点の対応関係を求めたいという場合、その方式には大きく2種類がある。
この観点からまとめられている記事をあまり見た事がないので投稿。
マッチング方式
「特徴点 対応付け」や「特徴点 マッチング」と検索すると引っかかるのは大体この方式のこと。
特徴点は英語では feature (point) と言ったり keypoint と言ったりする。
ブロック図にすると下記のようになる:
つまり、処理手順としては
- 双方の画像で「特徴点」を見つける
- 双方の画像で見つけた特徴点について「特徴量(特徴記述子と言ったりもする)」を求める
- 双方の特徴量を比較して一番近しい(似ている)ペアを見つける
という流れ。
特徴点抽出手法や特徴量記述手法にはいろんなものが提案されている。
- 2つセットで提案されているものとして、例えばSIFT, SURF, ORB, KAZE, AKAZE, etc...
- 特徴点抽出(検出)手法単体として提案されているものとして、例えばHarrisコーナー, Shi-Tomasiコーナー(=GFTT), FAST, etc...
- 大抵の特徴点抽出手法や特徴量記述手法はグレースケール画像に対して実施するが、RGB画像をそのまま使用するものもあるようだ。例えば、SIFTに色情報を追加する論文がいくつか見つかる。
処理が理解しやすいように、ブロック図にイメージ図も付け足すとこんな感じ:
ちなみに近しい特徴量が複数ある場合はマッチングに失敗することもある。例えばこんな感じ:
このような誤マッチングの回避策として、例えばOpenMVGでは下記のようなチェックがなされている:
つまり特徴が突出しているわけではなければ、マッチング不成立としよう、という考え方。
他にも、双方向にマッチングしてみて、結果が1対1対応しなければ、棄却する、などという手法もある。
ただ、どんどん計算量が増えていってしまうところが悩みどころ。
オプティカルフロー方式(疎)
ここではLucas-kanade法を扱うこととする。
また、説明の簡単化のためにPyramidの件は省略する。
ブロック図にすると下記のようになる:
つまり、処理手順としては
という流れ。
特徴点を追跡するので英語ではtrackingと言ったりする。matchingしているわけではない。
どうして「こんな処理でもう一方の画像上で対応点が見つかるのか?」という疑問に対する雑な回答としては、
「数学的に計算するとこれでできる」となる。下記でわかりやすく解説されている:
OpenCVでとらえる画像の躍動、Optical Flow - Qiita
オプティカルフロー推定の原理・特徴・計算式 | アルゴリズム雑記
処理が理解しやすいように、ブロック図にイメージ図も付け足すとこんな感じ:
オプティカルフロー方式でも誤った点対応(誤トラッキング)が発生する可能性がある。
具体的には「同じような模様の繰り返し」である。
Direct Sparse Odometry (DSO) を Win10, VS2017 でビルド
- SLAM手法の1つである Direct Sparse Odometry (DSO) をWindows上でTUMのデータセットを使って動かしたくトライ。
- ※DSOの要点まとめはこちらのエントリを参照:
環境
- Windows 10 Home x64
- Visual Studio Community 2017
- CMake 3.10.2
スタート地点
本家Gitは下記だがLinux/Mac前提。
GitHub - JakobEngel/dso: Direct Sparse Odometry
これをWindows向けに移植したforkがあったので、これを使う。
GitHub - AndreV2890/dso: Direct Sparse Odometry
しかし手元の環境ではすんなりビルドができなかった。
どうも、上記Win版DSOは下記の環境でのビルドを想定しているようである。
- Visual Studio 2015
- x86版
- Releaseモードのみ
thirdpartyの準備
まず、thirdpartyはx86を前提として準備されている。自分で全部やりなおすのが面倒だったので、同じくx86で行くことにする。
READMEによると、cmakeしたときに依存ライブラリのパスをそれぞれ設定すればOKと書いてあるが、一部はそのままではうまくいかなかった。
- READMEの通りやれば、うまくいったもの
- Eigen
- SuiteSparse
- CHOLMOD
- OpenCV
以下ではうまくいかなかったものについて記す
Boost, Pangolin, OpenCVへのlink (CMakeLists.txt)
cmakeのfind_packageではうまく見つからないので、
set (Boost_FOUND ON) set (Pangolin_FOUND ON) set (OpenCV_FOUND ON)
ということにして、includeやlibのパスをinclude_directories,() link_directories()で手動設定。
ちなみにこの3つがないと、dsoのコアライブラリまではビルドできるが、それを使ったアプリ(dso_dataset)がビルドされない。
更にちなみに、BoostはVisualStudio 2015 (vc14, v140)でビルドされているが、v140からそれ以降と互換性があるらしく今回VisualtStudio2017 (vc15, v141)だが再ビルド不要らしい。
VisualStudio2017でDSOをビルドするなら、v141のライブラリじゃないとダメだった。そのため、Boost1.66を別途x86でビルドしてリンク。
Pangolinのビルド
Releaseモード
普通にビルド。ただし、私はOpenNI2を入れていた関係でそれ関係のビルドもONになってしまい、それがリンクエラーを誘発してしばらく詰まっていた。今回OpenNI2は使わないので、CMakeの段階でOFFにする(自動的に設定されたパスを削除してConfigureする)ことで対処。
Debugモード(DSOをDebugモードで動かしたい場合は必須)
DSO.slnとライブラリの種類を合わせないと、最後のDSOビルドでpangolin.lib関連で大量のリンクエラーが発生する。
/MDとか/MTとかmsvcrt.libとかlibc.libとかlibcmt.libとか - snipsnipsnip
Pangolinプロジェクトを右クリックして[構成プロパティ]→[C/C++]→[コード生成]→[ランタイムライブラリ]を /MTd から /MDd に変更。
OpenCVのDebugモードビルド(DSOをDebugモードで動かしたい場合は必須)
thirdparty/opencvにはRelease版しか入っていない。OpenCVはDebug時はDebug用、Release時はRelease用をちゃんとリンクしないとエラーになるので、x86のDebugを自分で別途ビルド。
(配布されているPrebuiltなOpenCVはx64でしかビルドされていないので自分でビルドするしかない)
libzipのビルド
TUMのデータセットは入力画像をzipで固めているのだが、そのzipのまま画像を読み取れるようにするためにはlibzipが必要。そしてlibzipのビルドにはzlibが必要。
ということで何も考えずにWindowsでビルドできるよう環境がそろった下記を持ってきた。
GitHub - kiyolee/zlib-win-build: zlib Windows build with Visual Studio.
GitHub - kiyolee/libzip-win-build: libzip Windows build with Visual Studio.
> cd {somewhere}\ > git clone https://github.com/kiyolee/zlib-win-build.git > git clone https://github.com/kiyolee/libzip-win-build.git
上記gitのREADMEにも書いてあるが、zlib→libzipの順序でビルドする必要がある。その際、libzipのVSソリューション内のzlibへのincludeやlibのパスは自分の環境に合わせなおす必要があることに注意。
後から気づいたが、Pangolinをビルドするとthirdparty/Pangolin/build/external/にzlibがビルドされているので、これを使ってthirdparty/libzip-1.1.1.tar.gzを解凍&ビルドすればよかったかも。
DSOビルド
ようやくメインのビルド。これもそのままではビルドできない。
zipオフライン映像を入力するよう改造1
Win版forkではWebカメラで動かすように改造されているようなので、本家のmain_dso_pangolin.cppから該当箇所を逆移植。
実際の改造コードの掲載はここでは省略する。やることは次の通り:
- main関数内のcv::VideoCapture関係の記述を削除
- main関数冒頭でparseArgumentするようにし、sourceなどのハードコード指定を削除
- 画像読み込みのwhile文を削除し、本家からfor文を移植
zipオフライン映像を入力するよう改造2
src\util\DatasetReader.hの中のgetdir関数がPOSIX仕様なので、下記を参考に書き換え。
ディレクトリ内のファイルリストを得る
#ifdef _MSC_VER #include <io.h> #else #include <dirent.h> #endif inline int getdir(std::string dir, std::vector<std::string> &files) { #ifdef _MSC_VER struct _finddata_t fdata; intptr_t fh = _findfirst(dir.c_str(), &fdata); if (-1 == fh) { return -1; } do { std::string name = std::string(fdata.name); if (name != "." && name != "..") files.push_back(name); } while (0 == _findnext(fh, &fdata)); _findclose(fh); #else // (省略) #endif std::sort(files.begin(), files.end()); if(dir.at( dir.length() - 1 ) != '/') dir = dir+"/"; for(unsigned int i=0;i<files.size();i++) { if(files[i].at(0) != '/') files[i] = dir + files[i]; } return files.size(); }
usleep削除
main_dso_pangolin.cpp内にusleep()が使われているがWindowsでは使えない。
WinAPIのQueryPerformanceCounter()に代替するのが正しいのだろうが、面倒なのでひとまず単純にコメントアウト。問題が生じたら後で上の修正をすることとする…
リンクするライブラリを追加
リンクするライブラリはmain_dso_pangolin.cppの冒頭で #pragma comment で指定されている。下記のように編集。
//#include <GL/freeglut.h> #pragma comment(lib, "glew.lib") #pragma comment(lib, "libpng16.lib") #pragma comment(lib, "jpeg.lib") #pragma comment(lib, "libzip.lib") #pragma comment(lib, "glu32.lib") #pragma comment(lib, "opengl32.lib") // Boost #ifdef _DEBUG #pragma comment(lib, "libboost_thread-vc141-mt-gd-x32-1_66.lib") #pragma comment(lib, "libboost_system-vc141-mt-gd-x32-1_66.lib") #pragma comment(lib, "libboost_date_time-vc141-mt-gd-x32-1_66.lib") #pragma comment(lib, "libboost_chrono-vc141-mt-gd-x32-1_66.lib") #else #pragma comment(lib, "libboost_thread-vc141-mt-s-x32-1_66.lib") #pragma comment(lib, "libboost_system-vc141-mt-s-x32-1_66.lib") #pragma comment(lib, "libboost_date_time-vc141-mt-s-x32-1_66.lib") #pragma comment(lib, "libboost_chrono-vc141-mt-s-x32-1_66.lib") #endif // OpenCV #ifdef _DEBUG #pragma comment(lib,"opencv_core341d.lib") #pragma comment(lib,"opencv_highgui341d.lib") #pragma comment(lib,"opencv_imgproc341d.lib") #pragma comment(lib,"opencv_imgcodecs341d.lib") #pragma comment(lib,"opencv_videoio341d.lib") #else #pragma comment(lib,"opencv_core341.lib") #pragma comment(lib,"opencv_highgui341.lib") #pragma comment(lib,"opencv_imgproc341.lib") #pragma comment(lib,"opencv_imgcodecs341.lib") #pragma comment(lib,"opencv_videoio341.lib") #endif
thirdparty/Pangolin/glewがあったので、要らんやろと思いfreeglutのincludeも消したが、thirdparty/freeglutがあったことに後から気づいた。
いざビルド&実行
ビルドできた。
その後、libpng16.dll, libzip.dll, opencv_*.dllが見つかるようにパスを通すと実行できた。
Android Studio でデバッグ実行してもブレークポイントで止まらない場合の対処
タイトルの件でちょっとハマったのでメモ。
Android Studio でデバッグ実行してもブレークポイントで止まらない場合、考えられる理由は私の知っている範囲では次の3つ。まだまだ事例は他にもあるかもしれないですが。
私がハマったのは3番目の奴です。
1. AsyncTask等で別スレッドで動いている場合
そのスレッドでデバッグできるようにするために追加すべきコードがあります。
「AsyncTask ブレークポイント」等で検索すればたくさん出てきます。
2. Gradle の設定で debuggable が false になっている
「android studio ブレークポイント 止まらない」で検索すると下記サイトがヒットします。
d.hatena.ne.jp
3. Gradle の設定で minifyEnabledが true になっている
私はこれでした。debuggableが true でも minifyEnabled が false じゃないとブレークポイントでは止まらないのですね…
以上!
OpenCVを使いたいときのcmakeの書き方
次のように書けばよい。かなりシンプル。link_directoriesの指定は不要。
なぜならば、${OpenCV_LIBS}でライブラリへの絶対パスが指定されるため
OpenCVが見つかった場合のみプロジェクトを作成したければif(DEFINED ${OpenCV_VERSION})でadd_executableやtarget_link_librariesを囲めばよい
cmake_minimum_required(VERSION 2.8 FATAL_ERROR) project(opencv_project) find_package(OpenCV 2.4 REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) add_executable (project1 hoge.cpp) target_link_libraries (project1 ${OpenCV_LIBS})
一発でわかる Iterative Closest Point のコンセプト図解
2つの点群を重ね合わせる手法にICP (Iterative Closest Point) というものがあります。
1992年に発表されてから、現在に至るまであらゆる派生手法が提案されています。
ベーシックなICPの仕組みは一言で言うと「近傍点との距離の最小化を繰り返す」です。これを動画で示すと次の通りです。ちょっとずつ2つの点群の間が縮まって、最後的にぴったり合わせられるわけです。
ICPの注意点として、「初期位置合わせが必要」ということが挙げられます。言い換えると「2つの点群はある程度近い位置に配置されていないといけない」ということです。もし、初期位置合わせがされていなければ、下記の動画のように変な結果に終わってしまいます。(オレンジの破線が正しい点の対応関係を表す)
適切な初期位置合わせ+ICPで例えば次の画像のように2つの3D点群をピタッと合わせられます。
- 点群1
- 点群2
- ICP結果
Visual Studio 2013 でソリューションを開こうとするとビジーになる場合の対処
Visual Studio 2013 でソリューションを開こうとするとビジーになる場合が前からちょくちょくあったが、
今日は全く開けなくなった。何回再起動したり.suoファイルを削除したりしても治らない。
そこでちょっと調べて見つけた解決方法。私はこれで治った。
OpenCV3では視差画像が作れない?
OpenCVで視差画像を作ろうと思って調べたところ下記のサイトを発見。ちなみに2008年の情報。
Momma's Wiki: OpenCV/距離計測 - ステレオマッチング(二眼カメラ)による距離計測 ※CvCalibFilte...
これに従って、最新であるOpenCV3を使って試してみたら、cvFindStereoCorrespondence関数が未定義といって怒られる。
ちなみに、OpenCV2.4.11を使ったらできた。
OpenCV3では視差画像は作れないか、別の何かに置き換わったのかもしれない。
暇なときにOpenCV3の視差画像関連の機能を探したいと思います。