65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Windows Phone 8 中进行趣味人脸检测

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2014 年 10 月 22 日

Ms-PL

5分钟阅读

viewsIcon

17407

downloadIcon

441

这是一个有趣且简短的教程,将使用 FaceDetectionWP8 库在 Windows Phone 上尝试面部检测。

开始

如果有人正在寻找 Windows Phone 的面部检测库但还没有尝试过这个,那么这个教程就是为他们准备的。这是一个很棒的库,是 Julia Schwarz 的 facedetecionwp7 库的衍生品。你可以用它来做很多很酷的事情,而且非常省事。

让我们玩得开心一点

让我们开始创建一个 Windows Phone 8 项目。我们的任务是尝试使用这个库,在图片中检测人脸,并从中获得一些乐趣,例如用经典的 troll face 和笑声覆盖人脸。:P

现在,无论这听起来如何,让我们尝试一下。我为此设计了一个非常简单的 GUI。如下所示:

如果你想仔细看看 XAML,它看起来会是这样的:

<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

       
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="Face Detection in Windows Phone" 
            Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
            
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Button x:Name="CaptureImageButton" Content="Capture Image" 
            HorizontalAlignment="Center" Margin="0" VerticalAlignment="Top" 
            Width="267" Click="CaptureImageButton_Click"/>
            <Image Name="facesPic" HorizontalAlignment="Center" 
            Height="388" Margin="0,170,0,0" 
            VerticalAlignment="Top" Width="427"/>
        </Grid>
        
    </Grid>

现在,你可以清楚地看到这是一个非常基础的用户界面。我们有一个带有点击事件 `CaptureImageButton_Click` 的 `Button` 和一个名为 `facesPic` 的 `Image` 控件。

现在,在我们开始之前,让我们先添加一个我们需要的库的引用。我们需要来自这里的 WritableBitmapEx。这个库已经在 Nuget 上可用了,所以你也可以从那里获取。

在将它添加到项目引用后,您将需要来自FaceDetectionWP8的一些库才能使其正常工作。你也可以从以下链接获取那里的类。

下载 .zip 文件后,解压缩它并将所有类包含在你的项目中。我将它们添加到了一个名为 *FaceDetector* 的单独文件夹下。

让我们切换到我们的 *MainPage.xaml.cs*。

现在,首先要做的是选择一张图片,为此最好的方法是使用 `PhotoChooserTask`。让我们在 `CaptureImageButton_Click` 事件中添加以下代码片段:

private void CaptureImageButton_Click(object sender, RoutedEventArgs e)
{
    PhotoChooserTask photo = new PhotoChooserTask();
    photo.Completed += new EventHandler<PhotoResult>(photoChooserTask_Completed);
    photo.ShowCamera = true;
    photo.Show();
}

接下来需要编写的是 `photoChooserTask_Completed` 事件处理程序。让我们开始编写它。首先要做的是将选择的照片加载到一个 `WritableBitmap` 中,因为我们要对其进行一些更改。

if(e.TaskResult==TaskResult.OK)
{
    BitmapImage SourceBitmap = new BitmapImage();
    SourceBitmap.SetSource(e.ChosenPhoto);
    WriteableBitmap SourceWritableBitmap = new WriteableBitmap(SourceBitmap);
}

现在,我们需要做的下一件事是稍微缩小图像,因为大多数 Windows Phone 设备都配备了功能强大的摄像头,图片经常被过度采样。我个人建议你也缩小图像。我没有在这个演示中这样做,但如果你想要更快的性能,你确实应该尝试一下。我在 `if` 块内添加了以下代码段。

byte[] downsampledImage = new byte[SourceWritableBitmap.PixelWidth / 
_downsampleFactor * SourceWritableBitmap.PixelHeight / _downsampleFactor];

Utils.DownSample(SourceWritableBitmap.ToByteArray(), SourceWritableBitmap.PixelWidth, SourceWritableBitmap.PixelHeight, ref downsampledImage, _downsampleFactor);

SourceWritableBitmap = SourceWritableBitmap.FromByteArray(downsampledImage);

这里的代码段现在非常直接。这里的 `Utils` 类来自 `FaceDetectionWP8` 库。这里使用的 `Utils.DownSample` 的第一个参数是 `SourceWritableBitmap` 的字节数组,它使用了我们之前添加的 `WritableBitmapEx`。第二个参数是源位图的宽度,第三个参数是高度。传递的最后一个参数是下采样因子。它在 `MainPage` 类中定义如下:

int _downsampleFactor = 2;

然后 `SourceWritableBitmap` 会被它自己的下采样版本重新初始化。

接下来要做的是获取检测到的人脸,当然了。:D 那么,还在等什么?我为此添加了以下代码片段:

List<FaceDetector.Rectangle> faces = new List<FaceDetector.Rectangle>();
faces = _detector.getFaces(SourceWritableBitmap, 2f, 1.25f, 0.1f, 1, false, true);

我在这里使用了默认值,除了最后一个参数,它决定是否应检测多张人脸,我将其保留为 `true`。如果你想调整你的 Haar 级联检测参数,你可以随意尝试其他参数。

现在是乐趣所在,我们将用 troll 哈哈图片替换图片中的人脸/人脸。:P 我们可以看到它返回人脸为一个 `List`。我们现在要做的就是在方法返回的矩形中绘制 Troll 哈哈图片。有趣吧?

现在,在用 Troll 哈哈替换源图像之前,我们需要将其加载到另一个 `WritableBitmap` 中,以便我们可以将它们混合在一起。

StreamResourceInfo MaskImageSri = Application.GetResourceStream
(new Uri("Images/Troll.png", UriKind.Relative));
BitmapImage MaskImageBitmap = new BitmapImage();
MaskImageBitmap.SetSource(MaskImageSri.Stream);
                    
WriteableBitmap MaskWritableBitmap = new WriteableBitmap(MaskImageBitmap);

既然我们有了 *Troll.png*,为什么不把它粘贴到源位图上呢?

foreach (var r in faces)
{
   int x = Convert.ToInt32(r.X);
   int y = Convert.ToInt32(r.Y);
   int width = Convert.ToInt32(r.Width);
   int height = Convert.ToInt32(r.Height);                    

   System.Windows.Rect destRect = new Rect(x, y, width, height);
   System.Windows.Rect srcRect = new Rect(0, 0, MaskWritableBitmap.PixelWidth, MaskWritableBitmap.PixelHeight);
                 
   SourceWritableBitmap.Blit(destRect, MaskWritableBitmap, srcRect);
                        
}
   SourceWritableBitmap.Invalidate();
   facesPic.Source = SourceWritableBitmap;

现在,这也很直接。我们所需要的就是遍历人脸,并创建两个单独的矩形 `struct`。一个是来自 `MaskWritableBitmap`,所以我们可以得到我们要粘贴的图像部分的矩形,在这种情况下是完整的图像,而目标矩形是我们要将这个 troll 哈哈粘贴到其上的源图像中的人脸矩形。

接下来要做的是调用 `Blit()` 方法,因为它负责混合这两个 `WritableBitmaps`。它来自我们之前添加的 `WritableBitmapEx` 库。第一个参数是目标矩形,源矩形将被粘贴到那里。在这种情况下,是将被 troll 哈哈图像替换的人脸矩形。第二个参数将是包含要粘贴到源中的矩形的 `WritableBitmap`。这里是 Troll Image,最后一个是 Troll Image 中将被传输到主位图的矩形。

我们在 `SourceWritableBitmap` 上调用它,因为我们想将 troll 哈哈粘贴到上面。在一切完成之后,我们将 `facesPic` 的源设置为 `SourceWritableBitmap`。

现在,我们要做的就是构建和测试应用程序。结果如下:

那么,你还在等什么?测试文章上方的演示项目,玩得开心!

© . All rights reserved.