在前面的文章中介绍了如何搭建Kinect开发环境:
Kinect学习(一):开发环境搭建
。搭建好环境后,首先要做的当然就是试着读取Kinect中的数据了。
Kinect有三个镜头,中间的是RGB摄像头,左边的是红外线发射器,右边的是红外线CMOS摄像头构成的3D结构光摄像头,用来采集深度数据。彩色摄像头最大支持1280*960分辨率成像,红外摄像头最大支持640*480成像。
接下来就要通过微软提供的SDK来读取Kinect中的彩色摄像头的数据了。
先上代码,里面有注释,后面再详细介绍。
#include <windows.h>
#include <NuiApi.h>
#include <iostream>
#include <opencv2/opencv.hpp>
几个常用的头文件:
1、NuiApi.h ---包含所有的NUI(自然用户界面) API头文件和定义基本的初始化和函数访问入口。这是我们C++工程的主要头文件,它已经包含了NuiImageCamera.h 和 NuiSkeleton.h。
2、NuiImageCamera.h ---定义了图像和摄像头服务的API,包括调整摄像头的角度和仰角,打开数据流和读取数据流等。
3、NuiSkeleton.h ---骨架有关的API,包括使能骨架跟踪,获取骨架数据,骨架数据转换和平滑渲染等。
4、NuiSensor.h ---音频API,包括ISoundSourceLocalizer接口,用于返回声源的方向(波束形成)和音频的位置。
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
cv::Mat img;
img.create(480, 640, CV_8UC3);
HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR);
if (FAILED(hr))
cout << "NuiInitialize failed" << endl;
return hr;
HANDLE nextColorFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE colorStreamHandle = NULL;
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, NUI_IMAGE_RESOLUTION_640x480, 0, 2, nextColorFrameEvent, &colorStreamHandle);
if (FAILED(hr))
cout << "Could not open color image stream video" << endl;
NuiShutdown();
return hr;
cv::namedWindow("colorImage", CV_WINDOW_AUTOSIZE);
while (1)
const NUI_IMAGE_FRAME * pImageFrame = NULL;
if (WaitForSingleObject(nextColorFrameEvent, INFINITE) == 0)
hr = NuiImageStreamGetNextFrame(colorStreamHandle, 0, &pImageFrame);
if (FAILED(hr))
cout << "Could not get color image" << endl;
NuiShutdown();
return -1;
INuiFrameTexture * pTexture = pImageFrame->pFrameTexture;
NUI_LOCKED_RECT LockedRect;
pTexture->LockRect(0, &LockedRect, NULL, 0);
if (LockedRect.Pitch != 0)
for (int i = 0; i < img.rows; i++)
uchar *ptr = img.ptr<uchar>(i);
uchar *pBuffer = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;
for (int j = 0;j < img.cols;j++)
ptr[3 * j] = pBuffer[4 * j];
ptr[3 * j + 1] = pBuffer[4 * j + 1];
ptr[3 * j + 2] = pBuffer[4 * j + 2];
cv::imshow("colorImage", img);
cout << "Buffer length of received texture is bogus\r\n" << endl;
pTexture->UnlockRect(0);
NuiImageStreamReleaseFrame(colorStreamHandle, pImageFrame);
if (cv::waitKey(20) == 27)
break;
NuiShutdown();
return 0;
整个程序可以分为以下流程:
- 初始化NUI接口;
- 定义事件句柄;
- 打开Kinect设备的数据流(彩色RGB);
- 等待数据更新,若更新完成则进行下一步;
- 从数据流中拿出图像数据;
- 提取数据帧并锁定数据;
- 将数据转换为OpenCV的Mat格式。
1、初始化NUI接口
//1、初始化NUI
HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR);
要使用微软提供的SDK中的SDK来操作Kinect,必须先调用NUI初始化函数。
函数原型为:
HRESULT NuiInitialize(DWORD dwFlags);
dwFlags
表示标志位,有以下几种情况:
NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX
: 提供带用户信息的深度图数据;NUI_INITIALIZE_FLAG_USES_COLOR
:提供RGB彩色图像数据;NUI_INITIALIZE_FLAG_USES_SKELETON
:提供骨骼点数据;NUI_INITIALIZE_FLAG_USES_DEPTH
:提供深度图像数据;NUI_INITIALIZE_FLAG_USES_AUDIO
:提供声音数据;NUI_INITIALIZE_DEFAULT_HARDWARE_THREAD
:初始化默认的硬件线程;
以上的都各自对应一个标志位,使用时可以使用|
将它们组合起来。
注意到,它还返回了一个HRESULT
类型的参数,通过它可以判断初始化函数是否执行成功。
if (FAILED(hr))
cout << "NuiInitialize failed" << endl;
return hr;
或者判断是否等于S_OK
:
if(hr == S_OK)
cout << "NuiInitialize successfully" << endl;
2、定义事件句柄
HANDLE nextColorFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
该函数会创建一个windows事件对象,创建成功则返回事件的句柄。这里的这个事件是用来判断是否有新数据的。
其中有四个参数:
- 第一个是安全属性,设定为
NULL
的安全描述符; - 第二个表示设置信号复位方式为自动恢复为无信号状态(FALSE)还是手动恢复为无信号状态(TRUE),设为
TRUE
,因为后面应用程序会重置事件消息; - 第三个是事件消息初始状态的布尔值,为
FALSE
; 最后一个是信号名称,可以直接设置为NULL
;
3、打开Kinect设备的彩色图像数据流
hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, NUI_IMAGE_RESOLUTION_640x480, 0, 2, nextColorFrameEvent, &colorStreamHandle);
if (FAILED(hr))
cout << "Could not open color image stream video" << endl;
NuiShutdown();
return hr;
使用这个函数可以打开Kinect设备的彩色图或是深度图的访问通道。也可以理解为,创建一个访问彩色图或深度图的数据流。
函数原型:
_Check_return_ HRESULT NUIAPI NuiImageStreamOpen(
_In_ NUI_IMAGE_TYPE eImageType,
_In_ NUI_IMAGE_RESOLUTION eResolution,
_In_ DWORD dwImageFrameFlags,
_In_ DWORD dwFrameLimit,
_In_opt_ HANDLE hNextFrameEvent,
_Out_ HANDLE *phStreamHandle
参数说明:
eImageType
:这是一个NUI_IMAGE_TYPE
枚举类型的变量,用来指定要创建的数据流的类型。比如,NUI_IMAGE_TYPE_COLOR
对应彩色图,NUI_IMAGE_TYPE_DEPTH
对应深度图。注意,这里指定的图像的类型,必须是前面初始化是已经指定过的,否则无法打开。eResolution
:这是一个NUI_IMAGE_RESOLUTION
枚举类型的变量,用来指定打开图像的分别率,但是由于3D结构光摄像头与RGB摄像头的分辨率不同,所以根据前面eImageType
参数指定的图像类型不同,这里也有所不同。如果eImageType
指定为彩色图NUI_IMAGE_TYPE_COLOR
,那么就有两种分辨率:NUI_IMAGE_RESOLUTION_1280x960
、NUI_IMAGE_RESOLUTION_640x480
;如果eImageType
指定为深度图NUI_IMAGE_TYPE_DEPTH
,那么就有三种分辨率:NUI_IMAGE_RESOLUTION_640x480
、NUI_IMAGE_RESOLUTION_320x240
、NUI_IMAGE_RESOLUTION_80x60
。dwImageFrameFlags_NotUsed
:看名字就知道了,没有用到这个参数,随便给个数就可以了。dwFrameLimit
:指定NUI运行时环境将要为你所打开的图像类型建立几个缓冲。最大值是NUI_IMAGE_STREAM_FRAME_LIMIT_MAXIMUM
,即4。大多数程序中,定为2就足够了。hNextFrameEvent
: 一个用来手动重置信号是否可用的事件句柄(event),该信号用来控制Kinect是否可以开始读取下一帧数据。也就是说在这里指定一个句柄后,随着程序往后继续运行,当你在任何时候想要控制kinect读取下一帧数据时,都应该先使用WaitForSingleObject
函数判断一下该句柄,判断是否有数据可拿。phStreamHandle
:函数执行成功后,会创建对应的数据流,并让这个句柄保存其地址。后面可以通过这个句柄来从Kinect读取数据。- 返回值:
S_OK
表示成功。
4、等待数据更新,若更新完成则进行下一步;
//4.1、无线等待新的数据,等到就返回
if (WaitForSingleObject(nextColorFrameEvent, INFINITE) == 0)
前面也提到了这个函数,如果事件(对应nextColorFrameEvent
)有信号,即有数据,那么返回值为0,程序也会往下执行;如果没有数据,就会等待。函数的第二个参数表示等待时间,单位为ms,这里设为INFINITE
,表示一直等待。
5、从数据流中拿出图像数据;
//4.2、从刚才打开数据流的流句柄中得到该帧的数据,读取到的数据地址存于pImageFrame中
hr = NuiImageStreamGetNextFrame(colorStreamHandle, 0, &pImageFrame);
colorStreamHandle
为前面保存了Kinect设备的彩色信息通道的句柄,这个函数会从colorStreamHandle
中取出RGB图像数据,并保存在pImageFrame
中。第二个参数,表示延时多久获取数据,直接取为0,就是不等待直接取数据。
成功调用完这个函数之后,从Kinect捕获到的一帧图像,会保存在一个NUI_IMAGE_FRAME
结构体中,pImageFrame
为指向那个结构体的指针,其中包含了很多信息,如:图像类型,分辨率,图像缓冲区,时间戳等等。
6、提取数据帧并锁定数据;
INuiFrameTexture * pTexture = pImageFrame->pFrameTexture;
NUI_LOCKED_RECT LockedRect;
pTexture->LockRect(0, &LockedRect, NULL, 0);
INuiFrameTexture
是一个保存图像帧数据的对象,主要要用到他的下面两个共有方法:
LockRect
:给缓冲区上锁;UnlockRect
:给缓冲区解锁;
因为图像帧是保存在缓冲区的,如果不上锁的话,缓冲区中还有的图像可能会导致Kinect修改要取出的图像。
提取数据帧到LockedRect
后,它包含两个数据对象:pitch
,每行字节数;pBits
,第一个字节地址。
7、将数据转换为OpenCV的Mat格式。
if (LockedRect.Pitch != 0)
for (int i = 0; i < img.rows; i++)
uchar *ptr = img.ptr<uchar>(i);
uchar *pBuffer = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;
for (int j = 0;j < img.cols;j++)
ptr[3 * j] = pBuffer[4 * j];
ptr[3 * j + 1] = pBuffer[4 * j + 1];
ptr[3 * j + 2] = pBuffer[4 * j + 2];
这一部分没什么说的了,就是把LockedRect
中的数据取出来,保存为OpenCV支持的Mat格式。
- https://blog.csdn.net/zouxy09/article/details/8146266
- https://blog.csdn.net/timebomb/article/details/7169372
这个笔记总体来说不难,主要是套路,微软官网的文档早就撤了,毕竟用的还是Kinect v1.0的,靠着博客和看看源码大概还能用用。
前段时间直到最近感觉都挺多事情的,很多笔记和写好的代码都没时间去整理,还要加把劲了。这段时间又有世界杯,熬夜看球什么的真的挺“伤”的。
方法是:利用KinectSDK读出彩色图和深度图,利用MFC窗显示
2.KinectSDK+OpenCV
方法是:利用KinectSDK读出彩色图和深度图,利用openCV显示
3.OpenNI和OpenCV
方法是:这个代码非常简单,一看便知
Kinect有三个数据流:彩色图,深度图和骨骼点。KinectSDK是利用三个数据流返回的句柄读取对应信息。
KinectSDK+OpenCV的方法详细内容可以参照博客:http://blog.csdn.net/yangtrees/article/details/16106271
利用OpenNI读取kinect代码非常简单,不再做解释。利用OpenNI读取信息的时候,要正确安装OpenNI和对应的Kinect驱动。
利用本资源提供的源代码,配置好OpenCv,OpenNI和KinectSDK即可运行。
kinect的深度数据和彩色数据的分辨率以及视场大小都不一样,不能直接对应起来,想要把深度和彩色融合起来就要费一番周折了。
看了MSDN中kinectSDK的介绍,发现了一个ICoordinateMapper的类,看介绍知道里面有一个成员函数MapDepthFrameToColorSpace可以实现深度图和彩色图的坐标校准,于
想试试看,结果转到定义处才发现这个成员函数是纯虚函数!!白高兴一场
在代码里进行了坐标转换,把CameraSpace转到DepthSpace。
然后用和示例代码(D2D的)里一样的方式进行绘制,但是由于Opencv里自带的显示图像的窗口默认以BGR3通道显示图像,Alpha值无法表示出来,所以,手势的半透明效果无法展示。最重要的一点,这程序运行起来的CPU占用率居然连1% 都不到,D2D那个要20%+。#include "opencv2/core.hpp"
Kinect基础版
此应用程序仅获取 Kinect 数据流并将其显示在屏幕上。
有两个显示窗口,一个用于 RGB 流(您的普通相机),另一个用于深度流(您离 Kinect 多远的可视化)。
要生成,请在 Visual Studio 中运行,然后按生成。
作者 | aipiano @CSDN编辑 | 3D视觉开发者社区导读Kinect作为微软推出的XBOX360体感周边外设,具有获取深度信息的能力。但由于Kinect自身RGB摄像头分辨率有限,其清晰度也略低,对于较高要求的开发者来说不够适用。因此,很多开发者都会使用第三方摄像头代替Kinect摄像头,但是二者之间如何配准一直是个问题。本篇就“如何将Kinect的深度图与第...
微软的kinect和华硕的xtion在RGBD物体识别上用得比较多,一般来说都是通过openni来采集的,pcl里io模块可以直接调用这个接口。但是对于之前接触图像和opencv比较多的同学来说,使用opencv比较方便。本文即介绍如何通过opencv显示kinect或者xtion的RGBD图像。opencv3编译在编译过程中需要打包编译openni2相关库,注意opencv官网上说的是> Conf
自从有了Kinect,根据深度图提取前景就非常方便了。因此出现了很多虚拟现实、视频融合等应用。但是,Kinect自身的RGB摄像头分辨率有限,清晰度也不及一些专业摄像头,因此有了用第三方摄像头代替Kinect摄像头的想法。现在的问题是,如何将Kinect的深度图与第三方摄像头的RGB图像对准?
我们知道,当使用Kinect的RGB时,有方便的MapColorCoordinatesToDepth(...
#include <NuiApi.h>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;int main(int argc, char *argv[])
Mat image;
//这里我们用灰度图来表述深度数
我正在使用Kinect v2,我必须将深度信息映射到RGB图像上来处理它们:特别是,我需要知道RGB图像中哪些像素在Z的某个距离(深度)范围内轴;我正在使用C#程序获取所有数据并将其保存为图像(RGB)和txt文件(深度) .我遵循了here和here的指示(我感谢他们分享),但我仍然遇到一些我不知道如何解决的问题 .我已经计算了深度传感器和RGB相机之间的旋转(R)和平移(T)矩阵,以及它们的内...
//最远距离(mm)const int MAX_DISTANCE = 3500;//最近距离(mm)const int MIN_DISTANCE = 300;int main(){//彩色图像Mat image_rgb;//深度图像Mat image_depth;//创建一个MATimage_rgb.create(480,640,CV_8UC3);image_depth.create(240,32
首先说一下,本系统所使用的开发环境版本是计算机系统Windows10、VisualStudio2013、Opencv3.1.0和KinectSDKv2.0。
vs2013需要vc12,如果VS版本更高的话可以使用较高版本的opencv,最好是vs、opencv版本一致,不然很容易出现问题。
Kinect sdk可直接从官网下载,OpenCV的配置方法和Kinect引入VS开发环境网上...
博主弱鸡一个,最近想利用kinect实现一个rgbd的稠密重建,但又不想基于ros(其实是还没怎么系统学),那咋办,就把kinect单独抽离出来呗。网上搜了一下,发现大部分都是kinect2的,关于1的比较少,由于博主装这个也花了一天的时间(确实是个弱鸡啊QAQ),现在就来把里面的坑和步骤都说一下,以便后来人参考。首先,你需要安装openni,sensor和NITE这三个包,博主一开始也是在网上找...
#include <time.h>
#include <signal.h>
#include <opencv2/opencv.hpp>#include <libfreenect2/libfreenect2.hpp>
不带用户ID的深度数据,也是存储在16位的变量中,但是只使用了前12位,用来表示深度。
带用户ID的深度数据,16位,前3位表示用户ID,最多可以识别6个人,后13位表示深度;
在前一篇文章(Kinect学习(四):提取深度数据...