The OpenNI Grabber Framework in PCL

PCLにおけるOpenNIフレームワーク

出典: http://pointclouds.org/documentation/tutorials/openni_grabber.php#openni-grabber

PCL 1.0の時点では、さまざまなデバイスやドライバ、ファイル形式、その他のデータソースへのスムーズで便利なアクセスを提供するため、新しい汎用グラバーインタフェースを提供している。

PCLが取り入れた最初のドライバは、OpenNI互換のカメラからのデータストリームを要求する簡単な方法である新しいOpenNI Grabberである。 このチュートリアルでは、グラバーをセットアップして使用する方法を紹介している。とてもシンプルなので短い紹介で済んでいる。

これまでにテストしたカメラは、Primesense Reference Design、Microsoft Kinect、Asus Xtion Proカメラである。 http://pointclouds.org/documentation/tutorials/_images/openni_cams.jpg

簡単な例

ビジュアライゼーション項には、pcl::PointCloud <XYZ>またはpcl::PointCloud <XYZRGB>クラウドコールバックを設定するために必要なすべてのコードを含む非常に短いコードがある。

PCL OpenNI Viewerのスクリーンショットとビデオは、OpenNI Grabberを使用している。 http://pointclouds.org/documentation/tutorials/_images/pcl_openni_viewer.jpg

YouTubeビデオ: https://youtu.be/x3SaWQkPsPI

visualization/tools/openni_viewer_simple.cppからのコードを見てみよう

 #include <pcl/io/openni_grabber.h>
 #include <pcl/visualization/cloud_viewer.h>

 class SimpleOpenNIViewer
 {
   public:
     SimpleOpenNIViewer () : viewer ("PCL OpenNI Viewer") {}

     void cloud_cb_ (const pcl::PointCloud<pcl::PointXYZ>::ConstPtr &cloud)
     {
       if (!viewer.wasStopped())
         viewer.showCloud (cloud);
     }

     void run ()
     {
       pcl::Grabber* interface = new pcl::OpenNIGrabber();

       boost::function<void (const pcl::PointCloud<pcl::PointXYZ>::ConstPtr&)> f =
         boost::bind (&SimpleOpenNIViewer::cloud_cb_, this, _1);

       interface->registerCallback (f);

       interface->start ();

       while (!viewer.wasStopped())
       {
         boost::this_thread::sleep (boost::posix_time::seconds (1));
       }

       interface->stop ();
     }

     pcl::visualization::CloudViewer viewer;
 };

 int main ()
 {
   SimpleOpenNIViewer v;
   v.run ();
   return 0;
 }

見てのとおり、SimpleOpenNIViewerrun()関数は、最初に新しいOpenNIGrabberインターフェイスを作成する。 次の行は最初は少し見づらいかもしれないみかけほどひどくはない。 コールバックcloud_cb_のアドレスを使ってboost :: bindオブジェクトを作成し、SimpleOpenNIViewerと引数_1への参照を渡している。

バインドは、コールバック関数型(この場合はvoid(const pcl :: PointCloud <pcl :: PointXYZ> :: ConstPtr&))でテンプレート化されたboost::functionオブジェクトにキャストされる。 結果の関数オブジェクトは、OpenNigrabberに登録され、その後に起動することができる `stop()`メソッドは必ずしも呼び出す必要はないことに注意しよう。デストラクタがそれを処理するためである。

Additional Details

追加の詳細

OpenNIGrabberは複数のデータ型を提供する。これはGrabberインタフェースを一般的なものにしたためであり、比較的複雑なboost::bindとなってしまった。 実際、この執筆時点で、以下のコールバック型を登録することができる:

  • void (const boost::shared_ptr<const pcl::PointCloud<pcl::PointXYZRGB> >&)
  • void (const boost::shared_ptr<const pcl::PointCloud<pcl::PointXYZ> >&)
  • void (const boost::shared_ptr<openni_wrapper::Image>&)
    これは組み込みカメラからの画像だけを与える

  • void (const boost::shared_ptr<openni_wrapper::DepthImage>&)
    これは奥行き画像を与え、色や輝度情報を含まない

  • void (const boost::shared_ptr<openni_wrapper::Image>&, const boost::shared_ptr<openni_wrapper::DepthImage>&, float constant)

このタイプのコールバックが登録されると、グラバーはRGB画像と奥行き画像と定数(1/焦点距離)を送信する。この定数は、独自の視差変換を行う場合に必要である。

注: 奥行き画像ストリームを必要とするすべてのコールバックタイプでは、同期メカニズムが有効になっており、奥行きと画像データが一貫していいることを保証する。この同期には、最初の画像を送信する前に少なくとも1つの画像セットが必要なため、これは多少の時間遅れを引き起こす。

Starting and stopping streams

registerCallbackの呼び出しは、boost::signals2::connectionオブジェクトを返す。上記の例ではこれを無視していた。 ただし、1つまたは複数の登録済みデータストリームを中断したり取り消したりしたい場合は、グラバー全体を停止することなくコールバックを切断することができる。

boost::signals2::connection = interface (registerCallback (f));

// ...

if (c.connected ())
  c.disconnect ();

Benchmark

次のコードは、奥行きストリームと色ストリームの両方を受け取ろうとするもので、システムのベンチマークとして役立つものである。 あなたのコンピュータの速度が遅い場合は〜29Hz +を取得できないかもしれない。その時は問い合わせせよ。 コードをさらに最適化できるかもしれないからである。

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/common/time.h>

class SimpleOpenNIProcessor
{
public:
  void cloud_cb_ (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr &cloud)
  {
    static unsigned count = 0;
    static double last = pcl::getTime ();
    if (++count == 30)
    {
      double now = pcl::getTime ();
      std::cout << "distance of center pixel :" << cloud->points [(cloud->width >> 1) * (cloud->height + 1)].z << " mm. Average framerate: " << double(count)/double(now - last) << " Hz" <<  std::endl;
      count = 0;
      last = now;
    }
  }

  void run ()
  {
    // create a new grabber for OpenNI devices
    pcl::Grabber* interface = new pcl::OpenNIGrabber();

    // make callback function from member function
    boost::function<void (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr&)> f =
      boost::bind (&SimpleOpenNIProcessor::cloud_cb_, this, _1);

    // connect callback function for desired signal. In this case its a point cloud with color values
    boost::signals2::connection c = interface->registerCallback (f);

    // start receiving point clouds
    interface->start ();

    // wait until user quits program with Ctrl-C, but no busy-waiting -> sleep (1);
    while (true)
      boost::this_thread::sleep (boost::posix_time::seconds (1));

    // stop the grabber
    interface->stop ();
  }
};

int main ()
{
  SimpleOpenNIProcessor v;
  v.run ();
  return (0);
}

Troubleshooting

Q:デバイスが接続されていないというエラーが表示される。

[OpenNIGrabber] No devices connected. terminate called after throwing an instance of ‘pcl::PCLIOException’ what(): pcl::OpenNIGrabber::OpenNIGrabber(const std::string&) in openni_grabber.cpp @ 69: Device could not be initialized or no devices found. [1] 8709 abort openni_viewer

A:おそらくこれはXnSensorServerの問題である。 ps-engineパッケージがインストールされているだろうか?また XnSensorServerの古いプロセスが走っているのなら、そうならそれを殺(kill)してみよ。

Q:クローズドネットワーク接続に関するエラーが表示される。

terminate called after throwing an instance of ‘pcl::PCLIOException’ what(): No matching device found. openni_wrapper::OpenNIDevice::OpenNIDevice(xn::Context&, const xn::NodeInfo&, const xn::NodeInfo&, const xn::NodeInfo&, const xn::NodeInfo&) @ /home/andreas/pcl/pcl/trunk/io/src/openni_camera/openni_device.cpp @ 96 : creating depth generator failed. Reason: The network connection has been closed!

A:このエラーは、gspca_kinectカーネルモジュールを含む新しいLinuxカーネルで発生する。このモジュールはkinectのusbインタフェースを要求し、OpenNIからの要求を妨げる。カーネルモジュール(rmmod gspca_kinect)を削除するか、ブラックリストに追加せよ(つまりecho "blacklist gspca_kinect"> /etc/modprobe.d/blacklist-psengine.confをルートとして実行)。 PCLが提供するOpenNI Ubuntuパッケージにはすでにこの修正が含まれているが、他の配布でも必要になる場合がある。

Conclusion

Grabberインターフェイスは非常に強力で汎用性が高く、OpenNI対応のカメラにコードで接続することができる。 FileGrabberは、同じインターフェースを使用して使用することができる。 すべてのPoint Cloudファイルをディレクトリからロードして、特定のレートでコールバックに提供する。 必要な変更は、グラバーオブジェクトの割り当てだけである(pcl::Grabber *g = new ...;)。

PCLの中で利用したいセンサーがある場合は、pcl-developers@pointclouds.orgに知らせてほしい。