SDK1.5中新增了人脸识别类库:Microsoft.Kinect.Toolkit.FaceTracking使得在Kinect中进行人脸识别变得简单,该类库的源代码也在Developer Toolkit中。在Developer Toolkit中也自带人脸识别的例子,您也可以打开运行或者查看源代码。
开发前准备
要使用面部追踪功能,Kinect SDK版本应该至少是1.5,最新版本为1.6,您可以参考之前的那篇文章下载安装,Kinect SDK 和 Kinect Developer Toolkit要一起安装,我的机器上装的是最新的SDK1.6版本。
安装Kinect Developer Toolkit会安装Kinect Studio、一些C#/VB.Net/C++的应用程序示例、源码以及两个用于面部追踪的类库FaceTrackData.dll ,FaceTrackLib.dll包括32位和64位版本,安装好了之后,这些dll应该都在该目录下面 :
如果用C++开发的话,可以直接在项目中使用着两个dll,否则,如果使用.NET开发的话,还需要将这些dll包装成托管代码。
幸运的是, Developer Toolkit中提供了两个使用.NET来实现面部追踪的代码,我们可以直接使用其替我们包装好了dll,该类库的源码也可以看到。 打开Developer Toolkit Browser 找到Component示例,然后安装,之后浏览所在目录,应该能够找到下面两个dll,不安装也可以,您也可以直接到SDK目录中查找这两个dll,在我的及其上目录为C:\Program Files\Microsoft SDKs\Kinect\Developer Toolkit v1.6.0\Samples\bin:
- Microsoft.Kinect.Toolkit
- Microsoft.Kinect.Toolkit.FaceTracking
这两个dll的源码可以在示例中找到,引用了这两个dll之后,还需要将FaceTrackData.dll和FaceTrackLib.dll拷贝到项目中,并保证他和exe在同一个目录下面。
创建WPF项目
新建一个名为KinectFaceTracking的WPF项目,然后引用Microsoft.Kinect ,Microsoft.Kinect.Toolkit.FaceTracking 和Microsoft.Kinect.Toolkit这三个dll
要使用面部追踪,在开启KinectSensor之后需要启用ColorImageStream, DepthImageStream 和SkeletonStream这三个Stream,部分代码如下:
//启用必要的三个Stream kinectSensor.ColorStream.Enable(); kinectSensor.DepthStream.Enable(DepthImageFormat.Resolution80x60Fps30); kinectSensor.SkeletonStream.TrackingMode = SkeletonTrackingMode.Seated; kinectSensor.SkeletonStream.Enable(new TransformSmoothParameters() { Correction = 0.5f, JitterRadius = 0.05f, MaxDeviationRadius = 0.05f, Prediction = 0.5f, Smoothing = 0.5f }); // 监听流事件. kinectSensor.AllFramesReady +=kinectSensor_AllFramesReady; // 初始化数据 colorPixelData = new byte[kinectSensor.ColorStream.FramePixelDataLength]; depthPixelData = new short[kinectSensor.DepthStream.FramePixelDataLength]; skeletonData = new Skeleton[6]; //打开Kinect kinectSensor.Start(); //初始化人脸识别 faceTracker = new FaceTracker(kinectSensor);
前面的代码应该都好懂,值得一提的是最后一句,初始化FaceTracker 对象。在初始化景深数据流时,我们使用最低分辨率的景深影像数据流,这样fps会更高,使得结果更加流畅. 最后,我们注册了AllFramesReady事件。
获取面部数据
在Developer Toolkit中,脸部数据帧使用FaceTrackFrame表示。 为了获取数据,我们需要调用FaceTracker对象的Track方法并传入我们从Kinect中获取的数据,然后FaceTracker对象会对这些数据进行处理来进行面部追踪,部分代码如下:
// 获取每一帧数据,然后存储到变量中来 using (ColorImageFrame colorImageFrame = e.OpenColorImageFrame()) { if (colorImageFrame == null) return; colorImageFrame.CopyPixelDataTo(colorPixelData); } using (DepthImageFrame depthImageFrame = e.OpenDepthImageFrame()) { if (depthImageFrame == null) return; depthImageFrame.CopyPixelDataTo(depthPixelData); } using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame()) { if (skeletonFrame == null) return; skeletonFrame.CopySkeletonDataTo(skeletonData); } //获取处于跟踪状态的骨骼数据对象,如果没有,则返回. var skeleton = skeletonData.FirstOrDefault(s => s.TrackingState == SkeletonTrackingState.Tracked); if (skeleton == null) return;
前面的都是数据准备阶段,一旦数据都获取了之后,我们使用FaceTracker对象来对这些数据进行识别,提取面部信息。
// 面部追踪 FaceTrackFrame faceFrame = faceTracker.Track(kinectSensor.ColorStream.Format, colorPixelData, kinectSensor.DepthStream.Format, depthPixelData, skeleton);
faceFrame是一个FaceTrackFrame类型的变量,代表面部追踪的处理结果,该对象的更详细信息,您可以查看 MSDN,下面是该对象的OMD图
首先,TrackSuccessful 属性只是是否识别出了面部,FaceRect是面部所在的矩形区域,最重要的是下面的几个方法:这些方法可以提供最多87个点来表示面部信息,这些点可以以二维或者三维的形式提供。我们可以提取一些用户的面部表情信息:
- Get3DShape() 方法返回三维表示的121个点集合.
- GetProjected3DShape() 方法返回同样数目的以二维形式展现的点的集合,这些点映射到640×480像素的图像上.
- GetAnimationUnitCoefficients() 返回面部表情的描述值,在下面我们会用到这个值来绘制人物图像.更多的信息,您可以查看 Animation Units on MSDN.
- GetTriangles() 该方法可以 和Get3DShape 方法一起使用. 他返回一个三角形集合,集合中的每个三角形的顶点都是由Get3DShape 中的点组成。这些三角形可以用来进行3D建模,就像Developer Toolkit自带例子中的那样,我们可以用它来对整个面部进行动态建模。
绘制面部
有了数据之后,我们可以在WPF中用XAML来绘制一个简单的人脸了。在绘制过程中,我们会使用一些变换Transformations (Translations, Rotations, Scales, …) 来表示人的表情的变化,比如说RenderTransform对象可以用来改变对象的渲染方式,我们使用一个Ellipse 对象来代表嘴巴 (Ellipse with x:Name=”Mouth”) :他有一个 ScaleTransform属性,我们可以使用它来改变X轴和Y轴上的缩放,来模拟口型的变化,眉头的模拟也是如此。
<Window x:Class="KinectFaceTracking.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid> <Canvas x:Name="MainCanvas" Width="500" Height="500"> <Canvas.RenderTransform> <TransformGroup> <RotateTransform x:Name="CanvasRotate" CenterX="250" CenterY="250" /> <!--<TranslateTransform x:Name="CanvasTranslate" />--> </TransformGroup> </Canvas.RenderTransform> <Ellipse Width="300" Height="300" x:Name="Face" StrokeThickness="2" Stroke="Black" Canvas.Left="105" Canvas.Top="6" /> <Ellipse Width="30" Height="30" x:Name="LeftEye" Stroke="Black" StrokeThickness="2" Canvas.Left="289" Canvas.Top="102" /> <Ellipse Canvas.Left="194" Canvas.Top="102" x:Name="RightEye" Height="30" Stroke="Black" StrokeThickness="2" Width="30" /> <Ellipse Canvas.Left="224" Canvas.Top="239" Height="18" x:Name="Mouth" Stroke="Black" StrokeThickness="2" Width="64" > <Ellipse.RenderTransform> <ScaleTransform x:Name="MouthScaleTransform" CenterX="32" CenterY="9" ScaleX="1" ScaleY="1"/> </Ellipse.RenderTransform> </Ellipse> <!--<Ellipse Canvas.Left="244" Canvas.Top="151" Height="53" x:Name="Nose" Stroke="Black" StrokeThickness="2" Width="22" />--> <Rectangle Width="70" Stroke="Black" Fill="Black" StrokeThickness="10" Height="5" Canvas.Left="169" Canvas.Top="80"> <Rectangle.RenderTransform> <TransformGroup> <TranslateTransform x:Name="RightBrow" /> <RotateTransform x:Name="RightBrowRotate" CenterX="50" Angle="0" /> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> <Rectangle Width="70" Stroke="Black" Fill="Black" StrokeThickness="10" Height="5" Canvas.Left="274" Canvas.Top="80" > <Rectangle.RenderTransform> <TransformGroup> <TranslateTransform x:Name="LeftBrow" /> <RotateTransform x:Name="LeftBrowRotate" CenterX="20" Angle="0" /> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> <Rectangle Canvas.Left="207" Canvas.Top="148" Fill="Black" Height="5" Stroke="Black" StrokeThickness="10" Width="50"> <Rectangle.RenderTransform> <TransformGroup> <RotateTransform Angle="-70" CenterX="50" /> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> <Rectangle Canvas.Left="246" Canvas.Top="190" Fill="Black" Height="5" Stroke="Black" StrokeThickness="10" Width="15"> <Rectangle.RenderTransform> <TransformGroup> <RotateTransform Angle="0" CenterX="50" /> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> </Canvas> </Grid> </Grid> </Window>
完成之后,一个简单的人脸就绘制完成了,如下:
将数据和表情绑定
现在表情绘制完成,我们只需要使用面部追踪的数据来改变各部分的动画参数就可以模拟脸部表情了。
获取数据之前,我们需要检查是否识别出来了脸部。如果识别出来人脸,我们就接着获取动画单位系数(Animation Units coefficients). 这个参数可以告诉哦我们被追踪对象嘴巴是张开还是合拢,还是在笑,眉头紧锁还是笑颜逐开等等…
//如果识别出来,进行进一步处理 if (faceFrame.TrackSuccessful) { // 获取动画单位系数 Animation Units coeffs. var AUCoeff = faceFrame.GetAnimationUnitCoefficients(); var jawLowerer = AUCoeff[AnimationUnit.JawLower]; jawLowerer = jawLowerer < 0 ? 0 : jawLowerer; MouthScaleTransform.ScaleY = jawLowerer * 5 + 0.1; MouthScaleTransform.ScaleX = (AUCoeff[AnimationUnit.LipStretcher] + 1); LeftBrow.Y = RightBrow.Y = (AUCoeff[AnimationUnit.BrowLower]) * 40; RightBrowRotate.Angle = (AUCoeff[AnimationUnit.BrowRaiser] * 20); LeftBrowRotate.Angle = -RightBrowRotate.Angle; CanvasRotate.Angle = faceFrame.Rotation.Z; }
上面的动画参数的取值范围都是在-1和1之间。到这里一个简单的面部追踪小程序就完成了,您可以对着Kinect做一些表情试一试变化。下面是我做的一些截图:
Kinect 开发 —— 面部追踪