同系列文章
源代码下载
源代码
MainWindow.xaml.cs
/* ---------------------------------------------------------- * 文件名称:MainWindow.xaml.cs * * 作者:秦建辉 * * 微信:splashcn * * 博客:http://www.firstsolver.com/wordpress/ * * 开发环境: * Visual Studio V2017 * .NET Framework 4 Client Profile * Baidu.AI 3.3.0.29135 * * 版本历史: * V1.0 2018年01月18日 * 百度AI 人脸比对演示示例 ------------------------------------------------------------ */ using AForge.Video.DirectShow; using Baidu.Aip.Face; using Com.FirstSolver.CR; using Com.FirstSolver.Splash; using System; using System.Collections.Generic; using System.Threading; using System.Windows; using System.Windows.Media.Imaging; namespace FaceMatch { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { /// <summary> /// 核验通过 /// </summary> private BitmapSource ImagePass; /// <summary> /// 核验失败 /// </summary> private BitmapSource ImageDeny; private Face Client; private Dictionary<string, object> options = new Dictionary<string, object> { { "image_liveness", ",faceliveness"}, // 第一张图为证件照,不做活体检测;第二张图为视频照片,做活体检测 { "types", "13,7"} }; private byte[][] ImageCollection = new byte[2][]; // 锯齿数组 /// <summary> /// 身份证读卡器 /// </summary> private IDCardReader CardReader = null; /// <summary> /// 身份证读卡器同步事件 /// </summary> private ManualResetEvent CardReaderSyncEvent = new ManualResetEvent(true); private volatile bool ShouldStop = true; private ManualResetEvent StartFaceMatchEvent = new ManualResetEvent(false); /// <summary> /// 人证比对通过阈值 /// </summary> private double MatchThreshold = 70.0; /// <summary> /// 单帧活体阈值 /// </summary> private double LivenessThreshold = 0.393241; public MainWindow() { InitializeComponent(); // 核验通过图片 ImagePass = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( Properties.Resources.pass.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); // 核验拒绝图片 ImageDeny = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( Properties.Resources.deny.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } private void Window_Loaded(object sender, RoutedEventArgs e) { InitializeMatch(); // 初始化摄像头 InitializeCamera(); // 初始化读卡设备 InitializeCardReader(); } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { CloseMatch(); CloseCamera(); // 关闭摄像头 CloseCardReader(); // 关闭读卡设备 } private void InitializeMatch() { string API_KEY = "你的 Api Key"; string SECRET_KEY = "你的 Secret Key"; Client = new Face(API_KEY, SECRET_KEY); ThreadPool.QueueUserWorkItem(FaceMacthHandler); } private void CloseMatch() { ShouldStop = true; StartFaceMatchEvent.Set(); } // 初始化摄像头 private void InitializeCamera() { CloseCamera(); // 关闭摄像头 // 设定初始视频设备 FilterInfoCollection VideoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); if (VideoDevices.Count > 0) { VSP.VideoSource = new VideoCaptureDevice(VideoDevices[0].MonikerString); VSP.Start(); } else { MessageBoxPlus.Show(this, "未发现摄像头!", "摄像头错误", MessageBoxButton.OK, MessageBoxImage.Error); } } // 关闭摄像头 private void CloseCamera() { if (VSP.VideoSource != null && VSP.IsRunning) { // 停止视频 VSP.SignalToStop(); VSP.WaitForStop(); } } // 初始化读卡器 private void InitializeCardReader() { CloseCardReader(); // 关闭读卡器 try { CardReader = new YAReader() { ExternalSyncEvent = CardReaderSyncEvent }; CardReader.OnReadCardCompleted += ReadCardCompletedHandler; // 事件处理器 if (CardReader.Start(IDCardBiometrics.Photo) == -1) { MessageBoxPlus.Show(this.Owner, "开启身份证扫描服务失败!", "读卡器错误", MessageBoxButton.OK, MessageBoxImage.Error); } } catch (Exception exception) { MessageBoxPlus.Show(this, exception.Message, "读卡器错误", MessageBoxButton.OK, MessageBoxImage.Error); return; } } // 停止身份证扫描服务 private void CloseCardReader() { if (CardReader != null) { CardReader.Close(); CardReader = null; } } // 刷身份证处理器 private void ReadCardCompletedHandler(object sender, ReadCardCompletedEventArgs e) { if (e == null || e.Photo == null) return; this.Dispatcher.BeginInvoke(new Action(() => { // 清除结果 ImageVideo.Source = null; ImageAnswer.Source = null; TextBlockScore.Text = string.Empty; // 显示身份证信息 TextBlockName.Text = e.Name; // 姓名 TextBlockGender.Text = e.Gender; // 性别 TextBlockNationality.Text = e.EthnicName; // 民族 TextBlockBirth.Text = e.Birth; // 出生 TextBlockSN.Text = e.IDC; // 身份证号码 TextBlockAddress.Text = e.Address; // 地址 ImageCollection[0] = e.Photo; this.Dispatcher.Invoke(new Action(() => { ImagePhoto.Source = e.Photo.ToBitmapImage(); })); ShouldStop = false; StartFaceMatchEvent.Set(); })); } private void FaceMacthHandler(object state) { while (true) { StartFaceMatchEvent.WaitOne(); if (ShouldStop) break; using (System.Drawing.Bitmap bmp = VSP.GetCurrentVideoFrame()) { ImageCollection[1] = bmp.ToByteArray(); var result = Client.Match(ImageCollection, options); var error_code = result["error_code"]; if (error_code != null) { MessageBoxPlus.Show(this, result["error_msg"].ToString(), "错误", MessageBoxButton.OK, MessageBoxImage.Warning); StartFaceMatchEvent.Reset(); } else if ((int)result["result_num"] == 1) { // 验证活体 if (double.Parse(result["ext_info"]["faceliveness"].ToString().Split(new char[] { ',' })[1]) > LivenessThreshold) { foreach (dynamic item in result["result"]) { this.Dispatcher.Invoke(new Action(() => { ImageVideo.Source = bmp.ToBitmapImage(); double Score = (double)item["score"]; ImageAnswer.Source = (Score >= MatchThreshold) ? ImagePass : ImageDeny; TextBlockScore.Text = Score.ToString(); })); CardReaderSyncEvent.Set(); // 开启新的读卡信号 StartFaceMatchEvent.Reset(); // 等待新的人脸匹配信号 } } } } } } } }
MainWindow.xaml
<Window x:Class="FaceMatch.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:AForge.Controls;assembly=AForge.Controls" Title="百度AI 人脸比对演示示例" WindowStartupLocation="CenterScreen" FontSize="16" Icon="FR.ico" SizeToContent="WidthAndHeight" Loaded="Window_Loaded" Closing="Window_Closing"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="946"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <WindowsFormsHost Grid.Column="0" Margin="4" Width="730" Height="534"> <controls:VideoSourcePlayer x:Name="VSP"/> </WindowsFormsHost> <Grid Grid.Column="1"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <GroupBox Grid.Row="0" Margin="4"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Border Grid.Row="0" Grid.Column="0" BorderThickness="1" Margin="4" BorderBrush="LightGray" Width="142" Height="166"> <Image Name="ImageVideo" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <Border Grid.Row="0" Grid.Column="1" BorderThickness="1" Margin="4" BorderBrush="LightGray" Width="142" Height="166"> <Image Name="ImagePhoto" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <Label Content="实时照" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Foreground="#666666"/> <Label Content="证件照" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Foreground="#666666"/> </Grid> </GroupBox> <GroupBox Grid.Row="1" Margin="4"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Image Name="ImageAnswer" Grid.Row="0" Width="128" Height="128"/> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Grid.Column="0" Content="相似度:" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" FontSize="22" Foreground="#333333"/> <TextBlock Name="TextBlockScore" Grid.Column="1" VerticalAlignment="Center" FontSize="22" Foreground="#333333"/> </Grid> </Grid> </GroupBox> <GroupBox Grid.Row="2" Margin="4"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Grid.Row="0" Grid.Column="0" Content="姓名" Foreground="#555555"/> <Label Grid.Row="0" Grid.Column="2" Content="性别" Foreground="#555555"/> <Label Grid.Row="1" Grid.Column="0" Content="民族" Foreground="#555555"/> <Label Grid.Row="1" Grid.Column="2" Content="出生" Foreground="#555555"/> <Label Grid.Row="2" Grid.Column="0" Content="证件号" Foreground="#555555"/> <Label Grid.Row="3" Grid.Column="0" Content="地址" Foreground="#555555"/> <TextBlock Name="TextBlockName" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="#777777"/> <TextBlock Name="TextBlockGender" Grid.Row="0" Grid.Column="3" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="#777777"/> <TextBlock Name="TextBlockNationality" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="#777777"/> <TextBlock Name="TextBlockBirth" Grid.Row="1" Grid.Column="3" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="#777777"/> <TextBlock Name="TextBlockSN" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="#777777"/> <TextBlock Name="TextBlockAddress" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="#777777"/> </Grid> </GroupBox> </Grid> </Grid> </Window>