关于OpenNI2和OpenCV2的那些事——获取彩色图和深度图(XtionProLive)

上一节讲述了搭环境时遇到的挫折,这一节我们来讲讲如何使用XtionProLive(XtionPro没有彩色摄像头,Live版才有)获取彩色图和数度图,以及彩色图的放大与水平镜像。(PS: 对比两代OpenNI,2真的比1要简洁得多,使用OpenNI2编程序,代码简单易懂。)

首先初始化环境:

OpenNI::initialize();

创建状态:

Status rc = STATUS_OK;
接着声明并打开设备:

Device xtion;
const char * deviceURL = openni::ANY_DEVICE;  //设备名
rc = xtion.open(deviceURL);

创建深度数据流和彩色数据流:

VideoStream streamDepth;
VideoStream streamColor;

打开数据流并对齐彩色/深度图像:

rc = streamDepth.start();
rc = streamColor.start();

// 图像模式注册,彩色图与深度图对齐
if (xtion.isImageRegistrationModeSupported(
	IMAGE_REGISTRATION_DEPTH_TO_COLOR))
{
	xtion.setImageRegistrationMode(IMAGE_REGISTRATION_DEPTH_TO_COLOR);
}
从数据流中读取数据保存到VideoFrameRef中并用opencv显示出来:
while (true)
{
	// 读取数据流
	rc = streamDepth.readFrame(&frameDepth);
	if (rc == STATUS_OK)
	{
		// 将深度数据转换成OpenCV格式
		const Mat mImageDepth(frameDepth.getHeight(), frameDepth.getWidth(), CV_16UC1, (void*)frameDepth.getData());
		Mat mScaledDepth, hScaledDepth;
		mImageDepth.convertTo(mScaledDepth, CV_8U, 255.0 / iMaxDepth);
		// 显示出深度图像
		imshow("Depth Image", hScaledDepth);
	}

	rc = streamColor.readFrame(&frameColor);
	if (rc == STATUS_OK)
	{
		// 同样的将彩色图像数据转化成OpenCV格式
		const Mat mImageRGB(frameColor.getHeight(), frameColor.getWidth(), CV_8UC3, (void*)frameColor.getData());
		// 首先将RGB格式转换为BGR格式
		Mat cImageBGR,bImageBGR,hImageBGR;
		cvtColor(mImageRGB, cImageBGR, CV_RGB2BGR);
		// 然后显示彩色图像
		imshow("Color Image", hImageBGR);
	}

	// 终止快捷键
	if (waitKey(1) == 27)
		break;
}

如图所示,彩色图和深度图默认都是320*240:

关于OpenNI2和OpenCV2的那些事——获取彩色图和深度图(XtionProLive)_第1张图片

接着我们对彩色流和深度流做一些格式设置:

// 设置深度图像视频模式
VideoMode mModeDepth;
// 分辨率大小
mModeDepth.setResolution(640, 480);
// 每秒30帧
mModeDepth.setFps(30);
// 像素格式
mModeDepth.setPixelFormat(PIXEL_FORMAT_DEPTH_1_MM);

streamDepth.setVideoMode(mModeDepth);
// 同样的设置彩色图像视频模式
VideoMode mModeColor;
mModeColor.setResolution(320, 240);
mModeColor.setFps(30);
mModeColor.setPixelFormat(PIXEL_FORMAT_RGB888);
streamColor.setVideoMode(mModeColor);
结果如图所示:

关于OpenNI2和OpenCV2的那些事——获取彩色图和深度图(XtionProLive)_第2张图片

经过尝试,发现对于深度图来说,320*240/640*480都可以,而彩色图像只能320*240。所以我们使用resize()把彩色的320*240强制转化为640*480,默认采用最近邻差值来补充缺失的像素点,结果如图:

关于OpenNI2和OpenCV2的那些事——获取彩色图和深度图(XtionProLive)_第3张图片

这时候我们发现图像左右相反,这是因为体感RGBD相机一般是捕捉人体的,所以是面向人体的捕捉图像。而此时我们把相机面向了我们的前方,用于以后捕捉环境图像三维重建,所以我们还需要对彩色图和深度图进行水平镜像处理,函数如下:

void hMirrorTrans(const Mat &src, Mat &dst)
{
	dst.create(src.rows, src.cols, src.type());

	int rows = src.rows;
	int cols = src.cols;

	switch (src.channels())
	{
	case 1:   //1通道比如深度图像
		const uchar *origal;
		uchar *p;
		for (int i = 0; i < rows; i++){
			origal = src.ptr(i);
			p = dst.ptr(i);
			for (int j = 0; j < cols; j++){
				p[j] = origal[cols - 1 - j];
			}
		}
		break;
	case 3:   //3通道比如彩色图像
		const Vec3b *origal3;
		Vec3b *p3;
		for (int i = 0; i < rows; i++) {
			origal3 = src.ptr(i);
			p3 = dst.ptr(i);
			for (int j = 0; j < cols; j++){
				p3[j] = origal3[cols - 1 - j];
			}
		}
		break;
	default:
		break;
	}
}
处理结果如下:

关于OpenNI2和OpenCV2的那些事——获取彩色图和深度图(XtionProLive)_第4张图片

完整代码如下:

#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace openni;
using namespace cv;

void showdevice(){
	// 获取设备信息  
	Array aDeviceList;
	OpenNI::enumerateDevices(&aDeviceList);

	cout << "电脑上连接着 " << aDeviceList.getSize() << " 个体感设备." << endl;

	for (int i = 0; i < aDeviceList.getSize(); ++i)
	{
		cout << "设备 " << i << endl;
		const DeviceInfo& rDevInfo = aDeviceList[i];
		cout << "设备名: " << rDevInfo.getName() << endl;
		cout << "设备Id: " << rDevInfo.getUsbProductId() << endl;
		cout << "供应商名: " << rDevInfo.getVendor() << endl;
		cout << "供应商Id: " << rDevInfo.getUsbVendorId() << endl;
		cout << "设备URI: " << rDevInfo.getUri() << endl;

	}
}

void hMirrorTrans(const Mat &src, Mat &dst)
{
	dst.create(src.rows, src.cols, src.type());

	int rows = src.rows;
	int cols = src.cols;

	switch (src.channels())
	{
	case 1:   //1通道比如深度图像
		const uchar *origal;
		uchar *p;
		for (int i = 0; i < rows; i++){
			origal = src.ptr(i);
			p = dst.ptr(i);
			for (int j = 0; j < cols; j++){
				p[j] = origal[cols - 1 - j];
			}
		}
		break;
	case 3:   //3通道比如彩色图像
		const Vec3b *origal3;
		Vec3b *p3;
		for (int i = 0; i < rows; i++) {
			origal3 = src.ptr(i);
			p3 = dst.ptr(i);
			for (int j = 0; j < cols; j++){
				p3[j] = origal3[cols - 1 - j];
			}
		}
		break;
	default:
		break;
	}

}

int main()
{
	Status rc = STATUS_OK;

	// 初始化OpenNI环境
	OpenNI::initialize();

	showdevice();

	// 声明并打开Device设备。
	Device xtion;
	const char * deviceURL = openni::ANY_DEVICE;  //设备名
	rc = xtion.open(deviceURL);

	// 创建深度数据流
	VideoStream streamDepth;
	rc = streamDepth.create(xtion, SENSOR_DEPTH);
	if (rc == STATUS_OK)
	{
		// 设置深度图像视频模式
		VideoMode mModeDepth;
		// 分辨率大小
		mModeDepth.setResolution(640, 480);
		// 每秒30帧
		mModeDepth.setFps(30);
		// 像素格式
		mModeDepth.setPixelFormat(PIXEL_FORMAT_DEPTH_1_MM);

		streamDepth.setVideoMode(mModeDepth);

		// 打开深度数据流
		rc = streamDepth.start();
		if (rc != STATUS_OK)
		{
			cerr << "无法打开深度数据流:" << OpenNI::getExtendedError() << endl;
			streamDepth.destroy();
		}
	}
	else
	{
		cerr << "无法创建深度数据流:" << OpenNI::getExtendedError() << endl;
	}

	// 创建彩色图像数据流
	VideoStream streamColor;
	rc = streamColor.create(xtion, SENSOR_COLOR);
	if (rc == STATUS_OK)
	{
		// 同样的设置彩色图像视频模式
		VideoMode mModeColor;
		mModeColor.setResolution(320, 240);
		mModeColor.setFps(30);
		mModeColor.setPixelFormat(PIXEL_FORMAT_RGB888);

		streamColor.setVideoMode(mModeColor);

		// 打开彩色图像数据流
		rc = streamColor.start();
		if (rc != STATUS_OK)
		{
			cerr << "无法打开彩色图像数据流:" << OpenNI::getExtendedError() << endl;
			streamColor.destroy();
		}
	}
	else
	{
		cerr << "无法创建彩色图像数据流:" << OpenNI::getExtendedError() << endl;
	}

	if (!streamColor.isValid() || !streamDepth.isValid())
	{
		cerr << "彩色或深度数据流不合法" << endl;
		OpenNI::shutdown();
		return 1;
	}

	// 图像模式注册,彩色图与深度图对齐
	if (xtion.isImageRegistrationModeSupported(
		IMAGE_REGISTRATION_DEPTH_TO_COLOR))
	{
		xtion.setImageRegistrationMode(IMAGE_REGISTRATION_DEPTH_TO_COLOR);
	}


	// 创建OpenCV图像窗口
	namedWindow("Depth Image", CV_WINDOW_AUTOSIZE);
	namedWindow("Color Image", CV_WINDOW_AUTOSIZE);

	// 获得最大深度值
	int iMaxDepth = streamDepth.getMaxPixelValue();

	// 循环读取数据流信息并保存在VideoFrameRef中
	VideoFrameRef  frameDepth;
	VideoFrameRef  frameColor;

	while (true)
	{
		// 读取数据流
		rc = streamDepth.readFrame(&frameDepth);
		if (rc == STATUS_OK)
		{
			// 将深度数据转换成OpenCV格式
			const Mat mImageDepth(frameDepth.getHeight(), frameDepth.getWidth(), CV_16UC1, (void*)frameDepth.getData());
			// 为了让深度图像显示的更加明显一些,将CV_16UC1 ==> CV_8U格式
			Mat mScaledDepth, hScaledDepth;
			mImageDepth.convertTo(mScaledDepth, CV_8U, 255.0 / iMaxDepth);

			//水平镜像深度图
			hMirrorTrans(mScaledDepth, hScaledDepth);
			// 显示出深度图像
			imshow("Depth Image", hScaledDepth);
		}

		rc = streamColor.readFrame(&frameColor);
		if (rc == STATUS_OK)
		{
			// 同样的将彩色图像数据转化成OpenCV格式
			const Mat mImageRGB(frameColor.getHeight(), frameColor.getWidth(), CV_8UC3, (void*)frameColor.getData());
			// 首先将RGB格式转换为BGR格式
			Mat cImageBGR,bImageBGR,hImageBGR;
			cvtColor(mImageRGB, cImageBGR, CV_RGB2BGR);

			//水平镜像深度图
			hMirrorTrans(cImageBGR, hImageBGR);
			resize(hImageBGR, hImageBGR, Size(640, 480));
			// 然后显示彩色图像
			imshow("Color Image", hImageBGR);
		}

		// 终止快捷键
		if (waitKey(1) == 27)
			break;
	}

	// 关闭数据流
	streamDepth.destroy();
	streamColor.destroy();
	// 关闭设备
	xtion.close();
	// 最后关闭OpenNI
	OpenNI::shutdown();

	return 0;
}

今天到此为止,接下来研究如果用深度图和彩色图生成三维点云。欢迎使用xtion的小伙伴或者使用openni2的小伙伴和我讨论相关问题~


======================================================================================================

2015/11/3号补充:

关于解决镜像(左右相反)问题,我们还可以直接使用videostream的函数:setMirroringEnabled(false) 例如如下:

// 同样的设置彩色图像视频模式
VideoMode mModeColor;
mModeColor.setResolution(320, 240);
mModeColor.setFps(30);
mModeColor.setPixelFormat(PIXEL_FORMAT_RGB888);

streamColor.setVideoMode(mModeColor);
// 解决镜像问题
streamColor.setMirroringEnabled(false);
// 打开彩色图像数据流
rc = streamColor.start();
这样获得的图像就正常了

你可能感兴趣的:(OpenCV,OpenNI,SLAM)