Kinect 开发 —— 面部识别

EmguCV库也能用来进行面部识别(face identify)。实际的面部识别,就是将一张图像上的人物的脸部识别出来,这是个很复杂的过程,具体过程我们这里不讨论。对一幅影像进行处理来找到包含脸部的那一部分是我们进行面部识别的第一个步骤。

    大多数面部识别软件或多或少都是基于类哈尔特征(Haar-like feature)来进行识别的,他是哈尔小波(Haar wavelets)的一个应用,通过一些列的数学方法来定义一个矩形形状。2001年,Paul Viola和Michael Jones发表了Viola-Jones物体识别方法的框架,该框架基于识别Haar-like特征进行的。该方法和其他面部识别算法相比,所需要的运算量较小。所以这部分方法整合进了OpenCV库。

    OpenCV和EmguCV中的面部识别是建立在一系列定义好了的识别规则基础之上的,规则以XML文件的形式存储,最初格式是由Rainer Lienhart定义的。在Emgu示例代码中该文件名为haarcascade_frontalface_default.xml。当然还有一系列的可以识别人物眼睛的规则在这些示例文件中,本例子中没有用到。

要使用Kinect SDK来构造一个简单的面部识别程序,首先创建一个名为KinectFaceFinder的WPF应用程序,然后引用Microsoft.Kinect, System.Drawing, Emgu.CV, Emgu.CV.UI, 和Emgu.Util. 并将所有以opencv_*开头的dll拷贝到程序编译的目录下面。最后将之前写好的两个扩展方法类库ImageExtension.cs和EmguImageExtensions.cs拷贝到项目中。项目的前端代码和之前的一样。只是在root根节点下面添加了一个名为rgbImage的Image控件。

<Window x:Class="FaceFinder.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 >
        <Image  HorizontalAlignment="Stretch"  Name="rgbImage"  VerticalAlignment="Stretch"  />
    </Grid>
</Window>

后台代码中,首先在MainWindow的构造函数中实例化KinectSensor对象然后配置彩色影像数据。因为我们使用EmguCV库来进行影像处理,所以我们可以直接使用RGB影像而不是深度影像。如下代码所示,代码中使用了BackgroundWork对象来从彩色影像数据流中拉取数据。每一次处理完了之后,拉取下一幅,然后继续处理。

KinectSensor _kinectSensor;
public MainWindow()
{
    InitializeComponent();

    this.Unloaded += delegate
    {
        _kinectSensor.ColorStream.Disable();
    };

    this.Loaded += delegate
    {
        _kinectSensor = KinectSensor.KinectSensors[0];
        _kinectSensor.ColorStream.Enable();
        _kinectSensor.Start();

        BackgroundWorker bw = new BackgroundWorker();
        bw.RunWorkerCompleted += (a, b) => bw.RunWorkerAsync();
        bw.DoWork += delegate { Pulse(); };
        bw.RunWorkerAsync();
    };
}

上面代码中,Pluse方法处理BackgroundWork的DoWork事件,这个方法是这个例子的主要方法。下面的代码简单的对Emgu提供的示例代码进行了一点修改。我们基于提供的脸部识别规则文件实例化了一个新的HaarCascade类。然后我们从彩色影像数据流获取了一幅影像,然后将他转换为了Emgu能够处理的格式。然后对图像进行灰度拉伸然后提高对比度来使得脸部识别更加容易。Haar识别准则应用到图像上去来产生一些列的结构来指示哪个地方是识别出来的脸部。处理完的影像然后转换为BitmapSource类型,最后后复制给Image控件。因为WPF线程的工作方式,我们使用Dispatcher对象来在正确的线程中给Image控件赋值。

String faceFileName = "haarcascade_frontalface_default.xml";
public void Pulse()
{
    using (HaarCascade face = new HaarCascade(faceFileName))
    {
        var frame = _kinectSensor.ColorStream.OpenNextFrame(100);
        var image = frame.ToOpenCVImage<Rgb, Byte>();
        using (Image<Gray, Byte> gray = image.Convert<Gray, Byte>()) //Convert it to Grayscale
        {
            //normalizes brightness and increases contrast of the image
            gray._EqualizeHist();

            //Detect the faces  from the gray scale image and store the locations as rectangle
            //The first dimensional is the channel
            //The second dimension is the index of the rectangle in the specific channel
            MCvAvgComp[] facesDetected = face.Detect(
                gray,
                1.1,
                10,
                Emgu.CV.CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
                new System.Drawing.Size(20, 20));

            Image<Rgb, Byte> laughingMan = new Image<Rgb, byte>("laughing_man.jpg");
            foreach (MCvAvgComp f in facesDetected)
            {
                image.Draw(f.rect, new Rgb(System.Drawing.Color.Blue), 2);
            }
            Dispatcher.BeginInvoke(new Action(() => { rgbImage.Source = image.ToBitmapSource(); }));
        }
    }
}

既然facesDetected包含了识别出来的脸部的位置信息,我们能够使用脸部识别算法来建立一个现实增强应用。我们可以将一个图片放到脸部位置,而不是用矩形框来显示。下面的代码显示了我们如何使用一个图片替代蓝色的矩形框

Image<Rgb, Byte> laughingMan = new Image<Rgb, byte>("laughing_man.jpg");
foreach (MCvAvgComp f in facesDetected)
{

    //image.Draw(f.rect, new Rgb(System.Drawing.Color.Blue), 2);
    var rect = new System.Drawing.Rectangle(f.rect.X - f.rect.Width / 2
        , f.rect.Y - f.rect.Height / 2
        , f.rect.Width * 2
        , f.rect.Height * 2);

    var newImage = laughingMan.Resize(rect.Width, rect.Height, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR);

    for (int i = 0; i < (rect.Height); i++)
    {
        for (int j = 0; j < (rect.Width); j++)
        {
            if (newImage[i, j].Blue != 0 && newImage[i, j].Red != 0 && newImage[i, j].Green != 0)
                image[i + rect.Y, j + rect.X] = newImage[i, j];
        }

    }
}
原文地址:https://www.cnblogs.com/sprint1989/p/3860490.html