再度開啟Kinect_深度影像分析
這次的研究
針對於opencv套用在Kinect v2 SDK 的深度影像呈現方式
我們已經很清楚知道Kinect 在處理 frame 數據的先後流程為
從「傳感器」獲得「源」
從「源」打開「閱讀器」
從「閱讀器」獲得「視訊幀(frame)」
(ps: 「幀」 讀作 ㄓㄥˋ 計算照片、字畫、影像等單位,也就是frame)
【程式碼】
#include <iostream>
#include <opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<opencv2\video\background_segm.hpp>
#include <Kinect.h>
using namespace std;
using namespace cv;
// Safe release for interfaces
template<class Interface>
inline void SafeRelease(Interface *& pInterfaceToRelease)
{
if (pInterfaceToRelease != NULL)
{
pInterfaceToRelease->Release();
pInterfaceToRelease = NULL;
}
}
int main()
{
//取得sensor
IKinectSensor* pSensor;//處理Kinect v2的Sensor接口
HRESULT hResult = S_OK;
hResult = GetDefaultKinectSensor(&pSensor);//取得預設的Sensor
if (FAILED(hResult))
{
cerr << " Error : GetDefaultKinectSensor " << endl;
return -1;
}
hResult = pSensor->Open();//打開Sensor
if (FAILED(hResult))
{
cerr << " Error : IKinectSensor::Open() " << endl;
return -1;
}
//從「Sensor」取得「Source」
IDepthFrameSource* pDepthSource;//取得Depth Frame的Source接口
hResult = pSensor->get_DepthFrameSource(&pDepthSource);//從Sensor取得Source
if (FAILED(hResult))
{
cerr << " Error : IKinectSensor::get_DepthFrameSource() " << endl;
return -1;
}
//從「Source」打開「Reader」
IDepthFrameReader* pDepthReader;//取得Depth Frame的Reader接口
hResult = pDepthSource->OpenReader(&pDepthReader);//從Source打開Reader
if (FAILED(hResult))
{
cerr << " Error : IDepthFrameSource::OpenReader() " << endl;
return -1;
}
//從「Reader」取得最新的「Frame」
int width = 512;
int height = 424;
unsigned int bufferSize = width * height * sizeof (unsigned short);
Mat bufferMat(height, width, CV_16SC1);//每個pixel佔16位元,有正負號,因為16位元有正負號所以用char表示pixel,每個pixel有一個channel
Mat depthMat(height, width, CV_8UC1);//每個pixel佔8位元,無正負號,因為8位元無正負號所以用char表示pixel,每個pixel有一個channel
namedWindow(" Depth ");
while (1){
// Frame
IDepthFrame* pDepthFrame = nullptr;
hResult = pDepthReader->AcquireLatestFrame(&pDepthFrame);
if (SUCCEEDED(hResult)){
hResult = pDepthFrame->AccessUnderlyingBuffer(&bufferSize, reinterpret_cast<UINT16**>(&bufferMat.data));
if (SUCCEEDED(hResult)){
//bufferMat.convertTo(depthMat, CV_8U, 255.0f / 4500.0f ); //255.0/4500.0 = 0.0566666
//bufferMat.convertTo(depthMat, CV_8U, 0.005);//有點黑
//bufferMat.convertTo(depthMat, CV_8U, 0.05);//變亮了
//bufferMat.convertTo(depthMat, CV_8U, 0.5);//變超白
//bufferMat.convertTo(depthMat, CV_8U, 0.1);//白背景灰身體
//bufferMat.convertTo(depthMat, CV_8U, 0.2);//白背景淡灰身體
bufferMat.convertTo(depthMat, CV_8U, 0.3);//白背景白身體
//原Mat.convertTo(輸出Mat, int rtype, double alpha=1, double beta=0 );
}
}
SafeRelease(pDepthFrame);
// Show Window
cv::imshow(" Depth " , depthMat);
if (cv::waitKey(30) == VK_ESCAPE){
break;
}
}
return 0;
}
這裡我要介紹一個 opencv 的 特殊函數
叫做「 .convertTo( ) 」
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
將原先的Mat格式的變數轉換為另一種數據格式存儲到輸出矩陣m中,如果m的格式或大小不正確則會重新創建.rtype為轉換后的類型,如果為負數,則與原Mat相同.這個函數可以對其中的值進行縮放和偏移,公式為 m(x,y)=newtype((alpha*src(x,y)+beta))
如果你對這個函數 -->右鍵-->查看定義
他會跟你說
converts matrix to another datatype with optional scalng. See cvConvertScale.
為了處理Depth數據而準備的OpenCV的cv::Mat類型
「bufferMat」是16bit的原始的Depth數據,
「depthMat」為了作為圖像顯示,把Depth數據儲存到8bit的範圍裡的處理。
「CV_16UC1」,是把無符號16bit整數(16U) 放入1個channel(C1)
並列來表現1個像素的數據格式。
「CV_8UC1」,是表現無符號8bit整數 (8U)的數據格式。
Kinect V2 Depth Image架構示意圖
Depth數據的排列
一個pixel 佔16 bit
得到「Frame」,就可以把取出Depth數據,作成圖像來可視化。
取出的Depth數據,像圖3一樣以16bit(0~4500)為1像素來構成。
因為這樣的圖像不能顯示
(注:OpenCV只能顯示8bit的圖像數據)
所以我們才需要把格式轉化為8bit(0~255)的範圍。
這裡我使用cv::Mat的轉換函數(cv::Mat::convertTo())
把離傳感器距離近的顯示很白(255),遠的顯示為很黑(0)的方式來轉化。
把離傳感器距離近的顯示很黑(0),遠的顯示為很白(255)的方式來轉化。
其他還有很多種 double 的 參數可以試試看 看出來效果
本次分析
告一段落
留言
張貼留言