热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

kinect学习笔记一

颜色深度图像的显示:初始化、绑定流、提取流。1、提取颜色数据:#include<iostream>#include"Windows.h"#include"
颜色深度图像的显示:初始化、绑定流、提取流。

1、提取颜色数据:

#include 
#include "Windows.h"
#include "MSR_NuiApi.h"
#include "cv.h"
#include "highgui.h"

using namespace std;

int main(int argc,char * argv[])
{
IplImage *colorImage=NULL;
colorImage = cvCreateImage(cvSize(640, 480), 8, 3);

//初始化NUI
HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR);
if( hr != S_OK )
{
cout<<"NuiInitialize failed"<return hr;
}
//定义事件句柄
HANDLE h1 = CreateEvent( NULL, TRUE, FALSE, NULL );//控制KINECT是否可以开始读取下一帧数据
HANDLE h2 = NULL;//保存数据流的地址,用以提取数据

hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR,NUI_IMAGE_RESOLUTION_640x480,0,2,h1,&h2);//打开KINECT设备的彩色图信息通道

if( FAILED( hr ) )//判断是否提取正确
{
cout<<"Could not open color image stream video"<NuiShutdown();
return hr;
}

//开始读取彩色图数据
while(1)
{
const NUI_IMAGE_FRAME * pImageFrame = NULL;

if (WaitForSingleObject(h1, INFINITE)==0)//判断是否得到了新的数据
{
NuiImageStreamGetNextFrame(h2, 0, &pImageFrame);//得到该帧数据
NuiImageBuffer *pTexture = pImageFrame->pFrameTexture;
KINECT_LOCKED_RECT LockedRect;
pTexture->LockRect(0, &LockedRect, NULL, 0);//提取数据帧到LockedRect,它包括两个数据对象:pitch每行字节数,pBits第一个字节地址
if( LockedRect.Pitch != 0 )
{
cvZero(colorImage);
for (int i=0; i<480; i++)
{
uchar* ptr = (uchar*)(colorImage->imageData+i*colorImage->widthStep);
BYTE * pBuffer = (BYTE*)(LockedRect.pBits)+i*LockedRect.Pitch;//每个字节代表一个颜色信息,直接使用BYTE
for (int j=0; j<640; 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];
}
}

cvShowImage("colorImage", colorImage);//显示图像

}
else
{
cout<<"Buffer length of received texture is bogus\r\n"<}
//释放本帧数据,准备迎接下一帧
NuiImageStreamReleaseFrame( h2, pImageFrame );
}

if (cvWaitKey(30) == 27)
break;
}
//关闭NUI链接
NuiShutdown();
return 0;
}


实验结果:


2、提取带有用户ID的深度数据

#include 
#include "Windows.h"
#include "MSR_NuiApi.h"
#include "cv.h"
#include "highgui.h"

using namespace std;

RGBQUAD Nui_ShortToQuad_Depth( USHORT s )//该函数我是调用的SDK自带例子的函数。

{
USHORT RealDepth = (s & 0xfff8) >> 3;//提取距离信息
USHORT Player = s & 7 ;//提取ID信息

//16bit的信息,其中最低3位是ID(所捕捉到的人的ID),剩下的13位才是信息

BYTE l = 255 - (BYTE)(256*RealDepth/0x0fff);//因为提取的信息时距离信息,这里归一化为0-255。======这里一直不明白为什么是除以0x0fff,希望了解的同志给解释一下。

RGBQUAD q;
q.rgbRed = q.rgbBlue = q.rgbGreen = 0;

switch( Player )
{
case 0:
q.rgbRed = l / 2;
q.rgbBlue = l / 2;
q.rgbGreen = l / 2;
break;
case 1:
q.rgbRed = l;
break;
case 2:
q.rgbGreen = l;
break;
case 3:
q.rgbRed = l / 4;
q.rgbGreen = l;
q.rgbBlue = l;
break;
case 4:
q.rgbRed = l;
q.rgbGreen = l;
q.rgbBlue = l / 4;
break;
case 5:
q.rgbRed = l;
q.rgbGreen = l / 4;
q.rgbBlue = l;
break;
case 6:
q.rgbRed = l / 2;
q.rgbGreen = l / 2;
q.rgbBlue = l;
break;
case 7:
q.rgbRed = 255 - ( l / 2 );
q.rgbGreen = 255 - ( l / 2 );
q.rgbBlue = 255 - ( l / 2 );
}

return q;
}

int main(int argc,char * argv[])
{
IplImage *depthIndexImage=NULL;
depthIndexImage = cvCreateImage(cvSize(320, 240), 8, 3);

//初始化NUI
HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX );
if( hr != S_OK )
{
cout<<"NuiInitialize failed"<return hr;
}
//打开KINECT设备的彩色图信息通道
HANDLE h1 = CreateEvent( NULL, TRUE, FALSE, NULL );
HANDLE h2 = NULL;

hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,NUI_IMAGE_RESOLUTION_320x240,0,2,h1,&h2);//这里根据文档信息,当初始化是NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX时,分辨率只能是320*240或者80*60

if( FAILED( hr ) )
{
cout<<"Could not open color image stream video"<NuiShutdown();
return hr;
}

while(1)
{
const NUI_IMAGE_FRAME * pImageFrame = NULL;

if (WaitForSingleObject(h1, INFINITE)==0)
{
NuiImageStreamGetNextFrame(h2, 0, &pImageFrame);
NuiImageBuffer *pTexture = pImageFrame->pFrameTexture;
KINECT_LOCKED_RECT LockedRect;
pTexture->LockRect(0, &LockedRect, NULL, 0);
if( LockedRect.Pitch != 0 )
{
cvZero(depthIndexImage);
for (int i=0; i<240; i++)
{
uchar* ptr = (uchar*)(depthIndexImage->imageData+i*depthIndexImage->widthStep);
BYTE * pBuffer = (BYTE *)(LockedRect.pBits)+i*LockedRect.Pitch;
USHORT * pBufferRun = (USHORT*) pBuffer;//注意这里需要转换,因为每个数据是2个字节,存储的同上面的颜色信息不一样,这里是2个字节一个信息,不能再用BYTE,转化为USHORT
for (int j=0; j<320; j++)
{
RGBQUAD rgb = Nui_ShortToQuad_Depth(pBufferRun[j]);//调用函数进行转化
ptr[3*j] = rgb.rgbBlue;
ptr[3*j+1] = rgb.rgbGreen;
ptr[3*j+2] = rgb.rgbRed;
}
}

cvShowImage("depthIndexImage", depthIndexImage);
}
else
{
cout<<"Buffer length of received texture is bogus\r\n"<}
//释放本帧数据,准备迎接下一帧
NuiImageStreamReleaseFrame( h2, pImageFrame );
}

if (cvWaitKey(30) == 27)
break;
}
//关闭NUI链接
NuiShutdown();
return 0;
}

实验结果:


3、不带ID的深度数据的提取

#include 
#include "Windows.h"
#include "MSR_NuiApi.h"
#include "cv.h"
#include "highgui.h"

using namespace std;


int main(int argc,char * argv[])
{
IplImage *depthIndexImage=NULL;
depthIndexImage = cvCreateImage(cvSize(320, 240), 8, 1);//这里我们用灰度图来表述深度数据,越远的数据越暗。

//初始化NUI
HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH);
if( hr != S_OK )
{
cout<<"NuiInitialize failed"<return hr;
}
//打开KINECT设备的彩色图信息通道
HANDLE h1 = CreateEvent( NULL, TRUE, FALSE, NULL );
HANDLE h2 = NULL;

hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH,NUI_IMAGE_RESOLUTION_320x240,0,2,h1,&h2);//这里根据文档信息,当初始化是NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX时,分辨率只能是320*240或者80*60

if( FAILED( hr ) )
{
cout<<"Could not open color image stream video"<NuiShutdown();
return hr;
}

while(1)
{
const NUI_IMAGE_FRAME * pImageFrame = NULL;

if (WaitForSingleObject(h1, INFINITE)==0)
{
NuiImageStreamGetNextFrame(h2, 0, &pImageFrame);
NuiImageBuffer *pTexture = pImageFrame->pFrameTexture;
KINECT_LOCKED_RECT LockedRect;
pTexture->LockRect(0, &LockedRect, NULL, 0);
if( LockedRect.Pitch != 0 )
{
cvZero(depthIndexImage);
for (int i=0; i<240; i++)
{
uchar* ptr = (uchar*)(depthIndexImage->imageData+i*depthIndexImage->widthStep);
BYTE * pBuffer = (BYTE *)(LockedRect.pBits)+i*LockedRect.Pitch;
USHORT * pBufferRun = (USHORT*) pBuffer;//注意这里需要转换,因为每个数据是2个字节,存储的同上面的颜色信息不一样,这里是2个字节一个信息,不能再用BYTE,转化为USHORT
for (int j=0; j<320; j++)
{
ptr[j] = 255 - (BYTE)(256*pBufferRun[j]/0x0fff);//直接将数据归一化处理
}
}

cvShowImage("depthIndexImage", depthIndexImage);
}
else
{
cout<<"Buffer length of received texture is bogus\r\n"<}
//释放本帧数据,准备迎接下一帧
NuiImageStreamReleaseFrame( h2, pImageFrame );
}

if (cvWaitKey(30) == 27)
break;
}
//关闭NUI链接
NuiShutdown();
return 0;
}

实验结果:


4、需要注意的地方

NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX与NUI_INITIALIZE_FLAG_USES_DEPTH不能同时创建数据流。这个我在试验中证实了。而且单纯的深度图像是左右倒置的。

文中归一化的地方除以0x0fff的原因是kinect的有效距离是1.2m到3.5m(官方文档),如果是3.5m那用十六进制表示是0x0DAC,我在实际测试中我的实验室能够测到的最大距离是0x0F87也就是3975mm。估计是官方他们直接使用极限距离0x0FFF来作为除数的。

文中的cv.h,highgui.h是我使用的opencv中的库,因为对这个比较熟悉。


5、骨骼数据的提取

#include   
#include "Windows.h"
#include "MSR_NuiApi.h"
#include "cv.h"
#include "highgui.h"

using namespace std;

void Nui_DrawSkeleton(NUI_SKELETON_DATA * pSkel,int whichone, IplImage *SkeletonImage)//画出骨骼,第二个参数未使用,想跟踪多人的童鞋可以考虑使用
{
float fx, fy;
CvPoint SkeletonPoint[NUI_SKELETON_POSITION_COUNT];
for (int i = 0; i {
NuiTransformSkeletonToDepthImageF( pSkel->SkeletonPositions[i], &fx, &fy );
SkeletonPoint[i].x = (int)(fx*320+0.5f);
SkeletonPoint[i].y = (int)(fy*240+0.5f);
}

for (int i = 0; i {
if (pSkel->eSkeletonPositionTrackingState[i] != NUI_SKELETON_POSITION_NOT_TRACKED)//跟踪点一用有三种状态:1没有被跟踪到,2跟踪到,3根据跟踪到的估计到
{
cvCircle(SkeletonImage, SkeletonPoint[i], 3, cvScalar(0, 255, 255), -1, 8, 0);
}
}
return;

}

int main(int argc,char * argv[])
{
IplImage *skeletOnImage=NULL;
skeletOnImage= cvCreateImage(cvSize(320, 240), 8, 3);

//初始化NUI
HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON );
if( hr != S_OK )
{
cout<<"NuiInitialize failed"<return hr;
}
//打开KINECT设备的彩色图信息通道
HANDLE h1 = CreateEvent( NULL, TRUE, FALSE, NULL );

hr = NuiSkeletonTrackingEnable( h1, 0 );//打开骨骼跟踪事件
if( FAILED( hr ) )
{
cout <<"NuiSkeletonTrackingEnable fail" <NuiShutdown();
return hr;
}

while(1)
{
if(WaitForSingleObject(h1, INFINITE)==0)
{
NUI_SKELETON_FRAME SkeletonFrame;//骨骼帧的定义
bool bFoundSkeleton = false;

if( SUCCEEDED(NuiSkeletonGetNextFrame( 0, &SkeletonFrame )) )//Get the next frame of skeleton data.直接从kinect中提取骨骼帧
{
for( int i = 0 ; i {
if( SkeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED )//最多跟踪六个人,检查每个“人”(有可能是空,不是人)是否跟踪到了
{
bFoundSkeleton = true;
}
}
}

if( !bFoundSkeleton )
{
continue;;
}

// smooth out the skeleton data
NuiTransformSmooth(&SkeletonFrame,NULL);//平滑骨骼帧,消除抖动


// draw each skeleton color according to the slot within they are found.
cvZero(skeletonImage);
for( int i = 0 ; i {
// Show skeleton only if it is tracked, and the center-shoulder joint is at least inferred.
//断定是否是一个正确骨骼的条件:骨骼被跟踪到并且肩部中心(颈部位置)必须跟踪到。

if( SkeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED &&
SkeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_SHOULDER_CENTER] != NUI_SKELETON_POSITION_NOT_TRACKED)
{
Nui_DrawSkeleton(&SkeletonFrame.SkeletonData[i], i , skeletonImage);
}
}

cvShowImage("skeletonImage", skeletonImage);//显示骨骼图像。
cvWaitKey(30);
}
}
//关闭NUI链接
NuiShutdown();
return 0;
}


实验结果:没有画出连线,大家如果想继续做可以对那个数组进行处理连线就可以了。




推荐阅读
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 使用C++编写程序实现增加或删除桌面的右键列表项
    本文介绍了使用C++编写程序实现增加或删除桌面的右键列表项的方法。首先通过操作注册表来实现增加或删除右键列表项的目的,然后使用管理注册表的函数来编写程序。文章详细介绍了使用的五种函数:RegCreateKey、RegSetValueEx、RegOpenKeyEx、RegDeleteKey和RegCloseKey,并给出了增加一项的函数写法。通过本文的方法,可以方便地自定义桌面的右键列表项。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文介绍了在Cpp中将字符串形式的数值转换为int或float等数值类型的方法,主要使用了strtol、strtod和strtoul函数。这些函数可以将以null结尾的字符串转换为long int、double或unsigned long类型的数值,且支持任意进制的字符串转换。相比之下,atoi函数只能转换十进制数值且没有错误返回。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
author-avatar
非机动车地了
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有