2015年2月24日火曜日

センサから取得された点郡の不要データを除去するフィルタ

 Kinectなどのセンサは通常307200個の点郡が取れるはずであるが、あくまでも赤外線を照射して帰ってきた情報しか取得できないため、ガラスなどの点郡はとることができない。しかし、一応データは存在するものとして扱われる(QNANのような感じになり、Viewerなどでは表示されない)そういったデータは処理の邪魔でしかないので消去するべきなのでPCLではPassThroughフィルタというものがあるのでそれを使用することで消去できる。以下入力された点郡データの使用できないデータを消去するコード。

コード

#include "stdafx.h"

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
#include <pcl/io/pcd_io.h>


pcl::PointCloud<pcl::PointXYZ>::Ptr pass_through(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PassThrough<pcl::PointXYZ> pass;
    pass.setInputCloud (cloud);
    pass.setFilterFieldName ("z");
    pass.setFilterLimits (0, 10);
    //pass.setFilterLimitsNegative (true);
    pass.filter (*cloud_filtered);
    printf("%f\n",cloud->points.size());

    return cloud_filtered;
}

int _tmain(int argc, _TCHAR* argv[])
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
   
    pcl::io::loadPCDFile(argv[1], *cloud);
    //pass_through(cloud);

    std::stringstream Filename;
    std::string name;
    name = argv[1];
    name.erase(name.length()-4);
    Filename << name << "_pass.pcd";

    pcl::io::savePCDFileBinary(Filename.str() , *pass_through(cloud));

    return (0); 
}


 このフィルタは通常は表示するデータのX、Y、Zの値を元にして、閾値以上となった場合は表示しないなどの処理を行うものであるが、今回のように「存在しない」という情報を持つ点の削除を行うこともできる。 以下Z座標値が0~0.05の場合のみ表示するコード。

コード

#include "stdafx.h"

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
#include <pcl/io/pcd_io.h>


 pcl::PointCloud<pcl::PointXYZ>::Ptr pass_through(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PassThrough<pcl::PointXYZ> pass;
    pass.setInputCloud (cloud);
    pass.setFilterFieldName ("z");
    pass.setFilterLimits (0, 0.05);
    //pass.setFilterLimitsNegative (true);
    pass.filter (*cloud_filtered);
    printf("%f\n",cloud->points.size());

    return cloud_filtered;
}

int _tmain(int argc, _TCHAR* argv[])
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
   

    pcl::io::loadPCDFile(argv[1], *cloud);
    //pass_through(cloud);

   std::stringstream Filename;
    std::string name;
    name = argv[1];
    name.erase(name.length()-4);
    Filename << name << "_pass.pcd";

    pcl::io::savePCDFileBinary(Filename.str() , *pass_through(cloud));

    return (0); 
}

 

 このコードの実行結果
 

 斜体+下線+太字の部分の値を変えただけ、ものは全く同じ。
このフィルタを使えば表示する点郡の数を減らせるので処理を速くできる。(有用不要にかかわらず情報の削除を行うので注意は必要)

点郡の重心座標を計算して全体を移動させるプログラム

 通常Kinectから得られる点郡データはz軸(奥行き)方向に一定の距離があるはずだが、色々処理をしたい場合は座標(0,0,0)にあったほうがやり易かったりするので、入力点郡の重心を求め、全体をその重心の座標が(0,0,0)になるように移動するプログラムを作る。

コード

#include "stdafx.h"

#include <iostream>
#include <boost/thread/thread.hpp>
#include <pcl/common/common_headers.h>
#include <pcl/features/normal_3d.h>
#include <pcl/io/pcd_io.h>
void centroid(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
    Eigen::Vector4f xyz_centroid;
    pcl::compute3DCentroid(*cloud, xyz_centroid);//重心を計算

    for(size_t i = 0; i < cloud->points.size(); i++){
        cloud->points[i].x = cloud->points[i].x - xyz_centroid[0];//X座標の移動
        cloud->points[i].y = cloud->points[i].y - xyz_centroid[1];//Y座標の移動
        cloud->points[i].z = cloud->points[i].z - xyz_centroid[2];//Z座標の移動
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

    pcl::io::loadPCDFile(argv[1], *cloud);

    centroid(cloud);//重心計算+移動の関数に点群を渡す。

    std::stringstream Filename;
    std::string name;
    name = argv[1];
    name.erase(name.length()-4);
    Filename << name << "_C.pcd";//最初に渡されたファイル名の末尾に_Cを付加する
    pcl::io::savePCDFileBinary(Filename.str(),*cloud);
}



 このコードは前回のViewerのように引数にPCDファイルを読み込み、重心を座標(0,0,0)に移動後、別名で保存する。保存時のファイル名は引数で渡したファイル名の末尾に「_C」を加える

2015年2月23日月曜日

保存された点群の表示

 Kinectからの点群データの取得はできるようになったはずなのでそのデータを以前に作ったViewerで表示する。
 一々プログラムを書き換えて読み込むファイルを指定するのは面倒なので引数を使用してPCDファイルならエラーなく読み込めるプログラムにする。尚前のViwerには、球体を表示する関数があったがそれは削除する。

コード

#include "stdafx.h"

#include <pcl/visualization/cloud_viewer.h>
#include <iostream>
#include <pcl/io/io.h> 
#include <pcl/io/pcd_io.h>

void viewerOneOff (pcl::visualization::PCLVisualizer& viewer)
{
     viewer.setBackgroundColor (1.0, 0.5, 1.0);
}

int _tmain(int argc, _TCHAR* argv[])
{


  pcl::PointCloud<pcl::PointXYZRGBA> cloud; //点群を入れる変数
    pcl::io::loadPCDFile (argv[1], cloud);//保存されている点群データの読み込み
    pcl::visualization::CloudViewer viewer("Cloud Viewer");//Viewerの作成
    viewer.runOnVisualizationThreadOnce (viewerOneOff);//背景色の設定

    viewer.showCloud(cloud.makeShared());//点群の表示
    while (!viewer.wasStopped ())
    {
    }
    return 0;
}


変更点はいつものように斜体+下線+太字の部分。ダブルスラッシュのあとにはその行の説明。この書き方だとドラッグ&ドロップでもプログラムを起動して表示できる。

動的に点群情報を変化させたりしない場合は、このViewerに点群を操作したり除去したりする機能を追加すればいい。 下の画像は実際にこのプログラムにファイルを読み込ませた結果。本来は色付きだったけど色は黒に一括変更されているものを使用した。
pcl::PointCloud<pcl::PointXYZRGBA> cloud; の PointXYZRGBA をPointXYZ などに変更すると色情報なしで表示できるようになる。
 

Kinectからのデータ取得

 PCLではKinectから点群データを取得する際はOpenNIを用いる。

 PCLのチュートリアルではリアルタイムにデータを取得→画面に表示を行っているのみで取得された点群データは保存されないので数行コードを足してやる必要がある。コードは以下



コード

 #include "stdafx.h"

 #include <pcl/io/openni_grabber.h>
 #include <pcl/visualization/cloud_viewer.h>
 #include <pcl/io/pcd_io.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); 
         pcl::io::savePCDFileBinary("pointcloud.pcd", *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 _tmain (int argc, _TCHAR* argv[])
 {
   SimpleOpenNIViewer v;
   v.run ();
   return 0;
 }
 
 以上
 
 斜体+下線+太字にした部分はチュートリアルには無い部分でその部分で取得されたデー
タを保存する。
 このコードではプログラムが終了した瞬間のデータを保存するだけなので、起動した瞬間
から全てのデータを1フレームずつ保存した場合は保存するファイル名を動的に変えたりする
必要がある。

2015年2月22日日曜日

PCLの動作の確認

 一つ前の記事でプロジェクトの設定などは完了しているので、ちゃんと動作するかの確認のためにPCLを使った短いコードを動かしてみる。

コード
 
#include "stdafx.h"

#include <pcl/visualization/cloud_viewer.h>
#include <iostream>
#include <pcl/io/io.h> 
#include <pcl/io/pcd_io.h>
 
 
void viewerOneOff (pcl::visualization::PCLVisualizer& viewer)
{
    viewer.setBackgroundColor (0.0, 0.0, 0.8);
    pcl::PointXYZ o;
    o.x = 0;
    o.y = 0;
    o.z = 0.5;
    viewer.addSphere (o, 0.25, "sphere", 0);   
}


int _tmain(int argc, _TCHAR* argv[])
{

    pcl::visualization::CloudViewer viewer("Cloud Viewer");
    viewer.runOnVisualizationThreadOnce (viewerOneOff);
    while (!viewer.wasStopped ())
    {
    }
    return 0;
}
 
 上のコードをコンパイルして実行すると ↓のような画面が表示されるはず
 
 
簡単な説明
 
 このプログラムは画像のような球体を表示するだけで他には一切なにもしない。この球体はPCLの中にある関数の viewer.addSphere で追加される。引数の一つ目には座標、2つ目は球体の半径、3つ目はID(通常は”sphere”)、4つ目は視点の位置。
 ちなみにこのViewerは古い版で最新のバージョンのPCLではもっと多機能なViewerを使うことができる。その分コードが若干面倒になる。ただただ点群を表示するだけならこれで十分

Point Cloud Libraryのインストール

 PCLのインストールとVC++でのプロジェクトの作り方を説明する。すでにいくつかのブログでも紹介されていたりするのでそちらも参照されるといいかも。Windowsが対象なのでバージョンは1.6.0。

まずはPCLのダウンロード先へいってPCL 1.6.0 All-In-One Installerをダウンロードして実行する この部分では基本的に「Continue」とかをおしておけばOK。結構時間がかかる。PCLのインストールそのものはよく考えれば記事にする必要なかった。

 VC++でのプロジェクトの作成について解説する。
 自分は用途関係なく全ての関連ファイルを入れておくことで、後から依存関係をいじる必要がないようにしている。そのため若干処理に時間がかかる。また環境によって動作のするしないは変わるので留意してほしい

プロジェクトの作成
  1.  「ファイル(F)」→「新規作成(N)」→「プロジェクト(P)」で画面を開く
  2. 表示された画面で「Win32コンソールアプリケーション」を選択、名前と場所は適当に設定する。 (今回作ったものを大元にして、あとのプロジェクトは「コピペ+名前変更」などを行うと依存関係の設定が不要になって楽なので「Template」とかにしておくといいかも)
  3. 「Win32アプリケーションウィザード」が表示されるので「次へ」を押す
  4.  「アプリケーションの設定」の画面になる。なにも選択しないで「完了」を押す。もし「空のプロジェクト」にチェックが入っている場合は外しておく
  5.  プロジェクトの作成はこれで完了

 次にプロジェクトの設定について解説する。この部分の設定を誤るとコンパイル中にエラーが出たりする。 要所々々で「適用しますか?」的なことを聞かれたらOKを押す

プロジェクトの設定
  1. VC++の「ソリューションエクスプローラー」内のプロジェクトの名前の部分(この場合は「Template」)を右クリックして一番下の「プロパティ」をクリックする
  2.  新しい画面が表示されるので左上の「構成(C)」の右にあるプルダウンリストから「全ての構成」を選ぶ
  3.  画面左側のリストから「構成プロパティ」を選択する
  4. 右側画面の「プロジェクトの規定値」の「文字セット」を 「マルチバイト文字セットを使用する」に変更
  5. 左側「構成プロパティ」→「C/C++」、右側「追加のインクルードディレクトリ」に
    C:\Program Files (x86)\PCL 1.6.0\include\pcl-1.6;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\Boost\include;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\Eigen\include;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\FLANN\include;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\Qhull\include\libqhullcpp;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\VTK\include\vtk-5.8;
    C:\Program Files (x86)\OpenNI\Include;
    を書き込む。*この部分はPCLをインストールした場所によって変わるので、PCLとOpenNIがインストールされた場所を確認して上記の項目を変更する。またバージョンが違ってることもあるので注意
  6. 左側「構成プロパティ」→「リンカー」、右側「追加のライブラリディレクトリ」に
    C:\Program Files (x86)\PCL 1.6.0\lib;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\Boost\lib;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\FLANN\lib;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\Qhull\lib;
    C:\Program Files (x86)\PCL 1.6.0\3rdParty\VTK\lib\vtk-5.8;
    C:\Program Files (x86)\OpenNI\Lib
    を書き込む。*この部分もPCLをインストールした場所によって変わるので、PCLとOpenNIがインストールされた場所を確認して上記の項目を変更する。またバージョンが違ってることもあるので注意
  7. 画面左上「構成(C)」の右にあるプルダウンリストから「Debug」を選択する
  8. 画面左「構成プロパティ」→「リンカー」→「入力」、画面右「追加の依存ファイル」に
    pcl_apps_debug.lib;
    pcl_common_debug.lib;
    pcl_features_debug.lib;
    pcl_filters_debug.lib;
    pcl_io_debug.lib;
    pcl_io_ply_debug.lib;
    pcl_kdtree_debug.lib;
    pcl_keypoints_debug.lib;
    pcl_octree_debug.lib;
    pcl_registration_debug.lib;
    pcl_sample_consensus_debug.lib;
    pcl_segmentation_debug.lib;
    pcl_search_debug.lib;
    pcl_surface_debug.lib;
    pcl_tracking_debug.lib;
    pcl_visualization_debug.lib;
    flann-gd.lib;
    flann_s-gd.lib;
    flann_cuda_s-gd.lib;
    flann_cpp_sgd.lib;
    qhull6_d.lib;
    qhullcpp_d.lib;
    qhullstatic_d.lib;
    qhullstatic_p_d.lib;
    openNI.lib;
    OpenNI.jni.lib;
    NiSampleModule.lib;
    NiSampleExtensionModule.lib;
    MapReduceMPI-gd.lib;
    mpistubs-gd.lib;
    vtkalglib-gd.lib;
    vtkCharts-gd.lib;
    vtkCommon-gd.lib;
    vtkDICOMParser-gd.lib;
    vtkexoIIc-gd.lib;
    vtkexpat-gd.lib;
    vtkFiltering-gd.lib;
    vtkfreetype-gd.lib;
    vtkftgl-gd.lib;
    vtkGenericFiltering-gd.lib;
    vtkGeovis-gd.lib;
    vtkGraphics-gd.lib;
    vtkHybrid-gd.lib;
    vtkImaging-gd.lib;
    vtkInfovis-gd.lib;
    vtkIO-gd.lib;
    vtkjpeg-gd.lib;
    vtklibxml2-gd.lib;
    vtkmetaio-gd.lib;
    vtkNetCDF-gd.lib;
    vtkNetCDF_cxx-gd.lib;
    vtkpng-gd.lib;
    vtkproj4-gd.lib;
    vtkRendering-gd.lib;
    vtksqlite-gd.lib;
    vtksys-gd.lib;
    vtktiff-gd.lib;
    vtkverdict-gd.lib;
    vtkViews-gd.lib;
    vtkVolumeRendering-gd.lib;
    vtkWidgets-gd.lib;
    vtkzlib-gd.lib;
    opengl32.lib;
    以上を書き込む。
  9. 画面左上「構成(C)」の右にあるプルダウンリストから「Release」を選択する
  10. 画面左「構成プロパティ」→「リンカー」→「入力」、画面右「追加の依存ファイル」に
    pcl_apps_release.lib;
    pcl_common_release.lib;
    pcl_features_release.lib;
    pcl_filters_release.lib;
    pcl_io_release.lib;
    pcl_io_ply_release.lib;
    pcl_kdtree_release.lib;
    pcl_keypoints_release.lib;
    pcl_octree_release.lib;
    pcl_registration_release.lib;
    pcl_sample_consensus_release.lib;
    pcl_segmentation_release.lib;
    pcl_search_release.lib;
    pcl_surface_release.lib;
    pcl_tracking_release.lib;
    pcl_visualization_release.lib;
    flann.lib;flann_s.lib;
    flann_cuda_s.lib;
    flann_cpp_s.lib;
    qhull6.lib;
    qhullcpp.lib;
    qhullstatic.lib;
    qhullstatic_p.lib;
    openNI.lib;
    OpenNI.jni.lib;
    NiSampleModule.lib;
    NiSampleExtensionModule.lib;
    MapReduceMPI.lib;
    mpistubs.lib;
    vtkalglib.lib;
    vtkCharts.lib;
    vtkCommon.lib;
    vtkDICOMParser.lib;
    vtkexoIIc.lib;
    vtkexpat.lib;
    vtkFiltering.lib;
    vtkfreetype.lib;
    vtkftgl.lib;
    vtkGenericFiltering.lib;
    vtkGeovis.lib;
    vtkGraphics.lib;
    vtkHybrid.lib;
    vtkImaging.lib;
    vtkInfovis.lib;
    vtkIO.lib;
    vtkjpeg.lib;
    vtklibxml2.lib;
    vtkmetaio.lib;
    vtkNetCDF.lib;
    vtkNetCDF_cxx.lib;
    vtkpng.lib;
    vtkproj4.lib;
    vtkRendering.lib;
    vtksqlite.lib;
    vtksys.lib;
    vtktiff.lib;
    vtkverdict.lib;
    vtkViews.lib;
    vtkVolumeRendering.lib;
    vtkWidgets.lib;
    vtkzlib.lib;
    opengl32.lib;
    以上を書き込む
  11. 適用を押して終了
以上でプロジェクトの設定は終了。
今回行った設定でプログラムを実行できるかはサンプルコードなどを書いて実行すればわかると思う。








このブログについて

 一発目の投稿

 とりあえず3次元のビジョンをメインでやってたので、はじめのうちはやってたことの確認みたいな内容になると思う。尚載せるコードは極めて短いものとする。長いコード書き出すとだれてきそうなので

 とりあえず今回は使用する可能性があるものを挙げて説明する。 

・そこそこの性能のPC
 ビジョン系の処理は2次元、3次元問わず負荷が大きくなるのでそれなりのPCが必要。OSはWindowsかUBUNTUを使う。

・Kinect
 言わずと知れたMicrosoft社が発売したセンサー2次元の画像だけでなく色情報付の3次元点郡を取得することができる。自分が持っているのはXBOX ONEのKinect2ではない 360のモデルのやつ

・Point Cloud Library (PCL)
 ここ数年で徐々に有名になってきている点郡を扱うライブラリ。チュートリアルだけでもそれなりのプログラムが組めてしまったりする。

・Microsoft Visual Studio 2010
 上記したPCLを使用してプログラムを作る際に必要。もしかしたらUBUNTUを使うかもしれないのでその場合はこれは不要

 以上、もちろん今後変更があると思う。PCのセットアップとVisual Studioのインストールの過程はカットする。特に特殊なことはしない