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);
}
}));
}));
}
}
}
运行结果