[AppsJapan]バーチャルテレポート: メッシュ処理(詳細編) #interop17


バーチャルテレポートの開発者向けの資料になります。
初見の方は、「[AppsJapan]バーチャルテレポート: メッシュ処理(概要編)」から閲覧ください。

今回はその「バーチャルテレポート」の中で、メッシュ処理部分について詳しくご紹介します。

PCL

PCL(Point Cloud Library)とは、点群を扱う為のライブラリ群です。
ROS(ロボットOS)や、OpenCV(画像認識)、を作っていたウィローガラージのプロジェクトのひとつとして誕生しました。

今回、マルチ深度カメラに対応したlibRealsenseを動かすにあたり、「Add Intel LibRealSense grabber and viewer #1633」にあるlebronzhang/pclをベースに開発を行いました。

メッシュの生成は下記の流れになります。
メッシュは別スレッドで処理し、データ送信時や、入力の受け取りはメインスレッドで実行しています。

入力: Realsenseからの点群データとRGBデータ

入力はpcl/visualization/tools/real_sense_viewer.cppを参考に作成しました。

input_pcd.png

画像は4つの深度カメラで撮影した入力の点群

PassThrough

  • 指定範囲の点を取り出す

VoxelGrid

  • 中央の点を残して間引く

voxelgrid.png

OutlierRemoval

  • 離れた点の除去

MovingLeastSquaresOMP

  • 点の配置が滑らかになるように移動

move.png

GreedyProjectionTriangulataion

  • 高速三角探索(面の作成)

連続して実行していると、pcl/kdtree/impl/kdtree_flann.hppの159行目で落ちます。

  • int& neighbor_index = k_indices[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);

gp3.png

VTK

Reduction Polygon

gp3.png

textureMeshwithMultipleCameras

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でした。

[AppsJapan]バーチャルテレポート: メッシュ処理(詳細編) #interop17


バーチャルテレポートの開発者向けの資料になります。
初見の方は、「[AppsJapan]バーチャルテレポート: メッシュ処理(概要編)」から閲覧ください。

今回はその「バーチャルテレポート」の中で、メッシュ処理部分について詳しくご紹介します。

PCL

PCL(Point Cloud Library)とは、点群を扱う為のライブラリ群です。
ROS(ロボットOS)や、OpenCV(画像認識)、を作っていたウィローガラージのプロジェクトのひとつとして誕生しました。

今回、マルチ深度カメラに対応したlibRealsenseを動かすにあたり、「Add Intel LibRealSense grabber and viewer #1633」にあるlebronzhang/pclをベースに開発を行いました。

メッシュの生成は下記の流れになります。
メッシュは別スレッドで処理し、データ送信時や、入力の受け取りはメインスレッドで実行しています。

入力: Realsenseからの点群データとRGBデータ

入力はpcl/visualization/tools/real_sense_viewer.cppを参考に作成しました。

input_pcd.png

画像は4つの深度カメラで撮影した入力の点群

PassThrough

  • 指定範囲の点を取り出す

VoxelGrid

  • 中央の点を残して間引く

voxelgrid.png

OutlierRemoval

  • 離れた点の除去

MovingLeastSquaresOMP

  • 点の配置が滑らかになるように移動

move.png

GreedyProjectionTriangulataion

  • 高速三角探索(面の作成)

連続して実行していると、pcl/kdtree/impl/kdtree_flann.hppの159行目で落ちます。

  • int& neighbor_index = k_indices[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);

gp3.png

VTK

Reduction Polygon

gp3.png

textureMeshwithMultipleCameras

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でした。

コメントする