樓主搬磚需要用到kinect v2 的深度圖與彩色圖,無奈樓主乃半路出家的程序員,對windows和C++都不熟,而網上的教程基本是C++和C#寫的,於是我潛心研究kinect sdk裡面的sample數日,加上請教另一位懂C++的夥伴,終於生拉硬刨出能用的圖像。

廢話少說,正文。

1.首先下載vs 2013以及以上版本,以及最近的sdk, 將kinect插上usb3.0口。

2.打開 SDK Browser, 選擇安裝Depth Basics-D2D(其實就是保存源代碼),然後打開DepthBasics.cpp這個文件,通過修改這個文件來獲取需要的圖像。

代碼裡面的CDepthBasics::Update函數用來獲取kinect的深度幀並保存為pBuffer。

深度圖與彩色圖對齊需要用到MapDepthFrameToColorSpace這個函數,通過查閱MSDN,可以知道這個函數通過深度幀的信息來計算深度圖和彩色圖的映射關係

public:
HRESULT MapDepthFrameToColorSpace(
UINT depthPointCount,
const UINT16 *depthFrameData,
UINT colorPointCount,
ColorSpacePoint *colorSpacePoints
)

第一個參數為深度幀裡面點的個數,對Kinect2為 512*424

第二個參數為深度幀

第三個參數是輸出彩色圖的像素個數,也是512*424第四個參數是二者的映射矩陣

通過查找ColorSpacePoint定義,可以知道它儲存的是深度圖像的每個點對應到1920*1080彩圖上的橫縱坐標。但需要注意的是,這種對應關係是通過Kinect相機的內參矩陣直接計算的,所以計算結果保存為浮點數,而且會有很多點超過1920*1080的範圍甚至是負無窮大。

typedef struct _ColorSpacePoint
{
float X;
float Y;
} ColorSpacePoint;

有了這些東西,就能開始魔改代碼了。

首先在代碼最前面定義映射矩陣:

std::vector<ColorSpacePoint> MappingMatrix(512 * 424);

然後在CDepthBasics::Update函數的SafeRelease(pFrameDescription)前添加計算映射矩陣的代碼:

if (SUCCEEDED(hr))
{
ICoordinateMapper* pCoordinateMapper;
m_pKinectSensor->get_CoordinateMapper(&pCoordinateMapper);
pCoordinateMapper->MapDepthFrameToColorSpace(512 * 424, pBuffer, 512 * 424, &MappingMatrix[0]);
}

最後在截圖保存圖像的CDepthBasics::SaveBitmapToFile函數裡面將MappingMatrix保存下來,比如存為txt文件,這樣每次按截圖的時候都能保存一個映射矩陣。

// Write txt file
std::ofstream out("myfile.txt");
for (size_t i = 0; i < 512*424; i++)
{
out << MappingMatrix[i].X << " " << MappingMatrix[i].Y<< std::endl;
}
out.close();

彩色圖可以通過SDK Browser裡面的sample直接獲取,這樣有了深度圖,彩色圖和映射矩陣,就能在自己熟悉的開發環境裡面生成和深度圖對齊的彩圖。

比如樓主用的python + PIL, 幾行代碼就能搞定。

import numpy as np
from PIL import Image
import shutil

mapping_matrix_name = 1.txt
color_image_name = 1.bmp
mapping_matrix = open(mapping_matrix_name, r).readlines()
color_image = np.array(Image.open(color_image_name))

mapped_color_image = np.zeros((424, 512, 3), dtype = uint8)
for i in range(424):
for j in range(512):
matrix_indices = i*512 + j
color_x = mapping_matrix[matrix_indices].split()[0]
color_y = mapping_matrix[matrix_indices].split()[1]
if not (color_x == -inf or color_y == -inf):
color_x = int(round(float(color_x)))
color_y = int(round(float(color_y)))
if color_x>=0 and color_x<1920 and color_y>=0 and color_y<1080:
mapped_color_image[i][j][0] = int(color_image[color_y][color_x][0])
mapped_color_image[i][j][1] = int(color_image[color_y][color_x][1])
mapped_color_image[i][j][2] = int(color_image[color_y][color_x][2])

img = Image.fromarray(mapped_color_image)
img.save(test.png)

效果如下:

512*424深度圖

1920*1080彩色圖

對齊後的512*424彩色圖,缺失的地方用黑色表示

二者對比

3. 保存完整深度信息。

SDK的sample裡面深度圖都是8位bmp圖像,而實際深度是16位整型,通過閱讀DepthBasics.cpp裡面的CDepthBasics::ProcessDepth函數,我們發現sample code對深度進行了求余運算

BYTE intensity = static_cast<BYTE>((depth >= nMinDepth) && (depth <= nMaxDepth) ? (depth % 256) : 0);

pRGBX->rgbRed = intensity;
pRGBX->rgbGreen = intensity;
pRGBX->rgbBlue = intensity;

++pRGBX;
++pBuffer;

那麼怎麼盡量少修改代碼來保存完整深度信息呢,我們發現 pRGBX其實還有一個Reserved通道,一般是用來儲存透明度,因此只需要把商存在Reserved通道就能保存完整的深度信息,並在後面的處理中進行還原。

BYTE intensity = static_cast<BYTE>((depth >= nMinDepth) && (depth <= nMaxDepth) ? (depth % 256) : 0);
BYTE alpha = depth / 256;

pRGBX->rgbRed = intensity;
pRGBX->rgbGreen = intensity;
pRGBX->rgbBlue = intensity;
pRGBX->rgbReserved = alpha;

++pRGBX;
++pBuffer;

以上都是非專業程序員的奇技淫巧。


推薦閱讀:
查看原文 >>
相关文章