我们使用Kinect和OpenNI Library捕获了一个3D图像,并使用此代码以OpenCV Mat的形式获得了rgb和深度图像.
main() { OpenNI::initialize(); puts( "Kinect initialization..." ); Device device; if ( device.open( openni::ANY_DEVICE ) != 0 ) { puts( "Kinect not found !" ); return -1; } puts( "Kinect opened" ); VideoStream depth,color; color.create( device,SENSOR_COLOR ); color.start(); puts( "Camera ok" ); depth.create( device,SENSOR_DEPTH ); depth.start(); puts( "Depth sensor ok" ); VideoMode paramvideo; paramvideo.setResolution( 640,480 ); paramvideo.setFps( 30 ); paramvideo.setPixelFormat( PIXEL_FORMAT_DEPTH_100_UM ); depth.setVideoMode( paramvideo ); paramvideo.setPixelFormat( PIXEL_FORMAT_RGB888 ); color.setVideoMode( paramvideo ); puts( "Réglages des flux vidéos ok" ); // If the depth/color synchronisation is not necessary,start is faster : //device.setDepthColorSyncEnabled( false ); // Otherwise,the streams can be synchronized with a reception in the order of our choice : device.setDepthColorSyncEnabled( true ); device.setImageRegistrationMode( openni::IMAGE_REGISTRATION_DEPTH_TO_COLOR ); VideoStream** stream = new VideoStream*[2]; stream[0] = &depth; stream[1] = &color; puts( "Kinect initialization completed" ); if ( device.getSensorInfo( SENSOR_DEPTH ) != NULL ) { VideoFrameRef depthFrame,colorFrame; cv::Mat colorcv( cv::Size( 640,480 ),CV_8UC3,NULL ); cv::Mat depthcv( cv::Size( 640,CV_16UC1,NULL ); cv::namedWindow( "RGB",CV_WINDOW_AUTOSIZE ); cv::namedWindow( "Depth",CV_WINDOW_AUTOSIZE ); int changedIndex; while( device.isValid() ) { OpenNI::waitForAnyStream( stream,2,&changedIndex ); switch ( changedIndex ) { case 0: depth.readFrame( &depthFrame ); if ( depthFrame.isValid() ) { depthcv.data = (uchar*) depthFrame.getData(); cv::imshow( "Depth",depthcv ); } break; case 1: color.readFrame( &colorFrame ); if ( colorFrame.isValid() ) { colorcv.data = (uchar*) colorFrame.getData(); cv::cvtColor( colorcv,colorcv,CV_BGR2RGB ); cv::imshow( "RGB",colorcv ); } break; default: puts( "Error retrieving a stream" ); } cv::waitKey( 1 ); } cv::destroyWindow( "RGB" ); cv::destroyWindow( "Depth" ); } depth.stop(); depth.destroy(); color.stop(); color.destroy(); device.close(); OpenNI::shutdown(); }
我们在上面添加了一些代码并从中获得了RGB和深度Mat,我们使用OpenCV处理了RGB.
现在我们需要以3D形式显示该图像.
我们正在使用: –
1)Windows 8 x64
2)Visual Studio 2012 x64
3)OpenCV 2.4.10
4)OpenNI 2.2.0.33
5)Kinect1
6)Kinect SDK 1.8.0
问题: –
1)我们可以使用OpenCV直接显示此图像还是需要任何外部库?
2)如果我们需要使用外部库,哪一个更适合这个简单的任务OpenGL,PCL或任何其他?
3)PCL是否支持Visual Studio 12和OpenNI2以及PCL自带其他版本的OpenNI这两个版本是否冲突?
解决方法
为了改善南极人的答案,
要以3D形式显示图像,您需要先创建点云…
RGB和深度图像为您提供创建有组织的彩色pointcloud所需的数据.为此,您需要计算每个点的x,y,z值. z值来自深度像素,但必须计算x和y.
要以3D形式显示图像,您需要先创建点云…
RGB和深度图像为您提供创建有组织的彩色pointcloud所需的数据.为此,您需要计算每个点的x,y,z值. z值来自深度像素,但必须计算x和y.
要做到这一点你可以做这样的事情:
void Viewer::get_pcl(cv::Mat& color_mat,cv::Mat& depth_mat,pcl::PointCloud<pcl::PointXYZRGBA>& cloud ){ float x,z; for (int j = 0; j< depth_mat.rows; j ++){ for(int i = 0; i < depth_mat.cols; i++){ // the RGB data is created PCD_BGRA pcd_BGRA; pcd_BGRA.B = color_mat.at<cv::Vec3b>(j,i)[0]; pcd_BGRA.R = color_mat.at<cv::Vec3b>(j,i)[2]; pcd_BGRA.G = color_mat.at<cv::Vec3b>(j,i)[1]; pcd_BGRA.A = 0; pcl::PointXYZRGBA vertex; int depth_value = (int) depth_mat.at<unsigned short>(j,i); // find the world coordinates openni::CoordinateConverter::convertDepthToWorld(depth,i,j,(openni::DepthPixel) depth_mat.at<unsigned short>(j,i),&x,&y,&z ); // the point is created with depth and color data if ( limitx_min <= i && limitx_max >=i && limity_min <= j && limity_max >= j && depth_value != 0 && depth_value <= limitz_max && depth_value >= limitz_min){ vertex.x = (float) x; vertex.y = (float) y; vertex.z = (float) depth_value; } else { // if the data is outside the boundaries vertex.x = bad_point; vertex.y = bad_point; vertex.z = bad_point; } vertex.rgb = pcd_BGRA.RGB_float; // the point is pushed back in the cloud cloud.points.push_back( vertex ); } } }
和PCD_BGRA是
union PCD_BGRA { struct { uchar B; // LSB uchar G; // --- uchar R; // MSB uchar A; // }; float RGB_float; uint RGB_uint; };
当然,这是针对您想要使用PCL的情况,但它或多或少地计算x,z值.这依赖于openni :: CoordinateConverter :: convertDepthToWorld来查找3D中点的位置.您也可以手动执行此操作
const float invfocalLength = 1.f / 525.f; const float centerX = 319.5f; const float centerY = 239.5f; const float factor = 1.f / 1000.f; float dist = factor * (float)(*depthdata); p.x = (x-centerX) * dist * invfocalLength; p.y = (y-centerY) * dist * invfocalLength; p.z = dist;
其中centerX,centerY和focallength是相机的固有校准(这个是Kinect).以及如果您需要以米或毫米为单位的距离的因素…这个值取决于您的程序
对于问题:
1)是的,您可以使用最新的OpenCV和viz class或其他适合您需求的外部库来显示它.2)OpenGl很不错,但是如果你不知道这两个库中的任何一个PCL它会更容易使用(我的意思是显示pointclouds)3)我没有在windows中使用它,但理论上它可以与visual studio 2012一起使用.据我所知,PCL自带的版本是OpenNI 1,它不会影响OpenNI2 ……