在前面的文章中介绍了如何搭建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);
    //1、初始化NUI
    HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR);
    if (FAILED(hr))
        cout << "NuiInitialize failed" << endl;
        return hr;
    //2、定义事件句柄
    //创建读取下一帧的信号事件句柄,控制KINECT是否可以开始读取下一帧数据
    HANDLE nextColorFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    HANDLE colorStreamHandle = NULL;//保存图像数据流的句柄,用于提取数据
    //3、打开KINECT设备的彩色信息通道,并用colorStreamHandle保存该流的句柄,以便于以后读取
    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);
    //4、开始读取彩色图数据
    while (1)
        const NUI_IMAGE_FRAME * pImageFrame = NULL;
        //4.1、无线等待新的数据,等到就返回
        if (WaitForSingleObject(nextColorFrameEvent, INFINITE) == 0)
            //4.2、从刚才打开数据流的流句柄中得到该帧的数据,读取到的数据地址存于pImageFrame中
            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;
            //4.3、提取数据帧到LockedRect(它包括两个数据对象:pitch每行字节数,pBits第一个字节地址)
            //并锁定数据,这样当我们读取数据的时候,kinect就不会去修改它
            pTexture->LockRect(0, &LockedRect, NULL, 0);
            //4.4、确认获得的数据是否有效
            if (LockedRect.Pitch != 0)
                //4.5、将数据转换为OpenCV的Mat格式
                for (int i = 0; i < img.rows; i++)
                    uchar *ptr = img.ptr<uchar>(i); //第i行的指针
                    //每个字节代表一个颜色信息,直接使用uchar
                    uchar *pBuffer = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;
                    for (int j = 0;j < img.cols;j++)
                        //内部数据是4个字节,0-1-2是BGR,第4个现在未使用
                        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;
            //5、这帧已经处理完了,所以将其解锁
            pTexture->UnlockRect(0);
            //6、释放本帧数据,准备获取下一帧
            NuiImageStreamReleaseFrame(colorStreamHandle, pImageFrame);
        if (cv::waitKey(20) == 27)
            break;
    //7、关闭NUI连接
    NuiShutdown();
    return 0;

整个程序可以分为以下流程:

  1. 初始化NUI接口;
  2. 定义事件句柄;
  3. 打开Kinect设备的数据流(彩色RGB);
  4. 等待数据更新,若更新完成则进行下一步;
  5. 从数据流中拿出图像数据;
  6. 提取数据帧并锁定数据;
  7. 将数据转换为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、定义事件句柄

//2、定义事件句柄
//创建读取下一帧的信号事件句柄,控制KINECT是否可以开始读取下一帧数据
HANDLE nextColorFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

该函数会创建一个windows事件对象,创建成功则返回事件的句柄。这里的这个事件是用来判断是否有新数据的。
其中有四个参数:

  • 第一个是安全属性,设定为NULL的安全描述符;
  • 第二个表示设置信号复位方式为自动恢复为无信号状态(FALSE)还是手动恢复为无信号状态(TRUE),设为TRUE,因为后面应用程序会重置事件消息;
  • 第三个是事件消息初始状态的布尔值,为FALSE
  • 最后一个是信号名称,可以直接设置为NULL

    3、打开Kinect设备的彩色图像数据流

//3、打开KINECT设备的彩色信息通道,并用colorStreamHandle保存该流的句柄,以便于以后读取
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

参数说明:

  1. eImageType:这是一个NUI_IMAGE_TYPE枚举类型的变量,用来指定要创建的数据流的类型。比如,NUI_IMAGE_TYPE_COLOR对应彩色图,NUI_IMAGE_TYPE_DEPTH对应深度图。注意,这里指定的图像的类型,必须是前面初始化是已经指定过的,否则无法打开。
  2. eResolution:这是一个NUI_IMAGE_RESOLUTION枚举类型的变量,用来指定打开图像的分别率,但是由于3D结构光摄像头与RGB摄像头的分辨率不同,所以根据前面eImageType参数指定的图像类型不同,这里也有所不同。如果eImageType指定为彩色图NUI_IMAGE_TYPE_COLOR,那么就有两种分辨率:NUI_IMAGE_RESOLUTION_1280x960NUI_IMAGE_RESOLUTION_640x480;如果eImageType指定为深度图NUI_IMAGE_TYPE_DEPTH,那么就有三种分辨率:NUI_IMAGE_RESOLUTION_640x480NUI_IMAGE_RESOLUTION_320x240NUI_IMAGE_RESOLUTION_80x60
  3. dwImageFrameFlags_NotUsed:看名字就知道了,没有用到这个参数,随便给个数就可以了。
  4. dwFrameLimit:指定NUI运行时环境将要为你所打开的图像类型建立几个缓冲。最大值是NUI_IMAGE_STREAM_FRAME_LIMIT_MAXIMUM,即4。大多数程序中,定为2就足够了。
  5. hNextFrameEvent: 一个用来手动重置信号是否可用的事件句柄(event),该信号用来控制Kinect是否可以开始读取下一帧数据。也就是说在这里指定一个句柄后,随着程序往后继续运行,当你在任何时候想要控制kinect读取下一帧数据时,都应该先使用WaitForSingleObject函数判断一下该句柄,判断是否有数据可拿。
  6. phStreamHandle:函数执行成功后,会创建对应的数据流,并让这个句柄保存其地址。后面可以通过这个句柄来从Kinect读取数据。
  7. 返回值: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;
//4.3、提取数据帧到LockedRect(它包括两个数据对象:pitch每行字节数,pBits第一个字节地址)
//并锁定数据,这样当我们读取数据的时候,kinect就不会去修改它
pTexture->LockRect(0, &LockedRect, NULL, 0);

INuiFrameTexture是一个保存图像帧数据的对象,主要要用到他的下面两个共有方法:

  • LockRect:给缓冲区上锁;
  • UnlockRect:给缓冲区解锁;

因为图像帧是保存在缓冲区的,如果不上锁的话,缓冲区中还有的图像可能会导致Kinect修改要取出的图像。

提取数据帧到LockedRect后,它包含两个数据对象:pitch,每行字节数;pBits,第一个字节地址。

7、将数据转换为OpenCV的Mat格式。

//4.4、确认获得的数据是否有效
if (LockedRect.Pitch != 0)
    //4.5、将数据转换为OpenCV的Mat格式
    for (int i = 0; i < img.rows; i++)
        uchar *ptr = img.ptr<uchar>(i); //第i行的指针
        //每个字节代表一个颜色信息,直接使用uchar
        uchar *pBuffer = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;
        for (int j = 0;j < img.cols;j++)
            //内部数据是4个字节,0-1-2是BGR,第4个现在未使用
            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格式。

  1. https://blog.csdn.net/zouxy09/article/details/8146266
  2. 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图像对准? 我们知道,当使用KinectRGB时,有方便的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学习(四):提取深度数据...