kinect 学习笔记一

颜色深度图像的显示:初始化、绑定流、提取流。
1、提取颜色数据:
 
#include <iostream> 
#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"<<endl; 
        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"<<endl; 
        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"<<endl; 
            } 
            //释放本帧数据,准备迎接下一帧 
            NuiImageStreamReleaseFrame( h2, pImageFrame ); 
        } 
 
        if (cvWaitKey(30) == 27) 
            break; 
    } 
    //关闭NUI链接 
    NuiShutdown(); 
    return 0; 
} 
 


实验结果:

\
 
 
2、提取带有用户ID的深度数据
 
#include <iostream> 
#include "Windows.h" 
#include "MSR_NuiApi.h" 
#include "cv.h" 
#include "highgui.h" 
 
using namespace std; 
 
RGBQUAD Nui_ShortToQuad_Depth( USHORT s )<span style="background-color: rgb(255, 255, 255); ">//该函数我是调用的SDK自带例子的函数。</span> 
 
{ 
    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"<<endl; 
        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"<<endl; 
        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"<<endl; 
            } 
            //释放本帧数据,准备迎接下一帧 
            NuiImageStreamReleaseFrame( h2, pImageFrame ); 
        } 
 
        if (cvWaitKey(30) == 27) 
            break; 
    } 
    //关闭NUI链接 
    NuiShutdown(); 
    return 0; 
} 



 
实验结果:
\

 
3、不带ID的深度数据的提取
 
#include <iostream> 
#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"<<endl; 
        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"<<endl; 
        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"<<endl; 
            } 
            //释放本帧数据,准备迎接下一帧 
            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 <iostream>   
#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 < NUI_SKELETON_POSITION_COUNT; 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 < NUI_SKELETON_POSITION_COUNT ; 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"<<endl;   
        return hr;   
    }   
    //打开KINECT设备的彩色图信息通道   
    HANDLE h1 = CreateEvent( NULL, TRUE, FALSE, NULL );    
 
    hr = NuiSkeletonTrackingEnable( h1, 0 );//打开骨骼跟踪事件 
    if( FAILED( hr ) ) 
    { 
        cout << "NuiSkeletonTrackingEnable fail" << endl; 
        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 < NUI_SKELETON_COUNT ; 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 < NUI_SKELETON_COUNT ; 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;   
} 
 


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


摘自 timebomb的专栏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度学习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值