【Kinect】1st-提取颜色代码分析

提取颜色数据并用OpenCV显示

http://blog.csdn.net/zouxy09/article/details/8146266

#include <windows.h>
#include <iostream> 
#include <NuiApi.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char *argv[])
{
    Mat image;
    image.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; 
}
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<image.rows; i++) 
            {
                uchar *ptr = image.ptr<uchar>(i);  //第i行的指针

                //每个字节代表一个颜色信息,直接使用uchar
                uchar *pBuffer = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;
                for (int j=0; j<image.cols; j++) 
                { 
                    ptr[3*j] = pBuffer[4*j];  //内部数据是4个字节,0-1-2是BGR,第4个现在未使用 
                    ptr[3*j+1] = pBuffer[4*j+1]; 
                    ptr[3*j+2] = pBuffer[4*j+2]; 
                } 
            } 
            imshow("colorImage", image); //显示图像 
        } 
        else 
        { 
            cout<<"Buffer length of received texture is bogus\r\n"<<endl; 
        }

        //5、这帧已经处理完了,所以将其解锁
        pTexture->UnlockRect(0);
        //6、释放本帧数据,准备迎接下一帧 
        NuiImageStreamReleaseFrame(colorStreamHandle, pImageFrame ); 
    } 
    if (cvWaitKey(20) == 27) 
        break; 
} 
//7、关闭NUI链接 
NuiShutdown(); 
return 0;
}

首先,对Kinect,我们必须要包含下面两个头文件:

#include <windows.h>

#include <NuiApi.h>

1、初始化NUI

HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR);

任何想使用微软提供的API来操作KINECT,都必须在所有操作之前,调用NUI的初始化函数:
HRESULT NuiInitialize(DWORD dwFlags);

dwFlags参数是以标志位的含义存在的。可以使用下面几个值来指定你打算使用NUI中的哪些内容。

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX 提供带用户信息的深度图数据;
NUI_INITIALIZE_FLAG_USES_COLOR 提供色彩图像数据;
NUI_INITIALIZE_FLAG_USES_SKELETON 提供骨骼点数据;
NUI_INITIALIZE_FLAG_USES_DEPTH 提供深度图像数据.
NUI_INITIALIZE_FLAG_USES_AUDIO 提供声音数据;
NUI_INITIALIZE_DEFAULT_HARDWARE_THREAD 初始化默认的硬件线程;

以上的标志位,你可以使用一个,也可以用 | 操作符将它们组合在一起。

另外,Kinect提供了两种处理返回值的方式,就是判断上面的函数是否执行成功。

//这是一种处理返回值的方式
if( FAILED( hr ) )
  {
    cout<<"NuiInitialize failed"<<endl;
    return hr;
  }
//这是另一种处理返回值的方式
if(hr == S_OK)
  {
    cout<<"NuiInitialize successfully"<<endl;
  }

2、定义事件句柄

HANDLE nextColorFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

CreateEvent()创建一个windows事件对象,创建成功则返回事件的句柄。事件有两个状态,有信号和没有信号!上面说到了。就是拿来等待新数据的。

CreateEvent函数需要4个参数: 设定为NULL的安全描述符; 一个设定为true的布尔值,因为应用程序将重置事件消息;
一个未指定的事件消息初始状态的布尔值; 一个空字符串,因为事件未命名。

HANDLE colorStreamHandle = NULL; //保存图像数据流的句柄,用以提取数据

3、打开KINECT设备的彩色数据流

hr=NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, NUI_IMAGE_RESOLUTION_640x480,0,2,nextColorFrameEvent,&colorStreamHandle);

我们使用这个函数来打开kinect彩色或者深度图的访问通道,当然,其内部原理是通过”流”来实现的,因此,你也可以把这个函数理解为,创建一个访问彩色或者深度图的数据流。

参数:
eImageType [in]
这是一个 NUI_IMAGE_TYPE 枚举类型的值,用来详细指定你要创建的流类型。
比如你要打开彩色图,就使用 NUI_IMAGE_TYPE_COLOR。 要打开深度图,就使用 NUI_IMAGE_TYPE_DEPTH。
能打开的图像类型,必须是你在初始化的时候指定过的。

eResolution [in]
这是一个 NUI_IMAGE_RESOLUTION 枚举类型的值,用来指定你要以什么分辨率来打开eImageType(参数1)中指定的图像类别。
假如你在参数eImageType中指定的是彩色图NUI_IMAGE_TYPE_COLOR,那么可以选择2种分辨率:
NUI_IMAGE_RESOLUTION_1280x1024,NUI_IMAGE_RESOLUTION_640x480
如果你在参数eImageType中指定的是深度图NUI_IMAGE_TYPE_DEPTH,那么可以选择3种分辨率
NUI_IMAGE_RESOLUTION_640x480,
NUI_IMAGE_RESOLUTION_320x240,
NUI_IMAGE_RESOLUTION_80x60

dwImageFrameFlags_NotUsed
[in] 这是个无用参数,随便给个整数就行了。

dwFrameLimit
指定NUI运行时环境将要为你所打开的图像类型建立几个缓冲。最大值是NUI_IMAGE_STREAM_FRAME_LIMIT_MAXIMUM(当前版本为 4)对于大多数啊程序来说,2就足够了。

hNextFrameEvent
[in, optional] 一个用来手动重置信号是否可用的事件句柄(event),该信号用来控制KINECT是否可以开始读取下一帧数据。也就是说在这里指定一个句柄后,随着程序往后继续推进,当你在任何时候想要控制kinect读取下一帧数据时,都应该先使用WaitForSingleObject判断一下该句柄,判断是否有数据可拿。

phStreamHandle
[out]出参,指定一个句柄的地址。函数成功执行后,将会创建对应的数据访问通道(流),并且让该句柄保存这个通道的地址。也就是说,如果现在创建成功了。那么以后你想读取数据,就要通过这个句柄了。

返回值
只有S_OK表示成功打开,错误原因却有很多,比如打开一个没初始化过的数据流;打开一个已被使用的数据流;参数phStreamHandle为NULL等等。自己查阅API手册吧。

4、无限等待新的数据,等到后返回

WaitForSingleObject(nextColorFrameEvent, INFINITE)==0

和刚才说的一样,程序运行都这里,这个事件有信号,就是说有数据,那么程序往下执行,如果没有数据,就会等待。函数第二个参数表示你愿意等多久,具体的数据的话就表示你愿意等多少毫秒,还不来,我就不要了,继续往下走。如果是INFINITE的话,就表示无限等待新数据,直到海枯石烂,一定等到为止。等到有信号后就返回0 。

5、从数据流中拿数据

hr = NuiImageStreamGetNextFrame(colorStreamHandle, 0, &pImageFrame);

从刚才打开数据流的流句柄中得到该帧数据,读取到的数据地址存于pImageFrame。第二个参数表示你延时多少微秒拿数据,0表示,我立刻拿。

如果你没有遇到什么错误的话,那么刚才KINECT就捕获了一副画面,并将该画面的信息保存在一个NUI_IMAGE_FRAME结构中,pImageFrame指向该结构的地址。

pImageFrame包含了很多有用信息,包括:图像类型,分辨率,图像缓冲区,时间戳等等。

6、INuiFrameTexture接口

INuiFrameTexture * pTexture = pImageFrame->pFrameTexture;

一个容纳图像帧数据的对象,类似于Direct3D纹理,但是只有一层(不支持mip-maping)。

其公有方法包含以下:

AddRef—增加一个对象上接口的引用数目;该方法在每复制一个指向该对象上接口的指针时都要调用一次;

BufferLen—获得缓冲区的字节长度;

GetLevelDesc—获得缓冲区的描述;

LockRect—给缓冲区上锁;

Pitch—返回一行的字节数;

QueryInterface—获取指向对象所支持的接口的指针,该方法对其所返回的指针调用AddRef函数;

Release—减少一个对象上接口的引用计数;

UnlockRect—对缓冲区解锁;

7、提取数据帧到LockedRect并锁定数据

pTexture->LockRect(0, &LockedRect, NULL, 0);

提取数据帧到LockedRect,它包括两个数据对象:pitch每行字节数,pBits第一个字节地址。另外,其还锁定数据,这样当我们读数据的时候,kinect就不会去修改它

好了,现在真正保存图像的对象LockedRect我们已经有了,并且也将图像信息写入这个对象了。

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

然后我们就将其保存图像的对象LockedRect的格式,转化为OpenCV的Mat格式,便于我们处理和显示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值