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);
/// <summary>
/// 获取键盘和鼠标没有操作的时间
/// </summary>
/// <returns></returns>
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.把人脸识别这个用途改成考勤

        /// <summary>
/// 窗体加载时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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
}

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

 /// <summary>
/// 持续检测一次人脸,直到停止。
/// </summary>
/// <param name="token">取消标记</param>
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<Models.FaceInfo> faceInfos = new List<Models.FaceInfo>();
using (FaceImage faceImage = bitmap.ToFaceImage())
{
var infos = await faceFactory.Get<FaceTracker>().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<MaskDetector>().PlotMaskAsync(faceImage, info);
faceInfo.HasMask = maskStatus.Masked;
}
if (CheckBoxFaceProperty.Checked)
{
FaceRecognizer faceRecognizer = null;
if (faceInfo.HasMask)
{
faceRecognizer = faceFactory.GetFaceRecognizerWithMask();
}
else
{
faceRecognizer = faceFactory.Get<FaceRecognizer>();
}
var points = await faceFactory.Get<FaceLandmarker>().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<AgePredictor>().PredictAgeAsync(faceImage, points);
faceInfo.Gender = await faceFactory.Get<GenderPredictor>().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.把人脸信息放在服务器端,桌面程序和服务器端同步人脸信息

 /// <summary>
/// 同步人员信息
/// </summary>
private List<PlatEmployeeDto> SyncServerEmployeeInfomation()
{
List<PlatEmployeeDto> list = new List<PlatEmployeeDto>();
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<List<PlatEmployeeDto>>(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.关于不排班实现考勤的思考 

        /// <summary>
/// 客户端添加attendance考勤明细
/// </summary>
/// <returns></returns>
[HttpPost("AddAttendanceDetails")]
//[ActionPermissionFilter(Permission = "business:erpattendancedetails:add")]
[Log(Title = "attendance考勤明细", BusinessType = BusinessType.INSERT)]
[AllowAnonymous]
public IActionResult AddAttendanceDetails([FromBody] AttendanceDetailsDto parm)
{
var modal = parm.Adapt<AttendanceDetails>().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。反正做得有点像无人值守的一些商业机器。好了,收工了。今天只上半天班。

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

  1. Matlab与.NET混编解决人脸识别问题

    原文 http://www.cnblogs.com/asxinyu/archive/2013/05/29/3107013.html 如果这些文章对你有用,有帮助,期待更多开源组件介绍,请不要吝啬手中的 ...

  2. Matlab与.NET混合编程解决人脸识别问题

    原文:[原创]Matlab与.NET混合编程解决人脸识别问题 如果这些文章对你有用,有帮助,期待更多开源组件介绍,请不要吝啬手中的鼠标. [原创分享]Matlab.NET混编调用Figure窗体 ht ...

  3. Opencv摄像头实时人脸识别

    Introduction 网上存在很多人脸识别的文章,这篇文章是我的一个作业,重在通过摄像头实时采集人脸信息,进行人脸检测和人脸识别,并将识别结果显示在左上角. 利用 OpenCV 实现一个实时的人脸 ...

  4. paper 50 :人脸识别简史与近期进展

    自动人脸识别的经典流程分为三个步骤:人脸检测.面部特征点定位(又称Face Alignment人脸对齐).特征提取与分类器设计.一般而言,狭义的人脸识别指的是"特征提取+分类器"两 ...

  5. DeepID人脸识别算法之三代(转)

    DeepID人脸识别算法之三代 转载请注明:http://blog.csdn.net/stdcoutzyx/article/details/42091205 DeepID,目前最强人脸识别算法,已经三 ...

  6. Python 3 利用 Dlib 19.7 实现摄像头人脸识别

    0.引言 利用python开发,借助Dlib库捕获摄像头中的人脸,提取人脸特征,通过计算欧氏距离来和预存的人脸特征进行对比,达到人脸识别的目的: 可以自动从摄像头中抠取人脸图片存储到本地: 根据抠取的 ...

  7. face recognition[翻译][深度人脸识别:综述]

    这里翻译下<Deep face recognition: a survey v4>. 1 引言 由于它的非侵入性和自然特征,人脸识别已经成为身份识别中重要的生物认证技术,也已经应用到许多领 ...

  8. 手把手教你如何用 OpenCV + Python 实现人脸识别

    下午的时候,配好了OpenCV的Python环境,OpenCV的Python环境搭建.于是迫不及待的想体验一下opencv的人脸识别,如下文. 必备知识 Haar-like 通俗的来讲,就是作为人脸特 ...

  9. 一种基于python的人脸识别开源系统

    今天在搜索人脸识别的文章时,无意中搜到一个比较开源代码,介绍说是这个系统人脸的识别率 是比较高的,可以达到:99.38%.这么高的识别率,着实把我吓了一跳.抱着实事求是的态度.个人 就做了一些验证和研 ...

  10. C# 图片人脸识别

    此程序基于 虹软人脸识别进行的开发 前提条件从虹软官网下载获取ArcFace引擎应用开发包,及其对应的激活码(App_id, SDK_key)将获取到的开发包导入到您的应用中 App_id与SDK_k ...

随机推荐

  1. RSAC创新沙盒十强出炉,这家SCA公司火了

    引言 近日,全球网络安全行业创新风向标RSAC创新沙盒公布了本年度入围十强的名单,软件供应链安全企业Endor Labs凭借基于依赖关系建立应用开发生命周期的解决方案获得了广泛关注. Endor La ...

  2. 一键在线获取APP公钥、包名、签名及备案信息方法介绍

    ​ 目录 一键在线获取APP公钥.包名.签名及备案信息方法介绍 摘要 引言 一键获取APP包信息 操作步骤 ​编辑 解析报告 总结 致谢 关键词 参考资料 声明 摘要 本文介绍了一款在线APP解析工具 ...

  3. <vue初体验> 基础知识 3、vue的计数器

    系列导航 <vue初体验> 一. vue的引入和使用体验 <vue初体验> 二. vue的列表展示 <vue初体验> 三. vue的计数器 <vue初体验&g ...

  4. P2234

    乐死我了,一道需要用平衡树的算法的题,在我忘了看标签的情况下下意识用了一个普及-难度的超简单思路解决了.当然其中加入了一些半骗分半贪心性质的剪枝. 总之这破算法竟然AC了就离谱,乐死我了 Code # ...

  5. spring-transaction源码分析(4)AspectJ和spring-aspects模块

    AspectJ是Java语言实现的一个面向切面编程的扩展库,能够基于一定的语法编写Aspect代码,使用ajc编译器将其编译成.class文件,之后在Java程序编写或加载时将Aspect逻辑嵌入到指 ...

  6. SpringBoot中使用LocalDateTime踩坑记录

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  7. 【TouchGFX 】使用 CubeMX 创建 TouchGFX 工程时 LCD 显示为雪花屏

    经几个晚上折腾,修改大量的LTDC时钟.时序,FMC时序等,结果还是一样,耐心与好使的工程仔细对比,发现是时钟源配置问题,真是冤,聊以此以示纪念 实质上是没有分清有源和无源晶振 无源晶振又被叫做 谐振 ...

  8. MySQL复习——20211027

    MYSQL MySQL创建数据库 我们可以在登录MySQL服务后,使用create命令创建数据库,语法如下: CREATE DATABASE 数据库名; 使用root用户登录,root用户拥有最高权限 ...

  9. [转帖]NET Framework 版本和依赖关系

    https://learn.microsoft.com/zh-cn/dotnet/framework/migration-guide/versions-and-dependencies 每个版本的 . ...

  10. [转帖]Innodb存储引擎-备份和恢复(分类、冷备、热备、逻辑备份、二进制日志备份和恢复、快照备份、复制)

    文章目录 备份和恢复 分类 冷备 热备 逻辑备份 mysqldump SELECT...INTO OUTFILE 恢复 二进制日志备份与恢复 快照备份(完全备份) 复制 快照+复制的备份架构 备份和恢 ...