前言
  由于最近要研究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. 12th,Jan 研究生创新项目申报成功
  2. cordova开发插件,并在android studio中开发、调试
  3. Java解压上传zip或rar文件,并解压遍历文件中的html的路径
  4. 从东岳流体下载自带OpenFOAM的Vmware虚拟机(Ubuntu20.04+OpenFOAM),无法共享文件夹【终极解决方案】
  5. 集算器访问HTTP数据的代码示例
  6. 数字三角形(洛谷P1216题题解,C++语言描述)
  7. Centos 安装配置gerrit
  8. php项目如何导jdbc包,JDBC-idea导入mysql连接java的jar包(mac)的方法
  9. 判断是否为数组的 JavaScript 方法总结
  10. MySQL + Atlas --- 部署读写分离
  11. 基于java的高校实习生管理系统
  12. QT5.11下载与安装教程
  13. PPT背景填充的几种方式,简单高效
  14. hdu 6202 cube cube cube
  15. 域用户登录方法在计算机上不被允许,域管理员不允许本地交互式登陆的解决方法...
  16. 重要信息通知短信解决方案
  17. java中的toArray()
  18. 2020年R2移动式压力容器充装多少分及格及R2移动式压力容器充装考试申请表
  19. Comment obtenir des vecteurs de phrases?
  20. 剪辑视频,教你虚化边框背景一看就会

热门文章

  1. 实验6:蚁群算法在TSP问题中的实现
  2. 为什么要用NoSQL
  3. 僵死进程(将死进程)
  4. lisp 左手钢筋_钢筋下料小常识.doc
  5. Python爬虫-带你自制新闻网站,先学先会一起来学吧
  6. 二、51单片机 使用Proteus仿真掌握矩阵键盘(仿真及代码)
  7. docker将镜像导出为tar文件并导入启动容器
  8. 跟大家分享一下,前端不算es6,就有八种循环,你用过几种呢
  9. Opencv中鼠标作为画笔
  10. 基于javaweb的演唱会售票管理系统(java+ssm+jsp+javascript+jquery+mysql)