坐标映射
本文解释了如何通过windows SDK将获得到的原始数据进行坐标空间的映射。
三个坐标空间
相机空间
相机空间指的是kinect使用的3D空间坐标。它是这样定义的:
- 坐标原点(x=0,y=0,z=0)位于kinect的红外相机中心。
- X轴方向为顺着kinect的照射方向的左方向
- Y轴方向为顺着kinect的照射方向的上方向
- z轴方向为顺着kinect的照射方向
- 坐标单位为米(m)
注意!这是一个右手坐标系,这与计算机图形学中定义的相机空间是一致的,但是有的环境是使用左手坐标系的,比如Unity。
深度图像空间
深度图像空间用来描述深度图片上的位置。x代表列,y代表行,(x,y)就表示深度图上的一个像素坐标。(0,0)对应于图片的左上角,而(511,423)代表着图片的右下角。
In some cases, a z value is needed to map out of depth space. For
these cases, simply sample the depth image at the row/column in
question, and use that value (which is depth in millimeters) directly
as z.
我们经常会用深度图来得到一个点云,这种情况下就要用到深度图像空间到相机空间的反投影。注意如果一个点一个点地去反投影,这样开销太大。所以推荐你使用MapDepthPointsToCameraSpace,GetDepthFrameToCameraSpaceTable这样的函数。
如果你还想知道深度图上每个像素对应的红外强度值,你可以直接从红外图像中读取相同坐标位置的像素,因为深度图和红外图都是一个传感器得到的。
如果你还想知道深度图上每个像素对应的彩色值,你会用到Coordinate mapping类来获得彩色图上对应的像素位置。
彩色图像空间
首先要知道在kinect上,彩色相机是和红外相机有一定平移距离的,它们观察的视角也就不相同,再加上深度图和彩色图的分辨率不同因此得到的图像也就不能按像素直接对应。类似深度图像空间。x代表列,y代表行,(x,y)就表示深度图上的一个像素坐标。(0,0)对应于图片的左上角,而(1919,1079)代表着图片的右下角。
深度空间映射到彩色图像空间常用于背景消除。只需要先从人物索引图像中确定哪些深度像素属于用户,然后通过坐标映射,找到对应的彩色值。
Coordinate mapping类
Coordinate mapping类主要完成下面的两个功能:
- 将3D相机坐标空间中的坐标投影到2D的深度图中,或从深度图中反投影到相机坐标空间。
- 找到深度图和彩色图中的对应的像素位置。
成员函数:
- GetCoordinateMappingChangedEventData
- GetDepthCameraIntrinsics:获得ir相机参数(出厂已写入设备)
- GetDepthFrameToCameraSpaceTable
- MapCameraPointsToColorSpace
- MapCameraPointsToDepthSpace
- MapCameraPointToColorSpace
- MapCameraPointToDepthSpace
- MapColorFrameToCameraSpace
- MapColorFrameToDepthSpace
- MapDepthFrameToCameraSpace
- MapDepthFrameToColorSpace
- MapDepthPointsToCameraSpace
- MapDepthPointsToColorSpace
- MapDepthPointToCameraSpace
- MapDepthPointToColorSpace
- SubscribeCoordinateMappingChanged
- UnsubscribeCoordinateMappingChanged
深度图映射到彩色图
这里用深度图映射到彩色图展示Coordinate mapping类的功能。
注意映射的概念,比如深度图映射到彩色图的意思是对于深度图上的一个像素,找到彩色图上的一个像素与之对应(与函数中x到y的映射概念类似)。
- 获得深度帧:
hr = m_pDepthFrameReader->AcquireLatestFrame(&m_pDepthFrame);
- 保存深度数据到数组
UINT16 *depthData = new UINT16[424*512];
hr = m_pDepthFrame->CopyFrameDataToArray(424 * 512, depthData);
- 获得坐标映射器:
ICoordinateMapper* m_pCoordinateMapper;
hr = m_pKinectSensor->get_CoordinateMapper(&m_pCoordinateMapper);
- 进行映射:
HRESULT hr = m_pCoordinateMapper->MapDepthFrameToColorSpace(512 * 424, depthData, 512 * 424, m_pColorCoordinates);
这里m_pColorCoordinates是最终得到的512*424大小的数组,元素为一个个坐标,代表了对应的彩色图上的坐标。
- 显示:
Mat i_depthToRgb(424, 512, CV_8UC4);
if (SUCCEEDED(hr))
{
for (int i = 0; i < 424 * 512; i++)
{
ColorSpacePoint p = m_pColorCoordinates[i];
if (p.X != -std::numeric_limits<float>::infinity() && p.Y != -std::numeric_limits<float>::infinity())
{
int colorX = static_cast<int>(p.X + 0.5f);
int colorY = static_cast<int>(p.Y + 0.5f);
if ((colorX >= 0 && colorX < 1920) && (colorY >= 0 && colorY < 1080))
{
i_depthToRgb.data[i*4] = i_rgb.data[(colorY * 1920 + colorX)*4];
i_depthToRgb.data[i*4 + 1] = i_rgb.data[(colorY * 1920 + colorX) * 4+1];
i_depthToRgb.data[i*4 + 2] = i_rgb.data[(colorY * 1920 + colorX) * 4+2];
i_depthToRgb.data[i*4 + 3] = i_rgb.data[(colorY * 1920 + colorX) * 4+3];
}
}
}
}
imshow("rgb2depth", i_rgbToDepth);
if (waitKey(1) == VK_ESCAPE)
break;
下图左侧表示的是原始的深度图,右侧是深度图映射到彩色图后,得到的彩色图。