WPF:基于Dlib的人脸关键点检测演示示例

MainWindow.xaml

<Window x:Class="Splash.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="人脸关键点检测" WindowStartupLocation="CenterScreen" WindowState="Maximized" Closing="Window_Closing" Loaded="Window_Loaded" Icon="FR.ico">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Margin="4" Content="选择检测器"/>
        <TextBox Grid.Row="0" Grid.Column="1" Margin="4" IsReadOnly="True" VerticalContentAlignment="Center" Name="TextBoxPredictorFileName"/>
        <Button Grid.Row="0" Grid.Column="2" Margin="4" Padding="16,4" Content="…" Name="ButtonSelectPredictor" Click="ButtonSelectPredictor_Click"/>

        <Label Grid.Row="1" Grid.Column="0" Margin="4" Content="选择源图像"/>
        <TextBox Grid.Row="1" Grid.Column="1" Margin="4" IsReadOnly="True" VerticalContentAlignment="Center" Name="TextBoxImageFileName"/>
        <Button Grid.Row="1" Grid.Column="2" Margin="4" Padding="16,4" Content="…" Name="ButtonSelectImage" Click="ButtonSelectImage_Click"/>

        <Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Margin="4" Padding="16,4" HorizontalAlignment="Center" Content="检测" Name="ButtonDetect" Click="ButtonDetect_Click"/>

        <TabControl Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" Margin="4" Name="TabControl1">
            <TabItem Width="240" Header="源图像">
                <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
                    <Image Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top" Name="ImageRaw"/>
                </ScrollViewer>
            </TabItem>

            <TabItem Width="240" Header="关键点图像">
                <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
                    <Image Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top" Name="ImageFaceLandmark"/>
                </ScrollViewer>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

MainWindow.xaml.cs

/* ----------------------------------------------------------
* 文件名称:MainWindow.xaml.cs
*
* 作者:秦建辉
*
* 微信:splashcn
*
* 博客:http://www.firstsolver.com/wordpress/
*
* 开发环境:
*      Visual Studio V2017
*      .NET Framework 4.7.2
*      dlib 19.16
*
* 版本历史:
*		V1.0    2019年01月21日
*				Dlib人脸关键点检测演示示例
* 
* 相关网站:
*       http://dlib.net/
*       https://github.com/takuya-takeuchi/DlibDotNet
* ---------------------------------------------------------- */
using Com.FirstSolver.Splash;
using System;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Splash
{
    public partial class MainWindow : Window
    {
        /// <summary>
        /// 检测器
        /// </summary>
        private DlibDotNet.ShapePredictor Predictor = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {

        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (Predictor != null) { Predictor.Dispose(); Predictor = null; }
        }

        // 选择检测器
        private void ButtonSelectPredictor_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog
                {
                    Filter = "Dat|*.dat",
                    DereferenceLinks = true
                };

                this.CenterChild();
                if (dlg.ShowDialog(Owner).Value == true)
                {   // 析构原先的检测器
                    if (Predictor != null) { Predictor.Dispose(); Predictor = null; }

                    // 构建新的检测器
                    Predictor = DlibDotNet.ShapePredictor.Deserialize(dlg.FileName);
                    TextBoxPredictorFileName.Text = dlg.FileName;
                }
            }
            catch (System.Exception exception)
            {
                TextBoxPredictorFileName.Text = string.Empty;
                MessageBoxPlus.Show(this, exception.Message, "检测器构建异常", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // 选择图像文件
        private void ButtonSelectImage_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog
                {
                    Filter = "Image|*.jpg;*.bmp;*.png;*.tif;*.tga;*.ras;*.jp2;*.j2k;*.jpe",
                    DereferenceLinks = true
                };

                this.CenterChild();
                if (dlg.ShowDialog(Owner).Value == true)
                {
                    ImageRaw.Source = new BitmapImage(new Uri(dlg.FileName, UriKind.Absolute));
                    TextBoxImageFileName.Text = dlg.FileName;
                    TabControl1.SelectedIndex = 0;
                }
            }
            catch (System.Exception exception)
            {
                TextBoxImageFileName.Text = string.Empty;
                MessageBoxPlus.Show(this, exception.Message, "人脸图像文件异常", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void ButtonDetect_Click(object sender, RoutedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback((state) => {
                this.Dispatcher.BeginInvoke(new Action(() => {
                    try
                    {
                        if (ImageRaw.Source is BitmapImage bi)
                        {   // 生成灰度数组
                            byte[] Gray = bi.ToGray();
                            using (var img = DlibDotNet.Dlib.LoadImageData<byte>(Gray, (uint)bi.PixelHeight, (uint)bi.PixelWidth, (uint)bi.PixelWidth))
                            {   // 对源图像进行上采样,以便能够检测40*40像素大小的人脸
                                DlibDotNet.Dlib.PyramidUp(img);

                                DrawingVisual drawingVisual = new DrawingVisual();
                                using (DrawingContext drawingContext = drawingVisual.RenderOpen())
                                {   // 绘制源图像(图像放大到新尺寸)
                                    drawingContext.DrawImage(bi, new Rect(new Size(img.Columns, img.Rows)));

                                    // 检测人脸框
                                    using (var detector = DlibDotNet.Dlib.GetFrontalFaceDetector())
                                    {
                                        DlibDotNet.Rectangle[] faces = detector.Operator(img);
                                        foreach (var face in faces)
                                        {   // 绘制人脸框
                                            drawingContext.DrawRectangle(null, new Pen(Brushes.Red, 3.0f), new Rect(face.TopLeft.X, face.TopLeft.Y, face.Width, face.Height));

                                            // 提取人脸关键点
                                            using (var shape = Predictor.Detect(img, face))
                                            {
                                                for (int i = 0; i < shape.Parts; i++)
                                                {
                                                    DlibDotNet.Point dot = shape.GetPart((uint)i);

                                                    // 绘制人脸关键点
                                                    drawingContext.DrawEllipse(Brushes.Green, null, new System.Windows.Point(dot.X, dot.Y), 3, 3);
                                                }
                                            }
                                        }
                                    }
                                }

                                // 转换到位图,注意此处dpiX和dpiY必须设置为96
                                RenderTargetBitmap rtb = new RenderTargetBitmap(img.Columns, img.Rows, 96, 96, PixelFormats.Default);
                                rtb.Render(drawingVisual);

                                // 恢复到源图像大小,注意此处dpiX和dpiY务必与源图像一致
                                using (DrawingContext drawingContext = drawingVisual.RenderOpen())
                                {
                                    drawingContext.DrawImage(rtb, new Rect(new Size(bi.Width, bi.Height)));
                                }
                                RenderTargetBitmap bmp = new RenderTargetBitmap(bi.PixelWidth, bi.PixelHeight, bi.DpiX, bi.DpiY, PixelFormats.Default);
                                bmp.Render(drawingVisual);

                                // 呈现人脸框+人脸关键点的新图像
                                ImageFaceLandmark.Source = bmp;
                                TabControl1.SelectedIndex = 1;
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        MessageBoxPlus.Show(this, exception.Message, "异常", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }));
            }));
        }
    }
}

运行结果

WPF:基于Dlib的人脸关键点检测演示示例