投稿

4月, 2020の投稿を表示しています

Open3Dのビルド

イメージ
3次元点群の処理だと一昔前はPCLが幅を利かせていたが、最近はOpen3Dが優勢なように思う。PCLに比べてOpen3Dは大分使い勝手が良いのが理由だと思う。 ということで、Open3Dをビルドしてみた。 まず、ソースコードを以下からgit cloneしてくる。 Open3D https://github.com/intel-isl/Open3D その後、以下のようにsubmodule updateする。 git submodule update --init --recursive 後は、Open3DのページにあるようにCMakeでVisual StudioのSolutionを生成し、ビルドするとライブラリが生成される。 Compiling from source http://www.open3d.org/docs/release/compilation.html#windows 以下は、exampleの中にあるRegistrationRANSACの実行結果。2つのPoint Cloudを位置合わせしている。 入力 結果(左は法線情報の可視化結果)

3次元相似変換(Exponential Map)の実装

イメージ
Visual SLAMでは、スケールの不定性があるため、スケールドリフトと呼ばれる問題が生じ、Loop Closureなどで蓄積誤差を解消するにあたって3次元の剛体変換ではうまく補正できない場合がある。このような場合に、3次元相似変換Sim3を用いて補正を行う方法が提案されている。 Scale Drift-Aware Large Scale Monocular SLAM http://roboticsproceedings.org/rss06/p10.pdf Sim3のExponential Mapは上記論文や以下の記事を参照してください。(書くのが面倒なので。。。) Sim(3) optimization の exponential map を計算してみた 3次元の相似変換では以下のように回転成分にスケール要素が掛かった形になる。 ここで、exp Sim(3) 、exp SO(3) はそれぞれSim(3)、SO(3)のExponential Mapを表す。 W の中身については、論文や上記記事を参照してください。 Sophusを用いた実装例は、以下のようになる。 #include <iostream> #include "sophus/sim3.hpp" #include "sophus/geometry.hpp" int main() { // 単位行列 Sophus::Sim3d T0; std::cout << "T0:" << std::endl; std::cout << T0.matrix() << std::endl; // 7次元のパラメータベクトル Sophus::Vector7d uws; uws << 10.0, 0.0, 0.0, Sophus::Constants ::pi(), 0.0, 0.0, 0.0; Sophus::Sim3d T1 = Sophus::Sim3d::exp(uws); // スケーリング double scale = 2.0; T1.setScale(scale); std::cout &l

3次元の剛体変換(Exponential Map)

イメージ
3次元の回転(Exponential Map) ではSO(3)による回転表現について、簡単な説明とC++のライブラリSophusを用いた実装例について紹介した。今回は、併進を加えた3次元の剛体変換SE(3)について、簡単に紹介する。 SE(3)のExponential Mapは6次元のベクトル( u ω ) T を用いて以下のように表される。ここで、 ω はSO(3)の時と同様に回転の軸角表現となっている。 逆変換のLogarithm Mapは、まずSO(3)のLogarithm Mapを用いてωを算出し、その後、このωを用いて以下のように算出できる。 また、以下のような2つの変換行列T 1 ,T 2 の補間を行いたい場合は、次のように中間の剛体変換を算出することができる。 ここで、wは重みで0~1の値をとる。この補間方法を用いることで、2つのカメラ視点の中間視点などを算出できるようになる。 以下に、Sophusを用いたSE(3)の実装例を示す。 #include <iostream> #include "sophus/geometry.hpp" int main() { // 単位行列 Sophus::SE3d T0; std::cout << "T0:\n" << T0.matrix() << std::endl; // パラメータベクトルse3(後ろ3つが回転so3と対応) Sophus::Vector6d uw1; uw1 << 0.0, 0.0, 0.0, Sophus::Constants ::pi() / 3, 0.0, 0.0; // x軸周りに60度回転させる変換行列 Sophus::SE3d T1 = Sophus::SE3d::exp(uw1); std::cout << "Transform matrix" << std::endl; std::cout << T1.matrix() << std::endl; std::cout << std::endl; //変換行列2 So

画像1枚から全身アバターを作る

イメージ
ICCV2019で発表された画像1枚から3次元の人物を復元することができるPIFuというアルゴリズム、そのうち試してみようと思っていて放置していたけれど、ようやく試してみた。 PIFu: Pixel-Aligned Implicit Function for High-Resolution Clothed Human Digitization https://shunsukesaito.github.io/PIFu/ 画像1枚から人物のモデルを生成する手法はMPIの以下のグループなどが様々な手法を提案している。 REAL VIRTUAL HUMANS https://virtualhumans.mpi-inf.mpg.de/ これまでの手法とPIFuとの違いは、従来の手法はSMPLなどのテンプレートモデルをベースに復元処理を実行するのに対して、PIFuはImplicit Function(陰関数)の表現を学習させるところにある(たぶん)。これにより、従来の手法ではスカートやダボっとした服装など裸のSMPLモデルから外れるようなケースではうまく復元できないという問題を解決している。 とりあえず、以下よりコードをクローンしてくる。 https://github.com/shunsukesaito/PIFu その後、AnacondaでPIFu用の環境を作成し、以下のように必要なライブラリをインストールする。 pip install torch==1.5.0+cu101 torchvision==0.6.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html pip install pillow pip install opencv-python pip install scikit-image pip install tqdm また、PIFu/checkpointsフォルダを作成し、ここに学習済みのモデルをダウンロードする。ダウンロードはPIFu/scriptsにあるdownload_trained_model.shを利用するか、手動でdownload_trained_model.shに記載されているアドレスからダウンロードする。 ここまでで、利用する準備は完了。PIF

顔写真からアバターを作ってみる

イメージ
最近、コロナウイルスの影響で、色々なイベントがオンラインやVR開催になってきている。VR開催の場合、自分自身のアバターを使いたいというような要望も多いようで、Photogrammetryの技術を使って全身を撮影したデータから3次元復元してアバターを作るサービスなどもあるよう。実際に撮影してみたい気持ちもあるが、緊急事態宣言が出されている状況では撮影スタジオに行くのもどうかと思うので、別の方法で作成してみることにした。 色々と調べていると、有料だけれどもCharacter Creatorというソフトウェアがよさそう。このソフトウェアのHeadShotというプラグイン(別売り)を使うと顔写真1枚からアニメーション可能な頭部のモデルを作ってくれる。しかも2020年4月30日までの限定で全ての製品が半額になるキャンペーンをしている。今年はゴールデンウイークもまだ緊急事態宣言期間中なので、自宅に引きこもることになるのでキャラクターアニメーションの勉強のためにも購入することにした。 Character Creator https://www.reallusion.com/store/product.html?l=4&p=cc どんなことができるかはこの動画を見ると分かるかと思う。 Wikipediaの安倍総理大臣の顔写真から作った3次元モデルは以下のようになった。 画像をドラッグアンドドロップするだけでこのクオリティで生成できるのはすごい。また、処理の過程を見ていると、まずは髪の毛無しの頭部を復元して、後から髪のモデルを生成しているよう。 生成したモデルは、以下のように顔の表情を変えることもできる。 アバターに関しては、頭部のみパーソナライズされていれば、首から下はいくつかのテンプレートが用意されていれば十分な気がする。 今回は、全自動で頭部のモデルを生成する「AUTO」モードを利用したけれど、「Pro」モードについてもどういったことができるのかこれから試してみようと思う。 ところで、AR/VRでリアルなアバターの需要ってどのくらいあるんだろうか。有名企業がDigital Humanのプロジェクトをやっているのを見ると、それなりに需要はあるのかな。

ブラウザ上で画像処理

Android, iOS, Windowsなどのデバイスに依存せずにウェブブラウザ上で画像処理をやってみたいなと思って、少しやってみた。 画像処理はやっぱり慣れ親しんでいるOpenCVを使いたいので、OpenCVを利用する。実現するためには、OpenCV.jsというOpenCVのJavascript版を利用すると良いよう。ただ、OpenCV.jsのビルドは色々と面倒なようなので、今回は以下のような感じでダウンロードしたものを利用した。 curl https://docs.opencv.org/4.3.0/opencv.js -o opencv.js OpenCV.jsのサンプルについては、OpenCVの以下のディレクトリにいくつかあるので、これを参考にすればよさそう。 \opencv\doc\js_tutorials 今回は、動画ファイルを読み込んでグレースケールに変換する処理を試してみた。 ソースは以下の通り。このhtmlフィアルとその他の必要なファイル(utils.js, cup.mp4(入力動画), opencv.js, js_example_style.css)をサーバ上にアップするとスマホのウェブブラウザ上で入力の動画ファイルがグレースケールに変換されて出力されるのが確認できた。 <! DOCTYPE   html > < html > < head > < meta   charset = "utf-8" > < title > Gray Scale </ title > < link   href = "js_example_style.css"   rel = "stylesheet"   type = "text/css"   /> </ head > < body > < h2 > Gray Scale </ h2 > < div > < div   class = "control" >< button   id = &quo

三次元の回転(Exponential Map)

イメージ
SLAMの論文を読んでいると三次元の回転表現としてExponential Mapを使った表現を用いているものが多くある。Exponential Mapを用いることで、回転行列や同時変換行列を加法的に閉じた空間へ写像することが可能になる。これにより、2点間の変換パラメータを線形に補間することが可能になる。この辺の話は、以下の文献に詳しく書かれている。 Lie Groups for 2D and 3D Transformations Exponential Mapを用いた3次元回転群SO(3)は3次元ベクトル ω (ベクトルの方向が回転軸、ノルムが回転量θ)を用いて以下のように表すことができる。 また、SO(3)のExponential Mapの逆変換であるLogarithm Mapは以下のように表される。 Exponential Mapを取り扱うことができるライブラリを探していたところ、C++のライブラリとしてSophusというものが比較的簡単に導入できそうなので試してみた。 Sophus SophusはHeader Onlyのライブラリであり、依存しているのもEigenのみなので、SophusとEigenをinclude directoryに指定するだけで利用可能になる。以下は、3次元回転群SO(3)をSophusを用いて利用する例である。 #include <iostream> #include "sophus/geometry.hpp" int main() {   // 単位行列   Sophus::SO3d R0;   std::cout << "R0:\n" << R0.matrix() << std::endl;   // x軸周りに180度回転   const double kPi = Sophus::Constants<double>::pi();   Sophus::Vector3d w(kPi, 0.0, 0.0);   Sophus::SO3d R_x_pi = Sophus::SO3d::exp(w);   std::cout << "R_x_pi:\n" <<

心拍数をカメラで計測してみる

イメージ
最近、スマホのカメラを使って心拍数を計測することができるアプリが色々とあることを知った。いくつかの方法があるようだけれど、簡単そうなアルゴリズムをOpenCVを使って実装してみた。実装したアルゴリズムは、かなり単純で以下の手順で画像から心拍数を算出する。 入力動画ファイルの準備 スマホのライトを点灯した状態でカメラに指を押し付け動画を撮影する。ほとんどのアプリがこの方法で画像を取得している。長時間やると熱を持ってくるので注意が必要。撮影された動画は以下のような感じ。 撮影した動画をPCに取り込む。 画像処理 動画像の各フレームに対してメディアンフィルタをかける。 各フレームの緑(G)成分の平均輝度を算出する。算出された平均輝度値をプロットすると以下のようになる。ここでは、cv::plot::Plot2dを使ってプロットしてみた。このように、プロットした結果を見ると山と谷がはっきりと確認できる。 上記結果の谷の部分を検出する。今回は一定のウィンドウ内で最小となっている点を谷として検出した。 検出された谷の間隔とフレームレートから心拍数を算出する。算出された心拍数は以下の通り。心拍数はおよそ60になった。途中おかしな値が出ているが、これは谷の検出に失敗しているためで、何らかの異常値除去を行う必要がある。 今回、動画像から心拍数を算出する方法を試してみたが、単純な方法でも公開されているアプリと同じような結果を得ることができた。ただ、この方法で、どの程度の精度で算出できているのかは不明。

3次元空間の回転(ロドリゲス)

イメージ
最近、以下の本を読みながら3次元の回転について復習している。 3次元回転:パラメータ計算とリー代数による最適化 金谷先生の本は、各式の展開や意味など丁寧に説明してあり非常に分かりやすい。 SLAMなどの理論を理解し、実装するためには必須の項目なのでこれを機にきっちりと理解したい。 3次元回転のオイラー角による表現については、高校数学で習ったことがあるかもしれない。ただし、オイラー角には各軸周りの回転をどの順番で掛け合わせるのか自由度があるという問題やジンバルロックの問題があることが知られている。 なので、今回は上記問題が無い回転軸と回転角によって3次元の回転を表現する方法を紹介する。回転軸と回転角によって表現するロドリゲス(ロドリーグ)の式を紹介する。ロドリゲスの式では3つのパラメータK=[k 1 , k 2 , k 3 ]を用いて以下のように3次元の回転を表すことができる。 ここで、ロドリゲスの式では回転軸(x, y, z)と回転角(θ)の4つのパラメータがあるように見えるが、回転軸を表すベクトルのノルムは1という制約があるため、この方法による3次元回転の自由度は3となっている。 OpenCVやEigenなどには、ロドリゲスの式を使った3次元回転を取り扱うための方法がすでに実装されているので、それらを用いると簡単に3次元回転をプログラム上で実装することができる。以下は、Eigenを用いた場合の例である。x軸(1, 0, 0)周りに180度(π)回転させる例となっている。 #define _USE_MATH_DEFINES #include <cmath> #include <iostream> #include "Eigen/Core" #include "Eigen/Geometry" int main() {   Eigen::Vector3d rotation_axis;   rotation_axis << 1.0, 0.0, 0.0;   double angle = M_PI;   Eigen::AngleAxis<double> angle_axis(M_PI, rotation_axis);   std::cout &

Windows Remote Desktop

イメージ
コロナウイルスの影響で、在宅勤務になり自宅から会社のPCに接続して作業をしているのだが、WindowsのリモートデスクトップではOpenGLを使ったアプリケーションが起動できないという問題があり不便さを感じていた。(古いOpenGLを使っている場合は起動できるが、ほとんどのアプリケーションが起動不可だった) 解決策として、以下の記事にあるように、一旦リモートデスクトップ接続を切断した後、アプリケーションを立ち上げ、再度リモートデスクトップ接続を行うという手段をこれまでは取っていた。 リモートデスクトップで OpenGL アプリを操作したい ただ、最近、NVIDIAのGPUを使っている場合には、OpenGLの起動を可能にする方法があることを教えてもらった。その方法は、以下のNVIDIAのサイトにある「Accelerate Windows Remote Desktop」からファイルをダウンロードしインストールし、インストール後に再起動するというだけの簡単な方法だった。 https://developer.nvidia.com/designworks 再起動後は、リモートデスクトップ環境でBlenderなどのソフトが利用できるようになった。これでこれまでよりも快適に作業ができるはず。

三次元の変換行列

イメージ
SLAMなどの三次元復元やカメラの位置・姿勢推定では、三次元の変換行列を取り扱う場面が多くある。剛体変換の場合、三次元の変換行列は4×4の行列で表現でき、以下のように左上の3×3成分が回転行列 R 、4列目の上から3つが平行移動成分 t となっている。 これらのパラメータをプログラム内部でどのように保持するのが良いか悩ましい場合がある。特に、 R や t が別々の変数で保持されている場合に、どの座標系からどの座標系への変換なのかが不明な場合がある。(オープンソースのSLAMで、 R と t が同じ変換の向きで無い場合があったりする) 何が最善か分からないが、OpenCVを使う場合は、Affine3を利用するのが色々と問題も少なく良いような気がする。以下に、Affine3fを使った三次元変換行列の一例を示す。 #include <iostream> #include <opencv2/opencv.hpp> int main(int argc, char** argv) { cv::Vec3f rvect1(0.0f, 0.0f, 0.0f); cv::Vec3f tvect1(1.0f, 1.0f, 1.0f); cv::Affine3f T1(rvect1, tvect1); std::cout << "rotation matrix" << std::endl; std::cout << T1.rotation() << std::endl; std::cout << "translation" << std::endl; std::cout << T1.translation() << std::endl; std::cout << "4x4 matrix" << std::endl; std::cout << T1.matrix << std::endl; cv::Vec3f rvect2(1.0f, 1.0f, 0.0f); cv::Vec3f tvect2(1.

HD画質でキャプチャが出来ない

イメージ
Logicoolの c920 を使って、OpenCVで1920x1080の画像をキャプチャしようとしているのだけれどうまくいかない。調べてみると他にも同じ状況の方がいるよう。 http://kujiraiken.sit.ac.jp/blog/2018/09/opencv-cap_prop_frame_width-problem/ この方のブログと同様に、以下のように解像度を指定しても解像度は1920x1080になるが、キャプチャされる画像は低解像度の画像を引き延ばしたような4:3の画像になっており、左右には黒い余白が配置されている。上記ブログによると、長年未解決の問題らしい。。。 他にも色々と調べていると、これまでカメラデバイスをオープンする際に、デバイス番号のみを指定していたが、DirectShowを使ったキャプチャやMedia Foundationを使ったキャプチャを指定できるらしい。指定方法は簡単で、以下のように記述すれば良い。 cv::VideoCapture vcap_.open(0 + cv::CAP_DSHOW); // DirectShow cv::VideoCapture vcap_.open(0 + cv::CAP_MSMF); // Media Foundation 結果は以下のようになった。 CAP_DSHOW CAP_MSMF DirectShowベースのキャプチャをすると16:9の画像キャプチャできていることが確認できる。解像度も1920x1080で指定ができた。ただ、高解像度の画像をキャプチャする場合にはフレームレートが極端に落ちる。なんでだろう。。。 高解像度の画像をキャプチャしたい場合は、おとなしくOpenCV以外の方法を利用すべきなのかもしれない。 余談だが、CAP_DSHOWを利用する場合、以下を用いてカメラの各パラメータを設定するためのダイアログを呼び出すことができる。これはちょっと便利。 vcap_.set(cv::CAP_PROP_SETTINGS, 0);