AppsJapan バーチャルテレポート メッシュ処理(詳細編) interop17
- WebRTC
- 2017/06/01
バーチャルテレポートの開発者向けの資料になります。
初見の方は、「AppsJapan バーチャルテレポート: メッシュ処理(概要編)」から閲覧ください。
今回はその「バーチャルテレポート」の中で、メッシュ処理部分について詳しくご紹介します。
PCL
PCL(Point Cloud Library)とは、点群を扱う為のライブラリ群です。
ROS(ロボットOS)や、OpenCV(画像認識)、を作っていたウィローガラージのプロジェクトのひとつとして誕生しました。
今回、マルチ深度カメラに対応したlibRealsenseを動かすにあたり、「Add Intel LibRealSense grabber and viewer #1633」にあるlebronzhang/pclをベースに開発を行いました。
メッシュの生成は下記の流れになります。
メッシュは別スレッドで処理し、データ送信時や、入力の受け取りはメインスレッドで実行しています。
入力: Realsenseからの点群データとRGBデータ
入力はpcl/visualization/tools/realsenseviewer.cppを参考に作成しました。
画像は4つの深度カメラで撮影した入力の点群
PassThrough
- 指定範囲の点を取り出す
VoxelGrid
- 中央の点を残して間引く
OutlierRemoval
- 離れた点の除去
MovingLeastSquaresOMP
- 点の配置が滑らかになるように移動
GreedyProjectionTriangulataion
- 高速三角探索(面の作成)
連続して実行していると、pcl/kdtree/impl/kdtree_flann.hppの159行目で落ちます。
- int& neighborindex = kindices[i];
回避策として、下記の対応としました。
template <typename PointT, typename Dist> int
pcl::KdTreeFLANN<PointT, Dist>::nearestKSearch (const PointT &point, int k,
std::vector<int> &k_indices,
std::vector<float> &k_distances) const
{
// assert (point_representation_->isValid (point) && "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
if (!point_representation_->isValid (point))
return(0);
VTK
- vtkFillHolesFilter
- メッシュの穴埋め
Reduction Polygon
- ポリゴン削減
- PCLのIssueではVTKのバグとなってますが、Seg Faultを起こす為Issueに習って修正しました
- pcl::MeshQuadricDecimationVTK seg fault
textureMeshwithMultipleCameras
- カメラ座標とpcl::TextureMeshを引数にメッシュにRGBデータを投影する処理
- pcl/gpu/kinfu_large_scale/tools/standalone_texture_mapping.cppを参考に作成
- 座標の単位 1.0が現実世界の1メートルとして扱われます
- 以下はカメラ座標の参考コード
Eigen::Affine3f pos2pose (const realsense::Vec3 * pos,const realsense::Vec3 * rotation)
{
Eigen::Translation3f translation(Eigen::Translation3f(pos->x(),pos->y(),pos->z()));
Eigen::Quaternionf rotateX(Eigen::AngleAxisf(rotation->x(), Eigen::Vector3f::UnitX()));
Eigen::Quaternionf rotateY(Eigen::AngleAxisf(rotation->y(), Eigen::Vector3f::UnitY()));
Eigen::Quaternionf rotateZ(Eigen::AngleAxisf(rotation->z(), Eigen::Vector3f::UnitZ()));
Eigen::DiagonalMatrix<float, 3> scaling = Eigen::Scaling(1.0f, 1.0f, 1.0f);
Eigen::Affine3f pose = translation * scaling * rotateX * rotateY * rotateZ;
return pose;
}
pcl::TextureMapping<pcl::PointXYZ>::Camera cam;
cam.pose = pos2pose(pos,rotation);
cam.focal_length = 575.816;
cam.width = 640;
cam.height = 480;
出力: FlatBuffersにメッシュとRGBを格納
Unityではメッシュをバイナリで高速に受け取る処理が用意されておりません。
ここではシンプルなデータフォーマット(OBJフォーマットベース)でバイナリ化することで対応しました。
下記はFlatBuffersのフォーマットです。
※Vec2とVec3は簡略
table Texture {
binary: [ubyte];
}
table TexMesh {
triangles: [int];
}
table Send {
vertices:[Vec3];
normals:[Vec3];
uvs: [Vec2];
meshs: [TexMesh];
textures: [Texture];
}
ここのRGBですが、libjpeg-turbo/libjpeg-turboを使い圧縮、オンメモリで処理してから格納しています。
WebRTC
WebRTC (Web Real-Time Communication)とはWorld Wide Web Consortium (W3C)が提唱するリアルタイムコミュニケーション用のAPIの定義で、プラグイン無しでウェブブラウザ間のボイスチャット、ビデオチャット、ファイル共有ができる。(WikiPedia参照)
WebRTCを使うと、ブラウザでP2Pな通信が実現できます。Googleがオープンソースとして公開しているライブラリです。
今回WebRTCを利用するにあたり、aisouard/libwebrtcを利用させていただきました。
cmakeが使えるので私はやりやすかったです。
PCLとの連携
PCLとの連携では、WebRTCを一度Shared Libraryにパッケージして利用しています。 何故Shared Libraryに分けるかと言いますと、コンパイルオプションの都合で一度にビルドするのが困難だったからになります。
個別に対応したこと
実際に、WebRTC DataChannelで101716byte(100K)ファイルを送信してみたところ、65664と36052に分割されて届きました。
また、700Kbyteのような大きなサイズを送信すると、エラーで送信自体が行われないようです。
今回は、下記のように対策を行いました。
-
65664byteで分割が行われるので64KB(65535=64x1024-1)で分割
- 別途4byteのヘッダーを付与
- 1byte: dummy
- 2byte: 1=開始,9=終了,0=継続
- 3-4byte: 65535のデータサイズを表現
Unity WebRTC Asset
Unityでのやりとりには、WebRTC Networkを利用しました。
今回、WebRTCをShared Libraryで作ったことですし、次回以降は自作するのもよさそうです。
このフリーのAssetではコードが公開されていないこともあり、シグナリングでの接続にひと手間かかりました。
-
送信データ
- データはBlob(Binary Large Object)
- 8byteのヘッダー(2byteは送信データサイズ)
- 2byteの式 (256 x head[2] + head[1])
- 開始(8byte)と終了(4byte)は独自のバイナリを送信
No Good (NG集)
参考になるかはわかりませんが困ったことを書いておきます。
-
法線が裏を向いた部分が映らない
- コミュニティで話されてた両面に表示するシェーダを利用しました。
Double Sided Material
- コミュニティで話されてた両面に表示するシェーダを利用しました。
- PCL: 右手座標系、Unity: 左手座標系と異なるため、データをコンバートする必要があります。
-
動作中のRealSense断線エラー
- USB3.0は900mAですが、4AのハブにRealSenseを3台以上で動かすと頻繁に断線が起きます。RealSenseを2台ずつ動かすことで解決
-
赤外線パターン投影、吸収・反射する素材は投影できない
- Realsense R200では髪や、黒い素材の多く、透明な素材等で赤外線を投影できませんでした。今後解消すればと思います。
- CPUをRyzen(8コア16スレッド)にすることでメッシュの処理数は上がりましたが、Linux Kernel 4.4ではスレッド周りが安定しませんでした。
現在のLibrealseneではKernel 4.4までのUVCドライバに対応となっており、今後の対応が待たれます。
現場からは以上です。インフォコム技術企画室のo2でした。