gRPC(三)构建人证比对微服务:客户端

同系列文章:

演示程序界面

源代码
FrClientWindow.xaml

<Window x:Class="Splash.FrClientWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:controls="clr-namespace:AForge.Controls;assembly=AForge.Controls"
        mc:Ignorable="d"
        d:DesignHeight="710" d:DesignWidth="1280"
        Title="汉王人证比对客户端 By 秦建辉" Icon="dog.ico" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" Loaded="Window_Loaded" Closing="Window_Closing">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <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"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <GroupBox Grid.Row="0" Margin="4" Padding="0,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" BorderBrush="LightGray" Width="128" Height="168" Margin="4">
                        <Image Name="imageVideo" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>

                    <Border Grid.Row="0" Grid.Column="1" BorderThickness="1" BorderBrush="LightGray" Width="128" Height="168" Margin="4">
                        <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>

            <GroupBox Grid.Row="3" Margin="4" Header="服务器设置">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>

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

                    <Label Grid.Row="0" Grid.Column="0" Margin="4" Content="服务器地址"/>
                    <Label Grid.Row="1" Grid.Column="0" Margin="4" Content="服务器端口"/>

                    <TextBox Grid.Row="0" Grid.Column="1" VerticalContentAlignment="Center" Margin="4" x:Name="textBoxServerIP" Text="192.168.135.98"/>
                    <TextBox Grid.Row="1" Grid.Column="1" VerticalContentAlignment="Center" Margin="4" x:Name="textBoxServerPort" Text="50052"/>
                </Grid>
            </GroupBox>
        </Grid>
    </Grid>
</Window>

FrServerWindow.xaml.cs

/* ----------------------------------------------------------
 * 文件名称:FrClientWindow.xaml.cs
 * 作者:秦建辉
 * 
 * 微信:splashcn
 * 
 * 博客:http://www.firstsolver.com/wordpress/
 * 
 * 开发环境:
 *      Visual Studio V2017
 *      .NET Framework 4.5.2
 *      
 * 版本历史:
 *      V1.0    2017年10月16日
 *              基于gRPC构建的人证比对客户端实现
------------------------------------------------------------ */
using AForge.Video.DirectShow;
using Com.FirstSolver.CR;
using Com.FirstSolver.FR;
using Com.FirstSolver.Splash;
using Grpc.Core;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;

namespace Splash
{
    public partial class FrClientWindow : Window
    {
        /// <summary>
        /// 人脸框画笔
        /// </summary>
        private System.Drawing.Pen FaceReaderPen = new System.Drawing.Pen(System.Drawing.Color.Green, 1.0f);

        /// <summary>
        /// 身份证读卡器
        /// </summary>
        private IDCardReader CardReader = null;

        /// <summary>
        /// 身份证读卡器同步事件
        /// </summary>
        private ManualResetEvent CardReaderSyncEvent = new ManualResetEvent(true);

        /// <summary>
        /// 捕获图像事件
        /// </summary>
        private AutoResetEvent CaptureArrivedEvent = new AutoResetEvent(false);

        /// <summary>
        /// 关闭人证比对线程
        /// </summary>
        private volatile bool ShouldStop = false;

        /// <summary>
        /// 人证比对通过阈值
        /// </summary>
        private float Threshold = 0.7f;

        /// <summary>
        /// 核验通过
        /// </summary>
        internal BitmapSource ImagePass;

        /// <summary>
        /// 核验失败
        /// </summary>
        internal BitmapSource ImageDeny;

        public FrClientWindow()
        {
            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)
        {
            InitializeCamera(); // 初始化摄像头
            InitializeCardReader(); // 初始化读卡设备
            InitializeFaceIdThread(); // 初始化人证比对线程
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            CloseFaceIdThread(); // 关闭人证比对线程
            CloseCardReader(); // 关闭读卡设备
            CloseCamera(); // 关闭摄像头

            FaceReaderPen.Dispose();
        }

        // 初始化人证比对线程
        private void InitializeFaceIdThread()
        {
            if (!ThreadPool.QueueUserWorkItem(FaceIdThreadHandler))
            {
                MessageBoxPlus.Show(this, "创建人证比对线程失败,程序退出…", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                this.Close();
            }
        }

        // 关闭人证比对线程
        private void CloseFaceIdThread()
        {
            ShouldStop = true;
            CaptureArrivedEvent.Set();
        }

        // 人证比对线程处理器
        private void FaceIdThreadHandler(Object state)
        {
            while (true)
            {
                CaptureArrivedEvent.WaitOne();
                if (ShouldStop) break;

                this.Dispatcher.Invoke(new Action(async () =>
                {
                    Channel channel = null;
                    try
                    {   // 获取基准图像
                        using (Bitmap Photo = (imagePhoto.Source as BitmapImage).ToBitmap())
                        {
                            channel = new Channel(textBoxServerIP.Text, Convert.ToInt32(textBoxServerPort.Text), ChannelCredentials.Insecure);
                            var client = new FrService.FrServiceClient(channel);
                            using (var call = client.Compare())
                            {
                                FaceIdRequestMsg request = new FaceIdRequestMsg();

                                // 传送基准图像
                                request.Index = 1;
                                request.Photo = Google.Protobuf.ByteString.CopyFrom(Photo.ToBytes());
                                await call.RequestStream.WriteAsync(request);

                                await call.ResponseStream.MoveNext();
                                var responseBase = call.ResponseStream.Current;
                                if (responseBase.Status == 1)
                                {   // 提取人脸特征成功
                                    using (Graphics g = Graphics.FromImage(Photo))
                                    {   // 用矩形框标记人脸位置
                                        FrFaceRect Face = responseBase.Face;
                                        g.DrawRectangle(FaceReaderPen, Face.Left, Face.Top, Face.Right - Face.Left + 1, Face.Bottom - Face.Top + 1);
                                    }

                                    imagePhoto.Source = Photo.ToBitmapImage();

                                    // 传送视频图像
                                    while (true)
                                    {
                                        if (ShouldStop) break;

                                        // 抓取视频图像
                                        request.Index++;
                                        using (Bitmap bmp = vsp.GetCurrentVideoFrame())
                                        {
                                            request.Photo = Google.Protobuf.ByteString.CopyFrom(bmp.ToBytes());
                                            await call.RequestStream.WriteAsync(request);

                                            await call.ResponseStream.MoveNext();
                                            var response = call.ResponseStream.Current;
                                            if (response.Status == 1)
                                            {
                                                using (Graphics g = Graphics.FromImage(bmp))
                                                {   // 用矩形框标记人脸位置
                                                    FrFaceRect Face = response.Face;
                                                    g.DrawRectangle(FaceReaderPen, Face.Left, Face.Top, Face.Right - Face.Left + 1, Face.Bottom - Face.Top + 1);
                                                }

                                                imageVideo.Source = bmp.ToBitmapImage();
                                                if (response.Score >= Threshold)
                                                {
                                                    imageAnswer.Source = this.ImagePass; // 通过
                                                }
                                                else
                                                {
                                                    imageAnswer.Source = this.ImageDeny; // 拒绝
                                                }
                                                textBlockScore.Text = response.Score.ToString();
                                                break;
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    MessageBoxPlus.Show(this, "不合格的基准图像!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                                }
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        string ErrorMessage = exception.Message;
                        if (string.IsNullOrEmpty(ErrorMessage)) ErrorMessage = exception.InnerException.Message;
                        MessageBoxPlus.Show(this, ErrorMessage, "比对异常", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                    finally
                    {
                        if (channel != null) channel.ShutdownAsync().Wait();
                        CardReaderSyncEvent.Set(); // 进入读卡状态
                    }
                }));

                if (ShouldStop) break;
            }
        }

        // 初始化摄像头
        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, "开启身份证扫描服务失败!", "读卡器错误", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            catch (Exception exception)
            {
                string ErrorMessage = exception.Message;
                if (string.IsNullOrEmpty(ErrorMessage)) ErrorMessage = exception.InnerException.Message;
                MessageBoxPlus.Show(this, ErrorMessage, "读卡器错误", 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(() =>
            {   // 清除结果
                imagePhoto.Source = null;
                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;  // 地址

                // 显示身份证照片
                imagePhoto.Source = e.Photo.ToBitmapImage();
                CaptureArrivedEvent.Set();
            }));
        }
    }
}

Comments are closed.