• 一次人脸识别ViewFaceCore使用的经验分享,看我把门店淘汰下来的POS机改成了人脸考勤机


    POS软件是什么?你好意思吗,还在用老掉牙的Winform。

     

     

     

    门店被淘汰的POS机

    销售终端——POS(point of sale)是一种多功能终端,把它安装在信用卡的特约商户和受理网点中与计算机联成网络,就能实现电子资金自动转账,它具有支持消费、预授权、余额查询和转账等功能,使用起来安全、快捷、可靠。

     

     

     

    前言

     万事俱备只欠东风------一个USB摄像头和一个经过改造的人脸识别程序。

     

     下载地址:

    GitHub - ViewFaceCore/ViewFaceCore: C# 超简单的离线人脸识别库。( 基于 SeetaFace6 )

    开始干活,动手改造。

    1. 程序要支持无人值守,程序启动时自动打开摄像头。超过设定的时间无移动鼠标和敲击键盘,程序自动关闭摄像头,进入“休眠”
    2. 识别人脸成功后记录当前时间作为考勤记录
    3. 人脸信息放在服务器端,桌面程序和服务器端同步人脸信息
    4. 关于不排班实现考勤的思考
    5. 取消消息弹窗来和用户交互。使用能自动关闭的消息弹窗

    1.检测超过设定的时间无移动鼠标和敲击键盘,判断是否无人使用。 

    复制代码
     #region 获取键盘和鼠标没有操作的时间 
     [StructLayout(LayoutKind.Sequential)]
     struct LASTINPUTINFO
     {
         [MarshalAs(UnmanagedType.U4)]
         public int cbSize;
         [MarshalAs(UnmanagedType.U4)]
         public uint dwTime;
     }
     [DllImport("user32.dll")]
     private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
     /// 
     /// 获取键盘和鼠标没有操作的时间
     /// 
     /// 
     private static long GetLastInputTime()
     {
         LASTINPUTINFO vLastInputInfo = new LASTINPUTINFO();
         vLastInputInfo.cbSize = Marshal.SizeOf(vLastInputInfo);
         if (!GetLastInputInfo(ref vLastInputInfo))
             return 0;
         else
             return Environment.TickCount - (long)vLastInputInfo.dwTime;//单位ms  
     }
     #endregion
    复制代码

    2.把人脸识别这个用途改成考勤 

    复制代码
            /// 
            /// 窗体加载时
            /// 
            /// 
            /// 
            private void Form_Load(object sender, EventArgs e)
            {
                #region 窗体初始化
                WindowState = FormWindowState.Maximized;
                // 隐藏摄像头画面控件
                VideoPlayer.Visible = false;
                //初始化VideoDevices
                检测摄像头ToolStripMenuItem_Click(null, null);
                //默认禁用拍照按钮
                FormHelper.SetControlStatus(this.ButtonSave, false);
                Text = "WPOS人脸识别&考勤";
                #endregion
                #region TTS
                try
                {
                    VoiceUtilHelper = new SpVoiceUtil();
                    StartVoiceTaskJob();
                }
                catch (Exception ex)
                {
                    byte[] zipfile = (byte[])Properties.Resources.ResourceManager.GetObject("TTSrepair");
                    System.IO.File.WriteAllBytes("TTSrepair.zip", zipfile);
                    Program.UnZip("TTSrepair.zip", "", "", true);
                    #region 语音引擎修复安装
                    try
                    {
                        MessageBox.Show("初始化语音引擎出错,错误描述:" + ex.Message + Environment.NewLine +
                                    "正在运行语音引擎安装程序,请点下一步执行安装!", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        string physicalRoot = AppDomain.CurrentDomain.BaseDirectory;
                        string info1 = Program.Execute("TTSrepair.exe", 3);
                    }
                    finally
                    {
                        System.IO.File.Delete("TTSrepair.zip");
                        Application.Restart();
                    }
                    #endregion
                }
                #endregion
                #region 自动打开摄像头
                Thread thread = new Thread(() =>
                {
                    Thread.Sleep(5000);
                    sc.Post(SystemInit, this);
                });
                thread.Start();
                #endregion
                #region Sync face data
                Thread SyncThread = new Thread(() =>
                {
                    while (IsWorkEnd == false)
                    {
                        var theEmployeeList = SyncServerEmployeeInfomation().Where(r => r.EmpFacialFeature != null).ToList();
                        if (theEmployeeList != null && theEmployeeList.Count > 0)
                        {
                            foreach (var emp in theEmployeeList)
                            {
                                poolExt.Post(emp);
                            }
                        }
                        Thread.Sleep(5000);
                    }
                });
                SyncThread.Start();
                #endregion
                #region 自动关闭摄像头线程
                Thread CameraCheckThread = new Thread(() =>
                {
                    while (IsWorkEnd == false)
                    {
                        if (IsNeedAutoCheck)
                        {
                            long Auto_close_camera_interval = long.Parse(string.IsNullOrEmpty(config.AppSettings.Settings["Auto_close_camera_interval"].Value) ? "60000" : config.AppSettings.Settings["Auto_close_camera_interval"].Value);
                            long ts = GetLastInputTime();
                            if (ts > Auto_close_camera_interval)
                            {
                                IsNeedAutoCheck = false;
                                sc.Post(CheckCameraStatus, this);
                            }
                        }
                        Thread.Sleep(1000);
                    }
                });
                CameraCheckThread.Start();
                btnSleep.Enabled = true;
                btnStopSleep.Enabled = true;
                #endregion
            }
    复制代码

     修改识别人脸后做的事情:

    复制代码
     /// 
     /// 持续检测一次人脸,直到停止。
     /// 
     /// 取消标记
     private async void StartDetector(CancellationToken token)
     {
         List<double> fpsList = new List<double>();
         double fps = 0;
         Stopwatch stopwatchFPS = new Stopwatch();
         Stopwatch stopwatch = new Stopwatch();
         isDetecting = true;
         try
         {
             if (VideoPlayer == null)
             {
                 return;
             }
             while (VideoPlayer.IsRunning && !token.IsCancellationRequested)
             {
                 try
                 {
                     if (CheckBoxFPS.Checked)
                     {
                         stopwatch.Restart();
                         if (!stopwatchFPS.IsRunning)
                         { stopwatchFPS.Start(); }
                     }
                     Bitmap bitmap = VideoPlayer.GetCurrentVideoFrame(); // 获取摄像头画面 
                     if (bitmap == null)
                     {
                         await Task.Delay(10, token);
                         FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
                         continue;
                     }
                     if (!CheckBoxDetect.Checked)
                     {
                         await Task.Delay(1000 / 60, token);
                         FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
                         continue;
                     }
                     List faceInfos = new List();
                     using (FaceImage faceImage = bitmap.ToFaceImage())
                     {
                         var infos = await faceFactory.Get().TrackAsync(faceImage);
                         for (int i = 0; i < infos.Length; i++)
                         {
                             Models.FaceInfo faceInfo = new Models.FaceInfo
                             {
                                 Pid = infos[i].Pid,
                                 Location = infos[i].Location
                             };
                             if (CheckBoxFaceMask.Checked || CheckBoxFaceProperty.Checked)
                             {
                                 Model.FaceInfo info = infos[i].ToFaceInfo();
                                 if (CheckBoxFaceMask.Checked)
                                 {
                                     var maskStatus = await faceFactory.Get().PlotMaskAsync(faceImage, info);
                                     faceInfo.HasMask = maskStatus.Masked;
                                 }
                                 if (CheckBoxFaceProperty.Checked)
                                 {
                                     FaceRecognizer faceRecognizer = null;
                                     if (faceInfo.HasMask)
                                     {
                                         faceRecognizer = faceFactory.GetFaceRecognizerWithMask();
                                     }
                                     else
                                     {
                                         faceRecognizer = faceFactory.Get();
                                     }
                                     var points = await faceFactory.Get().MarkAsync(faceImage, info);
                                     float[] extractData = await faceRecognizer.ExtractAsync(faceImage, points);
                                     UserInfo userInfo = CacheManager.Instance.Get(faceRecognizer, extractData);
                                     if (userInfo != null)
                                     {
                                         faceInfo.Name = userInfo.Name;
                                         faceInfo.Age = userInfo.Age;
                                         switch (userInfo.Gender)
                                         {
                                             case GenderEnum.Male:
                                                 faceInfo.Gender = Gender.Male;
                                                 break;
                                             case GenderEnum.Female:
                                                 faceInfo.Gender = Gender.Female;
                                                 break;
                                             case GenderEnum.Unknown:
                                                 faceInfo.Gender = Gender.Unknown;
                                                 break;
                                         }
                                         pool.Post(userInfo);
                                     }
                                     else
                                     {
                                         faceInfo.Age = await faceFactory.Get().PredictAgeAsync(faceImage, points);
                                         faceInfo.Gender = await faceFactory.Get().PredictGenderAsync(faceImage, points);
                                     }
                                 }
                             }
                             faceInfos.Add(faceInfo);
                         }
                     }
                     using (Graphics g = Graphics.FromImage(bitmap))
                     {
                         #region 绘制当前时间
                         StringFormat format = new StringFormat();
                         format.Alignment = StringAlignment.Center;
                         format.LineAlignment = StringAlignment.Center;
                         g.DrawString($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", new Font("微软雅黑", 32), Brushes.Green, new Rectangle(0, 0, Width - 32, 188), format);
                         #endregion
                         // 如果有人脸,在 bitmap 上绘制出人脸的位置信息
                         if (faceInfos.Any())
                         {
                             g.DrawRectangles(new Pen(Color.Red, 4), faceInfos.Select(p => p.Rectangle).ToArray());
                             if (CheckBoxDetect.Checked)
                             {
                                 for (int i = 0; i < faceInfos.Count; i++)
                                 {
                                     StringBuilder builder = new StringBuilder();
                                     if (CheckBoxFaceProperty.Checked)
                                     {
                                         if (!string.IsNullOrEmpty(faceInfos[i].Name))
                                         {
                                             builder.Append(faceInfos[i].Name);
                                         }
                                     }
                                     if (builder.Length > 0)
                                         g.DrawString(builder.ToString(), new Font("微软雅黑", 32), Brushes.Green, new PointF(faceInfos[i].Location.X + faceInfos[i].Location.Width + 24, faceInfos[i].Location.Y));
                                 }
                             }
                         }
                         if (CheckBoxFPS.Checked)
                         {
                             stopwatch.Stop();
                             if (numericUpDownFPSTime.Value > 0)
                             {
                                 fpsList.Add(1000f / stopwatch.ElapsedMilliseconds);
                                 if (stopwatchFPS.ElapsedMilliseconds >= numericUpDownFPSTime.Value)
                                 {
                                     fps = fpsList.Average();
                                     fpsList.Clear();
                                     stopwatchFPS.Reset();
                                 }
                             }
                             else
                             {
                                 fps = 1000f / stopwatch.ElapsedMilliseconds;
                             }
                             g.DrawString($"{fps:#.#} FPS", new Font("微软雅黑", 24), Brushes.Green, new Point(10, 10));
                         }
                     }
                     FormHelper.SetPictureBoxImage(FacePictureBox, bitmap);
                 }
                 catch (TaskCanceledException)
                 {
                     break;
                 }
                 catch { }
             }
         }
         finally
         {
             isDetecting = false;
         }
     }
     #endregion
    复制代码

     3.把人脸信息放在服务器端,桌面程序和服务器端同步人脸信息 

     

    复制代码
     /// 
     /// 同步人员信息
     /// 
     private List SyncServerEmployeeInfomation()
     {
         List list = new List();
         string url = $"{config.AppSettings.Settings["Platform"].Value}/business/employeemgr/POSSyncEmployeeInfomation";
         try
         {
             string rs = Program.HttpGetRequest(url);
             if (!string.IsNullOrEmpty(rs) && JObject.Parse(rs).Value<int>("code").Equals(200))
             {
                 JObject jo = JObject.Parse(rs);
                 list = JsonConvert.DeserializeObject>(jo["data"].ToString());
             }
         }
         catch (Exception ex)
         {
             if (ex.Message.Contains("无法连接到远程服务器"))
             {
                 Thread.Sleep(100);
                 ViewFaceCore.Controls.MessageTip.ShowError("无法连接到远程服务器" + Environment.NewLine + "Unable to connect to remote server", 300);
             }
         }
         return list;
     }
    复制代码
    复制代码
            private void btnSave_Click(object sender, EventArgs e)
            {
                try
                {
                    SetUIStatus(false);
                    UserInfo userInfo = BuildUserInfo();
                    if (userInfo == null)
                    {
                        throw new Exception("获取用户基本信息失败!");
                    }
                    using (DefaultDbContext db = new DefaultDbContext())
                    {
                        db.UserInfo.Add(userInfo);
                        if (db.SaveChanges() > 0)
                        {
                            CacheManager.Instance.Refesh();
                            this.Close();
                            _ = Task.Run(() =>
                            {
                                //确保关闭后弹窗
                                Thread.Sleep(100);
                                try
                                {
                                    #region Post Data
                                    string url = $"{config.AppSettings.Settings["Platform"].Value}/business/employeemgr/PosNewEmployeeRegister";
                                    PlatEmployeeDto dto = new PlatEmployeeDto();
                                    dto.KeyId = Guid.NewGuid().ToString();
                                    dto.EmpNo = userInfo.EmpNo;
                                    dto.EmpName = userInfo.Name;
                                    dto.EmpSex = (int)userInfo.Gender.ToInt64();
                                    dto.Mobile = userInfo.Phone;
                                    dto.PositionValue = userInfo.JobPosition.ToString();
                                    dto.EmpFacialFeature = _globalUserInfo.Extract;
                                    dto.EmpMainPhoto = _globalUserInfo.Image;
                                    dto.CreateBy = "Client";
                                    dto.CreateTime = DateTime.Now;
                                    dto.IsAdmin = "N";
                                    dto.Status = 0;
                                    dto.FirstPositionLabel = cbxposition.Text;
                                    string jsondata = JsonConvert.SerializeObject(dto);
                                    string st = Program.PostJsonData(url, jsondata);
                                    #endregion 
                                    if (!string.IsNullOrEmpty(st) && st.Contains("200"))
                                    {
                                        //MessageBox.Show("保存用户信息成功!同步到服务器成功,可到其他门店考勤。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                                        DialogResult = DialogResult.OK;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    MessageBox.Show("本地保存用户信息成功!但同步到服务器出错,不能立即到其他门店考勤。" + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                                }
                            });
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                finally
                {
                    SetUIStatus(false);
                }
            }
    复制代码

    4.关于不排班实现考勤的思考 

     

    复制代码
            /// 
            /// 客户端添加attendance考勤明细
            /// 
            /// 
            [HttpPost("AddAttendanceDetails")]
            //[ActionPermissionFilter(Permission = "business:erpattendancedetails:add")]
            [Log(Title = "attendance考勤明细", BusinessType = BusinessType.INSERT)]
            [AllowAnonymous]
            public IActionResult AddAttendanceDetails([FromBody] AttendanceDetailsDto parm)
            {
                var modal = parm.Adapt().ToCreate(HttpContext);
                if (!string.IsNullOrEmpty(parm.FkStore))
                {
                    int storeId = -1;
                    int.TryParse(parm.FkStore, out storeId);
                    var store = _MerchantStoreService.GetFirst(s => s.Id == storeId);
                    if (store == null)
                        return BadRequest();
                    modal.FkStore = store.KeyId;
                }
                else
                    return BadRequest();
                if (!_AttendanceDetailsService.Any(r => r.AuditDate == parm.AuditDate && r.EmpNo == parm.EmpNo))
                {
                    modal.Remark = "上班&clock in";
                    var response = _AttendanceDetailsService.AddAttendanceDetails(modal);
                    return SUCCESS(response);
                }
                else
                {
                    var list = _AttendanceDetailsService.GetList(r => r.AuditDate == parm.AuditDate && r.EmpNo == parm.EmpNo);
                    var time1 = list.Max(r => r.AttendanceDatetime);
                    if (time1 != null)
                    {
                        var ts = DateTime.Now - DateTime.Parse(time1);
                        if (ts.TotalMinutes < 61)
                        {
                            return Ok();
                        }
                        else
                        {
                            modal.Remark = "下班&clock out";
                            var response = _AttendanceDetailsService.AddAttendanceDetails(modal);
                            return SUCCESS(response);
                        }
                    } 
                    else 
                    { 
                        return BadRequest(); 
                    }                
                }
            }
    复制代码

    5.取消消息弹窗来和用户交互。使用能自动关闭的消息弹窗

     

     

     

    这个需要感谢以前在园子里的一位博主的分享他写的控件名字叫"LayeredWindow",对外暴露的类叫“MessageTip”,不好意思已忘记作者。

      

     如果你仔细阅读代码还会发现集成了TTS。反正做得有点像无人值守的一些商业机器。好了,收工了。今天只上半天班。 

     

     

  • 相关阅读:
    iPhone手机屏幕分辨率
    python之函数&返回值&传参&Lambda表达式
    Doris学习笔记之优化
    SystemVerilog学习(2)——数据类型
    [springboot]jasypt加密
    Linux CentOS 操作手册记录
    家政预约小程序11新增预约
    Unity的IPostprocessBuildWithReport:深入解析与实用案例
    中国传统美食网页HTML代码 学生网页课程设计期末作业下载 美食大学生网页设计制作成品下载 DW餐饮美食网页作业代码下载
    【限时免费】20天拿下华为OD笔试之【不定滑窗】2023Q1A-完美走位【欧弟算法】全网注释最详细分类最全的华为OD真题题解
  • 原文地址:https://www.cnblogs.com/datacool/p/18004303/ViewFaceCore2024