首先初始化环境:
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:
接着我们对彩色流和深度流做一些格式设置:
// 设置深度图像视频模式
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);
结果如图所示:
经过尝试,发现对于深度图来说,320*240/640*480都可以,而彩色图像只能320*240。所以我们使用resize()把彩色的320*240强制转化为640*480,默认采用最近邻差值来补充缺失的像素点,结果如图:
这时候我们发现图像左右相反,这是因为体感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;
}
}
处理结果如下:
完整代码如下:
#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();
这样获得的图像就正常了