Open3Dで点群を表示してみよう(Windows, C++)
前章でWindowsでVisual Studio環境のC++でOpen3Dライブラリを導入する方法について見て来ました:
ビルド手順が少し複雑なのですが、最終成果物のOpen3D.libとOpen3D.dllを無事Visual Studioに組み入れる事ができました。これでC++側のAPIを呼び放題です。
そこで今回は手始めとして点群データをウィンドウに表示してみます。これ、思っている以上に簡単です。尚、既にOpen3DのAPIを呼べる環境が出来ている前提で話を進めます。環境がまだな方は上記のリンクの手順で整えて下さい。執筆時点(2023.8)時点でのOpen3Dのバージョンは0.17.0です。
Geometryクラス
Open3Dは2D及び3Dモデルのデータを扱うライブラリで、様々な形状のデータに対応しています。それらはGeometryクラスという基底クラスから派生したクラスでまとめられています。Open3DのC++リファレンスのGeometryに派生クラスの一覧が掲載されています:
大切なのでクラス図を抜き出して掲載します:
2D関連はGeometry2D、3DはGeometry3Dクラスを継承しています。3DについてもいわゆるポリゴンであるMeshBaseだけでなくPointCloud(点群)、LineSet(線分の塊)なども扱えます。
今回はこの中からPointCloudクラスをピックアップします。
点群データを管理するPointCloudクラス
PointCloudクラスは点群をひとまとめで管理するデータクラスです。点の座標等の基礎データだけでなく、その点群を通した様々な解析データ(境界ボックス、中心点など)や統計データ(マハラノビス距離など)を取得する事もできます。インデックスリストを指定する事で点群の一部を取り出して新しいPointCloudオブジェクトも作ってくれますし、点群から代表的な平面を抽出して平面のパラメータを返すなどもしてくるようです。下記に並ぶAPIを見る限りかなり多機能です:
と言っても今回は描画が目的なので、PointCloudクラスの細かな機能を使うのではなくて、一番基礎的な点群データを拝借するだけです。PointCloudクラスは以下の点群データをサポートしています:
座標リスト(std::vector<Eigen::Vector3d>)
法線リスト(std::vector<Eigen::Vector3d>)
カラーリスト(std::vector<Eigen::Vector3d>)
共分散行列リスト(std::vector<Eigen::Matrix3d>)
各点の座標と法線(点の正面方向)、色があるわけです。分かり良いですよね。尚、一番下の共分散行列は統計データを取る際に使う情報で描画に必須ではありません。
要は(座標、法線、カラー)のワンセットをPointCloudオブジェクトに登録して「描画よろしく!」と頼めば描画してくれるわけです。ではその描画担当者を見てみましょう。
簡易描画をするvisualization関数群
Open3Dは描画機能も持ち合わせています。色々な描画方法がありますが、とりあえずパッと表示したい時に重宝するのがvisualization関数群です:
open3d::visualization名前空間にまとめられています。名前空間内にあるDraw系関数にGeometryオブジェクトを渡すとウィンドウを起動してモデルを描画してくれます。非常にシンプルです。
という事で前情報を整理した所で、早速点群を描画してみましょう。
点群を描画
点群データをロード
まずは描画したい点群データをPointCloudに登録したいわけですが…、まぁ点群データなんてそう身近にある物でも無いですよね(^-^;。そこでOpen3Dが提供してくれるサンプル点群データを拝借します。前回のテストでも触れたのですが、
open3d::data::PLYPointCloud cloudFilePath;
open3d::data名前空間下にあるPLYPointCloudクラスのオブジェクトを作ると、Open3Dの点群サンプルデータの一つである「fragment.ply」を自動的に用意してくれます。PC内に無い場合はgitからダウンロードまでしてくれるという至れり尽くせり(gitがPCに入っていないといけませんが)。
ただPLYPointCloudクラスはGeometryクラスの派生クラスでは無いので描画は出来ません。このクラスはDownloadDatasetというクラスを継承していて、ダウンロードされたデータへのパスを保持しているだけのクラスなんです。このクラスが教えてくれるfragment.plyファイルへのパスを使って点群をロードしないといけないんですね。
点群をロードする関数も勿論用意されています。fragment.plyファイルパスを取得して点群をロードしてPointCloudオブジェクトに登録する一連のコードは以下のようになります:
auto cloudObj = std::make_shared< open3d::geometry::PointCloud >();
bool res = open3d::io::ReadPointCloudFromPLY(
open3d::data::PLYPointCloud().GetPath(),
*cloudObj,
open3d::io::ReadPointCloudOption() );
まず空のPointCloudオブジェクトを一つ作っています。Open3Dは基本的に生オブジェクトは扱わずstd::shared_ptrでオブジェクトの寿命を管理しています。その為上ではstd::make_shared関数を通してshared_ptr<PointCloud>を作っているんです。もちろん、
std::shared_ptr< open3d::geometry::PointCloud > cloudObj( new open3d::geometry::PointCloud );
上のように直接shared_ptr<PointCloud>を作っても構いません。ただ、ちと冗長なんですよね(^-^;
次にopen3d::io::ReadPointCloudFromPLY関数でplyフォーマットの点群データをファイルから読み込みます。
第1引数はファイルへのパスです。PLYPoinyCloud::GetPathメソッドがダイレクトにfragment.plyのフルパスを提供してくれます。
第2引数はデータを受け取るPointCloudオブジェクトを渡します。ここはオブジェクトへの参照になっているので*cloudObjと参照渡ししています。
第3引数は読み込み時のオプションをReadPointCloudOption構造体で指定します。ReadPointCloudOption構造体は、
struct ReadPointCloudOption {
std::string format;
bool remove_nan_points;
bool remove_infinite_points;
bool print_progress;
std::function<bool(double)> update_progress;
};
5つのメンバからなっていますが、コンストラクタでデフォルトの値にセットしてくれて、それでよろしくやってくれます。
ReadPointCloudFromPLY関数が成功したら戻り値にtrueが返り、第2引数に渡したPointCloudオブジェクトに点データがみっちり詰め込まれます。
点群を描画
有効なPointCloudができたら後は、
open3d::visualization::DrawGeometries( { cloudObj } );
DrawGeometries関数にGeometryオブジェクトを渡すと描画してくれます。関数の引数にはshared_ptr<Geometry>のvectorを渡しますが、上のように大括弧にオブジェクトを並べると初期化子が走ってvecor配列をその場で作ってくれます。これはC++11で導入された配列の初期化子リストです:
C++も多言語に負けず超便利に進化しています(^-^)
実行コード
長々説明してきましたが、最終的なコードは以下の通りです:
<main.cpp>
#include <Open3D.h>
int main() {
auto cloudObj = std::make_shared< open3d::geometry::PointCloud >();
bool res = open3d::io::ReadPointCloudFromPLY(
open3d::data::PLYPointCloud().GetPath(),
*cloudObj,
open3d::io::ReadPointCloudOption() );
if ( res )
open3d::visualization::DrawGeometries( { cloudObj } );
return 0;
}
実行すると点群が描画されます:
Open3Dのビューワーはマウスでぐりぐりも出来るし拡縮も出来るしと、必要最低限の機能がちゃんと搭載されているので良いですね。
注意点
Open3DのビューワーはDrawを呼び出したスレッドをブロッキングします。上のウィンドウ表示等はすべてOpen3D内のサブスレッドで行われているんです。ウィンドウを閉じるとブロックが解除されスレッドが先に進みます。メインスレッドでDrawを呼ぶとメインの動作が止まりますのでご注意ください。
終わりに
今回はOpen3Dの描画機能を使って点群を描画してみました。前知識は少し必要ですが、描画プロセスはかなりシンプルです。ビューワー周りを真っ先に取り上げたのはこの先Open3Dを扱う時に「モデルがどういう状態になっているか?」を確認できないとシンドイからです。見える化って大事ですものね。
さて次はPointCloudクラスの機能で遊んでみようと思います。
ではまた(^-^)/
この記事が気に入ったらサポートをしてみませんか?