Acquire deep face

这周我们小组的同学有两门考试,还要准备周五的报告,但是我们还是坚持完成两项工作,一是获取深度中的人脸,二是人脸矫正。

获取深度帧人脸

Kinect SDK中自带的脸部帧接口是在1920×1080彩色帧上操作,而512×484深度帧与彩色帧是两个独立的数据流,考虑到两点,一是若直接在彩色帧上进行后续人脸相关工作,会非常耗时,另一个是彩色数据到深度数据的转换不能单点操作,因此为了获得3D人脸,我们尝试了三种思路:

  • 提取深度图上对应点的RGB信息,用OpenCV人脸检测器在该512×484彩色图上检测人脸,关键代码如下:
1
2
3
4
5
6
7
8
9
//提取深度图上对应点的RGB信息,注意这里要将BYTE数据转换成int
for (int y = 0; y < DEPTH_HEIGHT; y++){
for (int x = 0; x < DEPTH_WIDTH; x++){
RGBQUAD color = app.depth2Color(depthBuffer, x, y, colorBuffer);
colorImg.at<Vec3b>(Point(x, y))[0] = (int)color.rgbBlue;
colorImg.at<Vec3b>(Point(x, y))[1] = (int)color.rgbGreen;
colorImg.at<Vec3b>(Point(x, y))[2] = (int)color.rgbRed;
}
}
  • Kinect SDK面部帧接口在1920×1080彩色图上检测出人脸区域,再利用MapColorFrameToCameraSpace接口将彩色帧转换到相机空间,从而获得彩色图范围上人脸区域的三维坐标,在此基础上也可以获得5个关键点以及头部偏转角等,因此相关代码处理如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DepthSpacePoint* pPointArray = new DepthSpacePoint[COLOR_WIDTH * COLOR_HEIGHT];
hr = app.pCoordinateMapper->MapColorFrameToDepthSpace(DEPTH_WIDTH * DEPTH_HEIGHT, &depthBuffer[0], \
COLOR_WIDTH * COLOR_HEIGHT, pPointArray);

for (int y = r.y; y < (r.y + r.height); y++)
{
for (int x = r.x; x < (r.x + r.width); x++)
{
pcl::PointXYZRGB point;
point.r = colorBuffer[y * COLOR_WIDTH + x].rgbRed;
point.g = colorBuffer[y * COLOR_WIDTH + x].rgbGreen;
point.b = colorBuffer[y * COLOR_WIDTH + x].rgbBlue;

CameraSpacePoint cameraSpacePoint;
const DepthSpacePoint& dPoint = pPointArray[y * COLOR_WIDTH + x];
if (dPoint.X >= 0 && dPoint.X < DEPTH_WIDTH && dPoint.Y >= 0 && dPoint.Y < DEPTH_HEIGHT)
{
UINT16 depth = depthBuffer[dPoint.Y * DEPTH_WIDTH + dPoint.X];
app.pCoordinateMapper->MapDepthPointToCameraSpace(dPoint, depth, &cameraSpacePoint);

point.x = cameraSpacePoint.X;
point.y = cameraSpacePoint.Y;
point.z = cameraSpacePoint.Z;
}
pointCloud->push_back(point);
}
}
  • 首先在彩色图上检出人脸区域,同时得到深度图上每个点的三维坐标,遍历深度图上所有点,对应判断其是否在彩色图的人脸区域范围内,若是,则归为人脸区域,从而在深度图中确定出人脸区域。

综合比较上述三种方法,方法1依赖的是OpenCV人脸检测接口,实时性没有Kinect SDK好,并且不能同时获得面部关键点位置以及头部偏转角信息,而方法2是基于1920×1080的大图操作,性能低下,因此,最后我们采用第三种方法,效果图如下,图中右半部分可以看出,恢复过来的深度帧边缘部分有较多毛刺,这与Kinect深度图获取原理有关,从脸部区域来看,深度帧中人脸位置基本准确,边缘有稍许晕染。

人脸矫正

我们获取到的原始深度数据是距离值,也就是目标人物到深度摄像头的位置,考虑到后续配准建模等任务都是基于人脸操作,如果人脸3D信息依赖于摄像头位置的话,会非常不方便,因此我们考虑将三维坐标系转换到以鼻尖为原点的坐标系,随后应用欧拉角对每个三维点进行旋正处理。可见下方视频,目标人物面部朝左、右、上、下时,右侧可视化窗口内基本可以得到正面人脸。

视频链接

当前浏览器不支持博客完整功能。如果要获得最佳体验,请点击右上角···,复制链接,并在其它浏览器打开