Kinect V2开发(3)用OpenCV显示Kinect图像

先从Kinect中读取深度图像,再用OpenCV显示出来。
1.深度图像
如下图所示,感应器可靠深度最小值到最大值的范围是500-4500,实际上这个范围是微软定义的,是固定值,说是适合用作人体骨架追踪,实际上这个值可以取到更大。
深度图像中每一个点的值都代表该点离感应器的距离,离越远,值越大。在显示出来后,就是越接近白色的部分越远,越接近黑色的部分越近。
这里写图片描述

代码如下:

#include <iostream>
// OpenCV 头文件
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
// Kinect for Windows SDK 头文件
#include <Kinect.h>

using namespace std;
int main(void)
{
    // 1a.获取感应器
    IKinectSensor* pSensor = nullptr;
    GetDefaultKinectSensor(&pSensor);

    // 1b. 打开感应器
    pSensor->Open();

    // 2a. 取得深度数据
    IDepthFrameSource* pFrameSource = nullptr;
    pSensor->get_DepthFrameSource(&pFrameSource);

    // 2b. 取得深度数据的描述信息(宽、高)
    int        iWidth = 0;
    int        iHeight = 0;
    IFrameDescription* pFrameDescription = nullptr;
    pFrameSource->get_FrameDescription(&pFrameDescription);
    pFrameDescription->get_Width(&iWidth);
    pFrameDescription->get_Height(&iHeight);
    pFrameDescription->Release();
    pFrameDescription = nullptr;

    // 2c. 取得感应器的“可靠深度”的最大、最小距离
    UINT16 uDepthMin = 0, uDepthMax = 0;
    pFrameSource->get_DepthMinReliableDistance(&uDepthMin);
    pFrameSource->get_DepthMaxReliableDistance(&uDepthMax);
    cout << "Reliable Distance: "
        << uDepthMin << " – " << uDepthMax << endl;

    // 建立图像矩阵,mDepthImg用来存储16位的图像数据,直接用来显示会接近全黑
    //不方便观察,用mImg8bit转换成8位再显示
    cv::Mat mDepthImg(iHeight, iWidth, CV_16UC1);
    cv::Mat mImg8bit(iHeight, iWidth, CV_8UC1);
    cv::namedWindow("DepthImage");

    // 3a. 打开深度数据阅读器
    IDepthFrameReader* pFrameReader = nullptr;
    pFrameSource->OpenReader(&pFrameReader);

    // 主循环
    while (1)
    {
        // 4a. 取得最新数据
        IDepthFrame* pFrame = nullptr;
        if (pFrameReader->AcquireLatestFrame(&pFrame) == S_OK)
        {
            // 4c. 把数据存入16位图像矩阵中
            pFrame->CopyFrameDataToArray(iWidth * iHeight,
                reinterpret_cast<UINT16*>(mDepthImg.data));//强制转换数据类型

           // 4d. 把16位转换成8位
            mDepthImg.convertTo(mImg8bit, CV_8U, 255.0f / uDepthMax);//converto()第一个参数是输出矩阵,第二个是转换类型,第三个是缩放因子,其中4500是深度数据的最大距离
            cv::imshow("DepthImage", mImg8bit);
                                                   //要改变显示的颜色和效果,就改变从mDepthImg到mImg8bit的转换  
            // 4e. 释放变量pFrame
            pFrame->Release();
        }

        if (cv::waitKey(30) == VK_ESCAPE) {
            break;
        }
    }

    // 3b. 释放变量pFrameReader
    pFrameReader->Release();
    pFrameReader = nullptr;

    // 2d.释放变量pFramesSource
    pFrameSource->Release();
    pFrameSource = nullptr;

    // 1c.关闭感应器
    pSensor->Close();

    // 1d.释放感应器
    pSensor->Release();
    pSensor = nullptr;

    return 0;
}

2.红外图像
红外图像的读取思路跟深度图像一样,代码也差不多只需要把函数名称替换一下。深度图像读的是物体离感应器的距离,而红外图像中的点表示的是感应器检测到的红外线强度。
这里写图片描述

另外,因为Kinect自身发出红外线,当检测物体距离过近时,会因为检测到的红外线太强出现一片白。

3.彩色图像
彩色图像的读取思路跟深度图像也一样,区别在于对输出数据的处理。
通常来说,彩色图像数据有多种格式,最常用的是RGB(3原色),其他常用的还有YUV色彩空间以及CMYK等等。Kinect的彩色图像数据格式为YUV2,调用IColorFrame中get_RawColorImageFormat()可读出数据格式,这种格式的数据处理起来比较麻烦,我们可以通过调用IColorFrame中CopyConvertedFrameDataToArray()来进行格式转换。
这里写图片描述
由上图可知,这个函数有3个参数,第一个参数是指容量,实际上是宽乘以高再乘以4个通道(width * height * 4),第二个参数是指目标数组,第三个参数是要转换的数据格式,包含ColorImageFormat_Rgba、ColorImageFormat_Bgra、ColorImageFormat_Byer、ColorImageFormat_Yuv、ColorImageFormat_Yuv2,一般比较常用的就是RGBA、BGRA和YUV了。

// 1a.获取感应器
    IKinectSensor* pSensor = nullptr;
    GetDefaultKinectSensor(&pSensor);

    // 1b. 打开感应器
    pSensor->Open();

    // 2a. 取得深度数据
    IColorFrameSource* pFrameSource = nullptr;
    pSensor->get_ColorFrameSource(&pFrameSource);

    // 2b. 取得深度数据的描述信息(宽、高)
    int        iWidth = 0;
    int        iHeight = 0;
    IFrameDescription* pFrameDescription = nullptr;
    pFrameSource->get_FrameDescription(&pFrameDescription);
    pFrameDescription->get_Width(&iWidth);
    pFrameDescription->get_Height(&iHeight);
    pFrameDescription->Release();
    pFrameDescription = nullptr;

    // 建立图像矩阵,mColorImg用来存储8位4通道的图像数据
    cv::Mat mColorImg(iHeight, iWidth, CV_8UC4);
    cv::namedWindow("ColorImage");

    // 3a. 打开深度数据阅读器
    IColorFrameReader* pFrameReader = nullptr;
    pFrameSource->OpenReader(&pFrameReader);

    // 主循环
    while (1)
    {
        // 4a. 取得最新数据
        IColorFrame* pFrame = nullptr;
        if (pFrameReader->AcquireLatestFrame(&pFrame) == S_OK)
        {
            // 4c. 把数据存入图像矩阵中并转换成BGRA格式
            pFrame->CopyConvertedFrameDataToArray(iWidth * iHeight * 4, (BYTE*)mColorImg.data, ColorImageFormat_Bgra);
            cv::imshow("ColorImage", mColorImg);

            // 4e. 释放变量pFrame
            pFrame->Release();
        }

        if (cv::waitKey(30) == VK_ESCAPE) {
            break;
        }
    }

    // 3b. 释放变量pFrameReader
    pFrameReader->Release();
    pFrameReader = nullptr;

    // 2d.释放变量pFramesSource
    pFrameSource->Release();
    pFrameSource = nullptr;

    // 1c.关闭感应器
    pSensor->Close();

    // 1d.释放感应器
    pSensor->Release();
    pSensor = nullptr;

RGBA和BGRA中的这个A代表的是透明度,所以开Mat时要开成四通道的(选8位而不选16位是因为转换函数第二个参数要求类型是BYTE,而一个BYTE是8位的)。
IColorFrame 还有一个 get_ColorCameraSettings() 的函数,可以取得 IColorCameraSettings 的信息,通过这个可以取得彩色图像的曝光时间、gain、gamma 等信息。


参考资料:
Hersey博客
K4W v2 C++ Part 2:使用 OpenCV 顯示深度影像
微软官方
Kinect Interface C++ Reference

展开阅读全文

没有更多推荐了,返回首页