PCL 室内三维重建

 

PCL 室内三维重建

http://blog.csdn.net/jinshengtao/article/details/48917263

分类: Kinect开发   566人阅读  评论(0)  收藏  举报
kinect 点云 三维重建

手头有三个prime sensor摄像头,分别固定在不同角度,打算根据RGBD信息,将三个摄像头的点云数据拼接起来。

设备限制+能力不足,一直没有把point cloud library 1.8环境搭建起来,因此无法实时读取点云信息。此外,笔记本电脑USB芯片总线中断协议限制,亦无法同时使用三个摄像头。

在如此坑爹的境地,分享下我是怎么搞三维重建的。。。。


本文环境win7+vs2012+opencv2.4.9+openni2.0+pcl1.7.2


一、点云数据获取

1.采用openni2.0 采集depth和rgb数据

2.opencv实时显示(否则你不知道采集的是啥),借助IplImage接口拷贝RGB数据给cloudpoint(直接cloudpoint之间赋值结果不对)

3.利用PCL的IO接口,将数据倒腾到PCD文件(PCD采用binary形式保存,ASCII性能实在太差)

[cpp]  view plain copy
  1. #include <pcl/io/pcd_io.h>  
  2. #include <pcl/point_types.h>  
  3. // 标准库头文件  
  4. #include <iostream>  
  5. #include <string>  
  6. #include <vector>   
  7. // OpenCV头文件  
  8. #include <opencv2\core\core.hpp>  
  9. #include <opencv2\highgui\highgui.hpp>  
  10. #include <opencv2\imgproc\imgproc.hpp>   
  11. // OpenNI头文件  
  12. #include <OpenNI.h>   
  13. typedef unsigned char uint8_t;  
  14. // namespace  
  15. using namespace std;  
  16. using namespace openni;  
  17. using namespace cv;  
  18. using namespace pcl;  
  19.   
  20. void CheckOpenNIError( Status result, string status )    
  21. {     
  22.     if( result != STATUS_OK )     
  23.         cerr << status << " Error: " << OpenNI::getExtendedError() << endl;    
  24. }   
  25.   
  26. int main( int argc, char **argv )  
  27. {  
  28.     Status result = STATUS_OK;  
  29.     int i,j;  
  30.     float x=0.0,y=0.0,z=0.0,xx=0.0;    
  31.     //IplImage *test,*test2;  
  32.     IplImage *test2;  
  33.   
  34.     //point cloud   
  35.     PointCloud<PointXYZRGB> cloud;  
  36.   
  37.     //opencv image  
  38.     Mat cvBGRImg;   
  39.     Mat cvDepthImg;    
  40.   
  41.     //OpenNI2 image    
  42.     VideoFrameRef oniDepthImg;    
  43.     VideoFrameRef oniColorImg;  
  44.   
  45.     namedWindow("depth");    
  46.     namedWindow("image");   
  47.   
  48.     char key=0;  
  49.   
  50.     // 初始化OpenNI    
  51.     result = OpenNI::initialize();  
  52.     CheckOpenNIError( result, "initialize context" );   
  53.       
  54.     // open device      
  55.     Device device;    
  56.     result = device.open( openni::ANY_DEVICE );   
  57.     CheckOpenNIError( result, "open device" );  
  58.   
  59.   
  60.     // create depth stream     
  61.     VideoStream oniDepthStream;    
  62.     result = oniDepthStream.create( device, openni::SENSOR_DEPTH );  
  63.     CheckOpenNIError( result, "create depth stream" );  
  64.     
  65.     // set depth video mode    
  66.     VideoMode modeDepth;    
  67.     modeDepth.setResolution( 640, 480 );    
  68.     modeDepth.setFps( 30 );    
  69.     modeDepth.setPixelFormat( PIXEL_FORMAT_DEPTH_1_MM );    
  70.     oniDepthStream.setVideoMode(modeDepth);    
  71.     // start depth stream    
  72.     result = oniDepthStream.start();   
  73.     CheckOpenNIError( result, "start depth stream" );  
  74.      
  75.     // create color stream    
  76.     VideoStream oniColorStream;    
  77.     result = oniColorStream.create( device, openni::SENSOR_COLOR );    
  78.     CheckOpenNIError( result, "create color stream" );  
  79.     // set color video mode    
  80.     VideoMode modeColor;    
  81.     modeColor.setResolution( 640, 480 );    
  82.     modeColor.setFps( 30 );    
  83.     modeColor.setPixelFormat( PIXEL_FORMAT_RGB888 );    
  84.     oniColorStream.setVideoMode( modeColor);   
  85.     // start color stream    
  86.     result = oniColorStream.start();   
  87.     CheckOpenNIError( result, "start color stream" );  
  88.   
  89.     while(true)  
  90.     {  
  91.         // read frame    
  92.         if( oniColorStream.readFrame( &oniColorImg ) == STATUS_OK )    
  93.         {    
  94.             // convert data into OpenCV type    
  95.             Mat cvRGBImg( oniColorImg.getHeight(), oniColorImg.getWidth(), CV_8UC3, (void*)oniColorImg.getData() );    
  96.             cvtColor( cvRGBImg, cvBGRImg, CV_RGB2BGR );    
  97.             imshow( "image", cvBGRImg );    
  98.         }    
  99.   
  100.         if( oniDepthStream.readFrame( &oniDepthImg ) == STATUS_OK )    
  101.         {    
  102.             Mat cvRawImg16U( oniDepthImg.getHeight(), oniDepthImg.getWidth(), CV_16UC1, (void*)oniDepthImg.getData() );    
  103.             cvRawImg16U.convertTo( cvDepthImg, CV_8U, 255.0/(oniDepthStream.getMaxPixelValue()));      
  104.             imshow( "depth", cvDepthImg );    
  105.         }    
  106.         // quit  
  107.         if( cv::waitKey( 1 ) == 'q' )        
  108.             break;  
  109.         // capture  depth and color data     
  110.         if( cv::waitKey( 1 ) == 'c' )  
  111.         {  
  112.             //get data  
  113.             DepthPixel *pDepth = (DepthPixel*)oniDepthImg.getData();  
  114.             //create point cloud  
  115.             cloud.width = oniDepthImg.getWidth();  
  116.             cloud.height = oniDepthImg.getHeight();  
  117.             cloud.is_dense = false;  
  118.             cloud.points.resize(cloud.width * cloud.height);  
  119.             //test = cvCreateImage(cvSize(cloud.width,cloud.height),IPL_DEPTH_8U,3);  
  120.             test2 = &IplImage(cvBGRImg);  
  121.   
  122.             for(i=0;i<oniDepthImg.getHeight();i++)  
  123.             {  
  124.                  for(j=0;j<oniDepthImg.getWidth();j++)  
  125.                  {  
  126.                      float k = i;    
  127.                      float m = j;   
  128.                      xx = pDepth[i*oniDepthImg.getWidth()+j];  
  129.                      CoordinateConverter::convertDepthToWorld (oniDepthStream,m,k,xx,&x,&y,&z);   
  130.                      cloud[i*cloud.width+j].x = x;  
  131.                      cloud[i*cloud.width+j].y = y;  
  132.                      cloud[i*cloud.width+j].z = xx;  
  133.                      cloud[i*cloud.width+j].b = (uint8_t)test2->imageData[i*test2->widthStep+j*3+0];  
  134.                      cloud[i*cloud.width+j].g = (uint8_t)test2->imageData[i*test2->widthStep+j*3+1];  
  135.                      cloud[i*cloud.width+j].r = (uint8_t)test2->imageData[i*test2->widthStep+j*3+2];  
  136.                     /* test->imageData[i*test->widthStep+j*3+0] = test2->imageData[i*test2->widthStep+j*3+0]; 
  137.                      test->imageData[i*test->widthStep+j*3+1] = test2->imageData[i*test2->widthStep+j*3+1]; 
  138.                      test->imageData[i*test->widthStep+j*3+2] = test2->imageData[i*test2->widthStep+j*3+2];*/  
  139.                  }  
  140.              }  
  141.               
  142.             //cvSaveImage("test.jpg",test);  
  143.             pcl::io::savePCDFileBinaryCompressed("test_pcdc.pcd",cloud);  
  144.             cerr<<"Saved "<<cloud.points.size()<<" data points to test_pcd.pcd."<<endl;  
  145.             imwrite("c_color.jpg",cvBGRImg);  
  146.             imwrite("c_depth.jpg",cvDepthImg);  
  147.             /*for(size_t i=0;i<cloud.points.size();++i) 
  148.             cerr<<"    "<<cloud.points[i].x<<" "<<cloud.points[i].y<<" "<<cloud.points[i].z<<endl;*/  
  149.         }  
  150.     }  
  151. }  

按C键,获取点云信息,按P键退出

我们得到下面三组数据:


PCL 室内三维重建_第1张图片


二、点云展示

这个没啥东西,直接读取PCD,调用show即可

[cpp]  view plain copy
  1. #include <pcl/visualization/cloud_viewer.h>  
  2. #include <iostream>  
  3. #include <pcl/io/io.h>  
  4. #include <pcl/io/pcd_io.h>  
  5.       
  6. int main ()  
  7. {  
  8.     pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);  
  9.   
  10.     if(pcl::io::loadPCDFile<pcl::PointXYZRGB>("test_pcda.pcd",*cloud)==-1)//*打开点云文件  
  11.     {  
  12.         PCL_ERROR("Couldn't read file test_pcd.pcd\n");  
  13.         return(-1);  
  14.     }  
  15.     std::cout<<"Loaded "<<cloud->width*cloud->height<<" data points from test_pcd.pcd with the following fields: "<<std::endl;  
  16.   
  17.     pcl::visualization::CloudViewer viewer("My First Cloud Viewer");  
  18.     viewer.showCloud(cloud);  
  19.     while(!viewer.wasStopped())  
  20.     {  
  21.   
  22.     }  
  23. }  

三组数据对应结果:

PCL 室内三维重建_第2张图片

PCL 室内三维重建_第3张图片

PCL 室内三维重建_第4张图片


三、点云拼接

  我直接拿原始数据让ICP处理,所以速度非常慢!关于ICP解释,摘录自《点云库PCL学习教程》

Iterative Closest Point,迭代最近点算法,对待拼接的2片点云,首先根据一定的准则确立对应点集P与Q,其中对应点对的个数为N。然后通过最小二乘法迭代计算最优的坐标变换,即旋转矩阵R和平移矢量t,使得误差函数最小。ICP算法计算简便直观且可使拼接具有较好的精度,但是算法的运行速度以及向全局最优的收敛性却在很大程度上依赖于给定的初始变换估计以及在迭代过程中对应关系的确立。各种粗拼接技术可为ICP算法提供较好的初始位置,所以迭代过程中确立正确对应点集以避免迭代陷入局部极值成为各种改进算法的关键,决定了算法的收敛速度与最终的拼接精度。

ICP处理流程:

1.对原始点云数据进行采样

2.确定初始对应点集

3.去除错误对应点对

4.坐标变换的求解

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <pcl/io/pcd_io.h>  
  3. #include <pcl/point_types.h>  
  4. #include <pcl/registration/icp.h>  
  5. #include <pcl/visualization/cloud_viewer.h>  
  6. #include <pcl/kdtree/kdtree_flann.h>  
  7. #include <time.h>  
  8.   
  9. int  main (int argc, char** argv)  
  10. {  
  11.   clock_t start,finish;  
  12.   double totaltime;  
  13.   
  14.    
  15.   pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_in (new pcl::PointCloud<pcl::PointXYZRGB>);  
  16.   pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_out (new pcl::PointCloud<pcl::PointXYZRGB>);  
  17.   pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_out2 (new pcl::PointCloud<pcl::PointXYZRGB>);  
  18.   pcl::PointCloud<pcl::PointXYZRGB>::Ptr my_cloud (new pcl::PointCloud<pcl::PointXYZRGB>);  
  19.   pcl::PointCloud<pcl::PointXYZRGB>::Ptr my_cloud2 (new pcl::PointCloud<pcl::PointXYZRGB>);  
  20.     
  21.   start=clock();  
  22.   if(pcl::io::loadPCDFile<pcl::PointXYZRGB>("test_pcda.pcd",*cloud_in)==-1)//*打开点云文件  
  23.   {  
  24.      PCL_ERROR("Couldn't read file test_pcd.pcd\n");  
  25.      return(-1);  
  26.   }  
  27.   finish=clock();  
  28.   totaltime = (double)(finish-start)/CLOCKS_PER_SEC;  
  29.   cout<<"\n load test_pcda.pcd data : "<<totaltime<<"seconds!"<<endl;  
  30.   
  31.   start=clock();  
  32.   if(pcl::io::loadPCDFile<pcl::PointXYZRGB>("test_pcdb.pcd",*cloud_out)==-1)//*打开点云文件  
  33.   {  
  34.         PCL_ERROR("Couldn't read file test_pcd.pcd\n");  
  35.         return(-1);  
  36.   }  
  37.   finish=clock();  
  38.   totaltime = (double)(finish-start)/CLOCKS_PER_SEC;  
  39.   cout<<"\n load test_pcdb.pcd data : "<<totaltime<<"seconds!"<<endl;  
  40.   
  41.   start=clock();  
  42.   if(pcl::io::loadPCDFile<pcl::PointXYZRGB>("test_pcdc.pcd",*cloud_out2)==-1)//*打开点云文件  
  43.   {  
  44.         PCL_ERROR("Couldn't read file test_pcd.pcd\n");  
  45.         return(-1);  
  46.   }  
  47.   finish=clock();  
  48.   totaltime = (double)(finish-start)/CLOCKS_PER_SEC;  
  49.   cout<<"\n load test_pcdc.pcd data : "<<totaltime<<"seconds!"<<endl;  
  50.   
  51.   //call icp api  
  52.   start=clock();  
  53.   pcl::IterativeClosestPoint<pcl::PointXYZRGB, pcl::PointXYZRGB> icp;  
  54.   icp.setInputCloud(cloud_in);  
  55.   icp.setInputTarget(cloud_out);  
  56.   pcl::PointCloud<pcl::PointXYZRGB> Final;  
  57.   icp.align(Final);  
  58.   std::cout << "has converged:" << icp.hasConverged() << " score: " <<  
  59.   icp.getFitnessScore() << std::endl;  
  60.   std::cout << icp.getFinalTransformation() << std::endl;  
  61.     
  62.   finish=clock();  
  63.   totaltime = (double)(finish-start)/CLOCKS_PER_SEC;  
  64.   cout<<"\n first time call icp process : "<<totaltime<<"seconds!"<<endl;  
  65.   
  66.   //构造拼接临时的点云  
  67.   for(int i=0;i< Final.points.size();i++)  
  68.   {  
  69.         pcl::PointXYZRGB basic_point;  
  70.         basic_point.x = Final.points[i].x;  
  71.         basic_point.y = Final.points[i].y;  
  72.         basic_point.z = Final.points[i].z;  
  73.         basic_point.r = Final.points[i].r;  
  74.         basic_point.g = Final.points[i].g;  
  75.         basic_point.b = Final.points[i].b;  
  76.         my_cloud->points.push_back(basic_point);  
  77.   }  
  78.   
  79.   //call icp api another time  
  80.   start=clock();  
  81.   icp.setInputCloud(cloud_out2);  
  82.   icp.setInputTarget(my_cloud);  
  83.   pcl::PointCloud<pcl::PointXYZRGB> Final2;  
  84.   icp.align(Final2);  
  85.   std::cout << "has converged:" << icp.hasConverged() << " score: " <<  
  86.   icp.getFitnessScore() << std::endl;  
  87.   std::cout << icp.getFinalTransformation() << std::endl;  
  88.   finish=clock();  
  89.   totaltime = (double)(finish-start)/CLOCKS_PER_SEC;  
  90.   cout<<"\n second time call icp process : "<<totaltime<<"seconds!"<<endl;  
  91.   
  92.   //my_cloud.reset();  
  93.    //构造拼接最终的点云  
  94.   for(int i=0;i< Final2.points.size();i++)  
  95.   {  
  96.         pcl::PointXYZRGB basic_point;  
  97.         basic_point.x = Final2.points[i].x;  
  98.         basic_point.y = Final2.points[i].y;  
  99.         basic_point.z = Final2.points[i].z;  
  100.         basic_point.r = Final2.points[i].r;  
  101.         basic_point.g = Final2.points[i].g;  
  102.         basic_point.b = Final2.points[i].b;  
  103.         my_cloud2->points.push_back(basic_point);  
  104.   }  
  105.   
  106.   pcl::visualization::CloudViewer viewer("My First Cloud Viewer");  
  107.   viewer.showCloud(my_cloud2);  
  108.   while(!viewer.wasStopped())  
  109.   {  
  110.   
  111.   }  
  112.   return (0);  
  113. }  



运行结果好像不大对头,收敛失败了(三幅点云图像之间的交集过小所致。。)

PCL 室内三维重建_第5张图片

PCL 室内三维重建_第6张图片

换一个顺序也不对:

PCL 室内三维重建_第7张图片



四、编译错误解决

代码写的是很简单,但是各种开源的东西凑到一起就蛋疼了,这不遇到了opencv中的flann模块与pcl中的flann模块的冲突!

给大家讲讲我是怎么发现的。。。

问题现象:


有个坑爹结构体,跑出来了!!咋办?

source insight导入point cloud library 所有源码,再把flann第三方所有头文件导入,关联后会找到这个坑爹结构体

PCL 室内三维重建_第8张图片

然后看看有谁调用它,Flann.hpp


它位于pcl第三方目录下

PCL 室内三维重建_第9张图片

到现在位置我觉得没啥问题,由于《学习教程》中没有引用flann.hpp头文件,我想尝试引用一下会不会解决问题呢,后来vs2012的智能关联功能,帮了我大忙!

PCL 室内三维重建_第10张图片

由于本次代码没有用到opencv的第三方库,果断干掉opencv2这块头文件

PCL 室内三维重建_第11张图片


万事大吉!


你可能感兴趣的:(Kinect开发)