前言
  由于最近要研究kinect采集到的深度信息的一些统计特征,所以必须先对kinect深度信息做进一步的了解。这些了解包括kinect的深 度值精度,深度值的具体代表的距离是指哪个距离以及kinect深度和颜色扫描范围等。经过查找资料可以解决这些问题,并且后面通过实验也验证了这些问题 的答案。
  开发环境:开发环境:QtCreator2.5.1+OpenNI1.5.4.0+Qt4.8.2+OpenCV2.4.3
 
  实验基础
  首先来看下Kinect性能的基本参数,如下图所示:
   
   Kinect在使用时,微软官方推荐的距离为1220mm(4’)~3810mm(12.5’),网友heresy在他的博文Kinect + OpenNI 的深度值中 统计过,kinect在距离为1.0m时其精度大概是3mm,而当距离是3.0m时,其精度大概是3cm,因此当kinect在官方推荐的距离范围内使用 是,如果是1.2m时,其精度应该在3mm附近,如果是3.6m时其精度就大于3cm了,因此距离越远,其深度值精度越低。另外,通过OpenNI获取到 的深度信息(即z坐标)的单位是mm,这一点在程序编程中要注意,且一般的深度值用12bit(其实是可以用13bit表示的)表示,即最大值为 4095,也就是代表4.095m,所以平时我们采集到的深度数据如果需要扩展到灰度图,可以乘以一个因子255/4095(为了加快该结果,一般设置为 256/4096,http://ming.ttplay8.cn 即转换后的灰度值每变化1,代表kinect采集到的深度值变化了16mm,如果当人的距离为1米左右时,本来精度是3mm,现在经过 归一化后精度确更加下降了,这时候拿这个距离值来做算法不懂会不会有影响,当然了,拿来做灰度图像的显示肯定是OK的),最后如果其深度值为0表示该位置 处侦测不到像素点的深度。
  Kinect的侦测范围入下图所示:
   
  可以看出,kinect的水平侦测范围为57度(即以sensor为中心,左右各28.5度)。垂直范围为43度(同理,以sensor为中 心,上下各21.5度)。如果人体活动超过了kinect侦测范围,kinect还会自动追焦27度,即马达能够上下旋转27度(因为涉及到专利的问 题,OpenNI驱动没有这个功能,微软SDK可以),因此理论上上下扫描的范围应该为97度(27+27+43)。但是水平方向上虽然有马达,可以手动 掰动kinect,不过在驱动中并没有对应的水平角度旋转的API,即使是微软的SDK也一样。
  Kinect的倾斜角度如下图所示:
   
  下面来解释Kinect采集到的深度值的具体含义:
  Openni的原始驱动类中的depth_metadata_其实也是一副图像,图像的坐标表示空间点的投影坐标,图像坐标里存的值是对应空间 点投影坐标的深度值。该深度值并不是指空间中对应像素点到深度sensor点之间的距离(即2点直接的距离),而是指空间中对应像素点到kinect传感 器所在平面的距离(即是一个垂直距离),因为前面已经提到,kinect是可以上下旋转的。现假设三种情况,第一:我们不上下旋转kinect,即保持 kinect传感器平面与水平地面垂直,这时像素点X深度值为a;第二:将kinect往上旋转一个角度(当然了,这个角度值小于27度),http://feiche.ttplay8.cn 这时候同样一 个像素点X的深度值为b;第三:将kinect往下旋转一个角度Beta角度,这时候X的深度值为c;你会发现,a,b,c这3者不一定相等。
 
  OpenCV知识点总结:
  当Mat中数据的类型为CV_16UC1的时候,这里的16U并不是指unsigned int,而是指的是unsigned short int,因为在OpenCV框架中,int不是16位的,而是32位的。没想到我使用OpenCV一年了,今天才弄清楚这个。
 
  实验结果:
  该实验是测试一个垂直摆放的柜子,该柜子一个平面上的点本来与kinect之间的距离是相等的,现在测试kinect在不同上下旋转角度的情况 下,这个柜子上的点的深度值是否一样。首先将kinect往上旋转一个角度,即kinect平面与水平面之间有一个夹角。实验结果如下:
   
  图中显示的数字为鼠标所在位置像素的真实深度值。
 
  柜子中同一个平面上另一个像素点的深度值结果如下:
   
  由此可以看出,同一个垂直柜子平面上的点像素值相差30cm以上(其实从图中深度图的颜色信息就可以看出,该柜子深度图像都是倾斜的,因为kinect本身就有转角)。
 
  如果把kinect所在的平面摆正,即与水平面之间没有夹角,则柜子上某一点的深度值如下图所示:
   
 
  同一垂直平面上另一个点的如下所示:
   
  由此可以看出,其深度值变化不大。
  上面的实验结果解释了在实验基础部分中所讲的kinect深度值的含义。
 
  实验主要代码及注释:
  copenni.h:
 
#ifndef COpenniHand_H
#define COpenniHand_H

#include <XnCppWrapper.h>
#include <iostream>
#include <vector>
#include <map>

using namespace xn;
using namespace std;

class COpenniHand
{
public:
    COpenniHand();
    ~COpenniHand();

/*OpenNI的内部初始化,属性设置*/
    bool Initial();

/*启动OpenNI读取Kinect数据*/
    bool Start();

/*更新OpenNI读取到的数据*/
    bool UpdateData();

/*得到色彩图像的node*/
    ImageGenerator& getImageGenerator();

/*得到深度图像的node*/
    DepthGenerator& getDepthGenerator();

/*得到手势姿势的node*/
    GestureGenerator& getGestureGenerator();

/*得到给定投影坐标的点,返回其对应真实坐标的深度值*/
    XnFloat GetProjectWorldPixpelDepth(XnPoint3D *project_world_pixpel);

/*得到手部的node*/
    HandsGenerator& getHandGenerator();
    DepthMetaData depth_metadata_;   //返回深度图像数据
    ImageMetaData image_metadata_;   //返回彩色图像数据
    std::map<XnUserID, XnPoint3D> hand_points_;  //为了存储不同手的实时点而设置的
    std::map< XnUserID, vector<XnPoint3D> > hands_track_points_; //为了绘画后面不同手部的跟踪轨迹而设定的

private:
    /*该函数返回真代表出现了错误,返回假代表正确*/
    bool CheckError(const char* error);

/*表示某个手势动作已经完成检测的回调函数*/
    static void XN_CALLBACK_TYPE  CBGestureRecognized(xn::GestureGenerator &generator, const XnChar *strGesture,
                                                      const XnPoint3D *pIDPosition, const XnPoint3D *pEndPosition,
                                                      void *pCookie);

/*表示检测到某个手势开始的回调函数*/
    static void XN_CALLBACK_TYPE CBGestureProgress(xn::GestureGenerator &generator, const XnChar *strGesture,
                                                   const XnPoint3D *pPosition, XnFloat fProgress, void *pCookie);

/*手部开始建立的回调函数*/
    static void XN_CALLBACK_TYPE HandCreate(HandsGenerator& rHands, XnUserID xUID, const XnPoint3D* pPosition,
                                            XnFloat fTime, void* pCookie);

/*手部开始更新的回调函数*/
    static void XN_CALLBACK_TYPE HandUpdate(HandsGenerator& rHands, XnUserID xUID, const XnPoint3D* pPosition, XnFloat fTime,
                                            void* pCookie);

/*手部销毁的回调函数*/
    static void XN_CALLBACK_TYPE HandDestroy(HandsGenerator& rHands, XnUserID xUID, XnFloat fTime, void* pCookie);

XnStatus status_;
    Context context_;
    XnMapOutputMode xmode_;
    ImageGenerator  image_generator_;
    DepthGenerator  depth_generator_;
    GestureGenerator gesture_generator_;
    HandsGenerator  hand_generator_;
};

#endif // COpenniHand_H
 
 
  copenni.cpp:
 
#include "copennihand.h"
#include <XnCppWrapper.h>
#include <iostream>
#include <map>

using namespace xn;
using namespace std;

COpenniHand::COpenniHand()
{
}

COpenniHand::~COpenniHand()
{
}

bool COpenniHand::Initial()
{
    status_ = context_.Init();
    if(CheckError("Context initial failed!")) {
        return false;
    }

context_.SetGlobalMirror(true);//设置镜像
    xmode_.nXRes = 640;
    xmode_.nYRes = 480;
    xmode_.nFPS = 30;

//产生颜色node
    status_ = image_generator_.Create(context_);
    if(CheckError("Create image generator  error!")) {
        return false;
    }

//设置颜色图片输出模式
    status_ = image_generator_.SetMapOutputMode(xmode_);
    if(CheckError("SetMapOutputMdoe error!")) {
        return false;
    }

//产生深度node
    status_ = depth_generator_.Create(context_);
    if(CheckError("Create depth generator  error!")) {
        return false;
    }

//设置深度图片输出模式
    status_ = depth_generator_.SetMapOutputMode(xmode_);
    if(CheckError("SetMapOutputMdoe error!")) {
        return false;
    }

//产生手势node
    status_ = gesture_generator_.Create(context_);
    if(CheckError("Create gesture generator error!")) {
        return false;
    }

/*添加手势识别的种类*/
    gesture_generator_.AddGesture("Wave", NULL);
    gesture_generator_.AddGesture("click", NULL);
    gesture_generator_.AddGesture("RaiseHand", NULL);
    gesture_generator_.AddGesture("MovingHand", NULL);

//产生手部的node
    status_ = hand_generator_.Create(context_);
    if(CheckError("Create hand generaotr error!")) {
        return false;
    }

//视角校正
    status_ = depth_generator_.GetAlternativeViewPointCap().SetViewPoint(image_generator_);
    if(CheckError("Can't set the alternative view point on depth generator!")) {
        return false;
    }

//设置与手势有关的回调函数
    XnCallbackHandle gesture_cb;
    gesture_generator_.RegisterGestureCallbacks(CBGestureRecognized, CBGestureProgress, this, gesture_cb);

//设置于手部有关的回调函数
    XnCallbackHandle hands_cb;
    hand_generator_.RegisterHandCallbacks(HandCreate, HandUpdate, HandDestroy, this, hands_cb);

return true;
}

bool COpenniHand::Start()
{
    status_ = context_.StartGeneratingAll();
    if(CheckError("Start generating error!")) {
        return false;
    }
    return true;
}

bool COpenniHand::UpdateData()
{
    status_ = context_.WaitNoneUpdateAll();
    if(CheckError("Update date error!")) {
        return false;
    }
    //获取数据
    image_generator_.GetMetaData(image_metadata_);
    depth_generator_.GetMetaData(depth_metadata_);

return true;
}

ImageGenerator &COpenniHand::getImageGenerator()
{
    return image_generator_;
}

DepthGenerator &COpenniHand::getDepthGenerator()
{
    return depth_generator_;
}

GestureGenerator &COpenniHand::getGestureGenerator()
{
    return gesture_generator_;
}

HandsGenerator &COpenniHand::getHandGenerator()
{
    return hand_generator_;
}

bool COpenniHand::CheckError(const char *error)
{
    if(status_ != XN_STATUS_OK) {
        cerr << error << ": " << xnGetStatusString( status_ ) << endl;
        return true;
    }
    return false;
}

void COpenniHand::CBGestureRecognized(GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pIDPosition, const XnPoint3D *pEndPosition, void *pCookie)
{
    COpenniHand *openni = (COpenniHand*)pCookie;
    openni->hand_generator_.StartTracking(*pEndPosition);
}

void COpenniHand::CBGestureProgress(GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pPosition, XnFloat fProgress, void *pCookie)
{
}

void COpenniHand::HandCreate(HandsGenerator &rHands, XnUserID xUID, const XnPoint3D *pPosition, XnFloat fTime, void *pCookie)
{
    COpenniHand *openni = (COpenniHand*)pCookie;
    XnPoint3D project_pos;
    openni->depth_generator_.ConvertRealWorldToProjective(1, pPosition, &project_pos);
    pair<XnUserID, XnPoint3D> hand_point_pair(xUID, XnPoint3D());//在进行pair类型的定义时,可以将第2个设置为空
    hand_point_pair.second = project_pos;
    openni->hand_points_.insert(hand_point_pair);//将检测到的手部存入map类型的hand_points_中。

pair<XnUserID, vector<XnPoint3D>> hand_track_point(xUID, vector<XnPoint3D>());
    hand_track_point.second.push_back(project_pos);
    openni->hands_track_points_.insert(hand_track_point);
}

void COpenniHand::HandUpdate(HandsGenerator &rHands, XnUserID xUID, const XnPoint3D *pPosition, XnFloat fTime, void *pCookie)
{
    COpenniHand *openni = (COpenniHand*)pCookie;
    XnPoint3D project_pos;
    openni->depth_generator_.ConvertRealWorldToProjective(1, pPosition, &project_pos);
    openni->hand_points_.find(xUID)->second = project_pos;
    openni->hands_track_points_.find(xUID)->second.push_back(project_pos);
}

void COpenniHand::HandDestroy(HandsGenerator &rHands, XnUserID xUID, XnFloat fTime, void *pCookie)
{
    COpenniHand *openni = (COpenniHand*)pCookie;
    openni->hand_points_.erase(openni->hand_points_.find(xUID));
    openni->hands_track_points_.erase(openni->hands_track_points_.find(xUID ));
}
 
 
  ckinectopencv.h:
 
#ifndef CKINECTOPENCV_H
#define CKINECTOPENCV_H

#include <opencv2/core/core.hpp>
#include "copennihand.h"
#include "XnCppWrapper.h"

using namespace cv;
using namespace xn;

class CKinectOpenCV
{
public:
    CKinectOpenCV();
    ~CKinectOpenCV();
    void GetAllInformation();   //在返回有用信息前调用该函数,因为openni的数据在不断更新,信息的处理最好放在一个函数中
    Mat GetColorImage() ;
    Mat GetDepthImage() ;
    Mat GetDepthRealValueImage();
    std::map<XnUserID, XnPoint3D> GetHandPoints();

private:
    COpenniHand openni_hand_;
    std::map<XnUserID, XnPoint3D> hand_points_;  //为了存储不同手的实时点而设置的
    Mat color_image_;    //颜色图像
    Mat depth_image_;    //深度图像
    Mat depth_realvalue_image_;

};

#endif // CKINECTOPENCV_H
 
 
  ckinectopencv.cpp:
 
#include "ckinectopencv.h"
#include <opencv2/imgproc/imgproc.hpp>
#include <map>

using namespace cv;
using namespace std;

#define DEPTH_SCALE_FACTOR 255./4096.

CKinectOpenCV::CKinectOpenCV()
{   
    /*初始化openni对应的设备*/
     CV_Assert(openni_hand_.Initial());

/*启动openni对应的设备*/
    CV_Assert(openni_hand_.Start());
}

CKinectOpenCV::~CKinectOpenCV()
{
}

void CKinectOpenCV::GetAllInformation()
{
    CV_Assert(openni_hand_.UpdateData());
    /*获取色彩图像*/
    Mat color_image_src(openni_hand_.image_metadata_.YRes(), openni_hand_.image_metadata_.XRes(),
                        CV_8UC3, (char *)openni_hand_.image_metadata_.Data());
    cvtColor(color_image_src, color_image_, CV_RGB2BGR);

/*获取深度图像*/
    Mat depth_image_src(openni_hand_.depth_metadata_.YRes(), openni_hand_.depth_metadata_.XRes(),
                        CV_16UC1, (char *)openni_hand_.depth_metadata_.Data());//因为kinect获取到的深度图像实际上是无符号的16位数据
    depth_image_src.convertTo(depth_image_, CV_8U, DEPTH_SCALE_FACTOR);

/*获取真实深度值图像,没有经过深度归一化的*/
    depth_realvalue_image_ = depth_image_src.clone();

hand_points_ = openni_hand_.hand_points_;   //返回手部点的位置

return;
}

Mat CKinectOpenCV::GetColorImage()
{
    return color_image_;
}

Mat CKinectOpenCV::GetDepthImage()
{
    return depth_image_;
}

Mat CKinectOpenCV::GetDepthRealValueImage()
{
    return depth_realvalue_image_;
}

std::map<XnUserID, XnPoint3D> CKinectOpenCV::GetHandPoints()
{
    return hand_points_;
}
 
 
  main.cpp:
 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "ckinectopencv.h"
#include "XnCppWrapper.h"

using namespace std;
using namespace cv;

CKinectOpenCV kinect_opencv;
Mat color_image ;
Mat depth_image ;
Mat depth_realvalue_image;
unsigned int  pixpel_depth_value = 0;

Point mouse_pixpel(0, 0);

void on_mouse(int event,int x,int y,int,void*)
{
    if(event == CV_EVENT_MOUSEMOVE) {  //鼠标移动的过程中
        pixpel_depth_value = depth_realvalue_image.at<unsigned short int>(y, x);
        mouse_pixpel = Point(x, y);
    }
}

int main()
{

//    namedWindow("color_image", CV_WINDOW_AUTOSIZE);
    namedWindow("depth_image", CV_WINDOW_AUTOSIZE);
    setMouseCallback("depth_image", on_mouse, 0);   //设置鼠标响应函数

while(1)
    {
        kinect_opencv.GetAllInformation();
//        color_image = kinect_opencv.GetColorImage();
        depth_image = kinect_opencv.GetDepthImage();
        depth_realvalue_image = kinect_opencv.GetDepthRealValueImage();
        stringstream depth_value_string;
        depth_value_string << pixpel_depth_value/1000. << " m" ;

putText(depth_image, depth_value_string.str(), mouse_pixpel, 3, 1, Scalar(50, 0, 0), 2, 8);

//        imshow("color_image", color_image);
        imshow("depth_image", depth_image);
        waitKey(30);
    }

return 0;
}
 
 
 
  实验总结: 通过本次资料的查找和实验的验证,对kinect的深度值有了更一步的了解了。

Kinect+OpenNI学习笔记之14(关于Kinect的深度信息)相关推荐

  1. Kinect+OpenNI学习笔记之2(获取kinect的颜色图像和深度图像)

    前言 网上有不少使用Qt做界面,OpenNI为库来开发kinect.或许大家的第一个问题就是询问该怎样使用Kinect来获取颜色信息图和深度信息图呢?这一节就是简单来回答这个问题的. 开发环境:QtC ...

  2. Kinect+OpenNI学习笔记之6(获取人体骨架并在Qt中显示)

    前言 MS的kinec SDK和OpenNI都提供了人体骨骼跟踪的算法,人体骨骼跟踪算法在kinect人体行为识别中非常重要,该识别过程通常被用来作为行为识别的第一步, 比如说,通过定位人体中的骨骼支 ...

  3. Kinect+OpenNI学习笔记之4(OpenNI获取的图像结合OpenCV显示)

    前言 本文来结合下opencv的highgui功能显示kinect采集得来的颜色图和深度图.本来在opencv中自带了VideoCapture类的,使用该类可以直接驱动kinect设备,具体的可以参考 ...

  4. Kinect+OpenNI学习笔记之13(Kinect驱动类,OpenCV显示类和手部预分割类的设计)

    前言 为了减小以后项目的开发效率,本次实验将OpenNI底层驱动Kinect,OpenCV初步处理OpenNI获得的原始数据,以及手势识别中的分割(因为本系统最后是开发手势识别的)这3个部分的功能单独 ...

  5. Kinect+OpenNI学习笔记之8(Robert-Walter手部提取代码的分析)

    前言 一般情况下,手势识别的第一步就是先手势定位,即手势所在部位的提取.本文是基于kinect来提取手势识别的,即 先通过kinect找出人体的轮廓,然后定位轮廓中与手部有关的点,在该点的周围提取出满 ...

  6. Kinect+OpenNI学习笔记之12(简单手势所表示的数字的识别)

    引自:http://www.cnblogs.com/tornadomeet/archive/2012/11/04/2753185.html 前言 这篇文章是本人玩kinect时做的一个小实验,即不采用 ...

  7. Kinect+OpenNI学习笔记之11(OpenNI驱动kinect手势相关的类的设计)

    前言 本文所设计的类主要是和人体的手部打交道的,与人体的检测,姿势校正,骨架跟踪没有关系,所以本次类的设计中是在前面的OpenNI+Kinect系列博文基础上去掉那些与手势无关的驱动,较小代码量负担. ...

  8. java openni rgb显示_Kinect+OpenNI学习笔记之4(OpenNI获取的图像结合OpenCV显示)

    前言 本文来结合下opencv的highgui功能显示kinect采集得来的颜色图和深度图.本来在opencv中自带了VideoCapture类的,使用该类可以直接驱动kinect设备,具体的可以参考 ...

  9. Kinect开发学习笔记之(二)Kinect开发学习资源整理

    Kinect开发学习笔记之(二)Kinect开发学习资源整理 zouxy09@qq.com http://blog.csdn.net/zouxy09 刚刚接触Kinect,在网上狂搜资料,获得了很多有 ...

最新文章

  1. Oracle表空间文件损坏后的排查及解决
  2. 超融合架构的优缺点_超融合服务器与传统架构服务器的区别哪?定制服务器厂家简要分析...
  3. Ruby已死——必须关注的内存问题
  4. 设计模式总结篇系列:工厂方法模式(Factory Method)
  5. linux安装mongodb(设置非root用户和开机启动)
  6. python显示图片列表_python读取图片任意范围区域
  7. 你缺啥,你缺一个得力的办公软件
  8. JavaScript之jQuery够用即可(查找筛选器、属性操作、jQuery文档处理)
  9. Linux文件系统的正确挂载方式
  10. 改变PSD文件单一的图标,让图标变成图像缩略图
  11. word2016如何插入目录以及页码
  12. 苹果开发者账户续费 支付授权失败
  13. 陈强教授 计量经济学及机器学习等数据集、程序等相关资源
  14. 使用aria2搭建离线下载服务器
  15. linux:解压命令
  16. SDL2函数简单介绍03
  17. 倾斜补偿的电子罗盘(3):椭球拟合,磁传感器软磁干扰和硬磁干扰的9参数校准
  18. 代码特效插件pycharm
  19. FEKO计算多层介质的反射系数
  20. 安卓毕业设计 记账app

热门文章

  1. Wemos D1mini(ESP8266)基础实验------按钮控制板载等亮灭
  2. Python在ACM输入模式下用Map代替For
  3. 8. A/D转换器【ADC0809】
  4. Mysql经典入门练习题(七)
  5. 两个令人大开眼界的视频
  6. M×N的矩阵,从左上角走,只能向右或者向下走,要求走过的每个元素的值加起来的和最大,步数不限
  7. Java 自带性能监控工具:监视和管理控制台 jconsole 的使用
  8. Win10设置java环境变量
  9. Java项目:SSM演唱会售票管理系统
  10. 中专计算机学生个人千字总结范文,学生个人总结中专范文6篇