FormGuard.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. using DirectShowLib;
  2. using Emgu.CV;
  3. using Emgu.CV.CvEnum;
  4. using Emgu.CV.Face;
  5. using Emgu.CV.Ocl;
  6. using Emgu.CV.Structure;
  7. using Emgu.CV.Util;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.ComponentModel;
  11. using System.Data;
  12. using System.Data.SqlClient;
  13. using System.Diagnostics;
  14. using System.Drawing;
  15. using System.IO;
  16. using System.Linq;
  17. using System.Runtime.CompilerServices;
  18. using System.Text;
  19. using System.Threading;
  20. using System.Threading.Tasks;
  21. using System.Windows.Forms;
  22. using Timer = System.Windows.Forms.Timer;
  23. using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
  24. namespace ImpulseVision
  25. {
  26. public partial class FormGuard : Form
  27. {
  28. public FormGuard()
  29. {
  30. InitializeComponent();
  31. CaptureTimer = new Timer()
  32. {
  33. Interval = Config.TimerResponseValue
  34. };
  35. CaptureTimer.Tick += CaptureTimer_Tick;
  36. }
  37. private void CaptureTimer_Tick(object sender, EventArgs e)
  38. {
  39. TslDate.Text = $"Сейчас: {DateTime.Now.ToString("HH:mm")} {DateTime.Now.ToString("dd.MM.yyyy")}";
  40. ProcessFrame();
  41. }
  42. #region <Переменные>
  43. public event PropertyChangedEventHandler PropertyChanged;
  44. private VideoCapture Capture;
  45. private CascadeClassifier HaarCascade;
  46. private Image<Bgr, Byte> BgrFrame = null;
  47. private Image<Gray, Byte> DetectedFace = null;
  48. private List<FaceData> FaceList = new List<FaceData>();
  49. private VectorOfMat ImageList = new VectorOfMat();
  50. private List<string> NameList = new List<string>();
  51. private VectorOfInt LabelList = new VectorOfInt();
  52. private EigenFaceRecognizer recognizer;
  53. private Timer CaptureTimer;
  54. public string UserName { get; set; } = "Лицо не обнаружено";
  55. /// <summary>
  56. /// ID пользователя, который обнаружен
  57. /// </summary>
  58. private string CurrentUserID = string.Empty;
  59. public struct VisitInput
  60. {
  61. public string FIO, TimeEntrance, TimeExit, Identification;
  62. }
  63. List<VisitInput> LstVisitInput = new List<VisitInput>();
  64. #region FaceName
  65. private string faceName;
  66. public string FaceName
  67. {
  68. get { return faceName; }
  69. set
  70. {
  71. faceName = value;
  72. if (faceName == "Лицо не обнаружено")
  73. {
  74. IsRecognized = false;
  75. }
  76. else
  77. {
  78. IsRecognized = true;
  79. }
  80. UserName = faceName;
  81. NotifyPropertyChanged();
  82. }
  83. }
  84. #endregion
  85. #region CameraCaptureImage
  86. private Bitmap cameraCapture;
  87. //Image<Bgr, byte> bgrFrame, Rectangle face
  88. Rectangle CurrentFace;
  89. public Bitmap CameraCapture
  90. {
  91. get { return cameraCapture; }
  92. set
  93. {
  94. cameraCapture = value;
  95. DrawName(cameraCapture, CurrentFace);
  96. PbxEther.Image = cameraCapture;
  97. NotifyPropertyChanged();
  98. }
  99. }
  100. /// <summary>
  101. /// отрисовка имени над прямоугольком
  102. /// </summary>
  103. private void DrawName(Bitmap cameraCapture, Rectangle face)
  104. {
  105. Pen PenForFace = new Pen(Brushes.Red, 5);
  106. Brush BrushForFace = Brushes.White;
  107. Font MyFont = new Font("Segoe UI Variable Small Semibol; 12pt; style=Bold", 12, FontStyle.Regular);
  108. Brush BrushInfo = Brushes.White;
  109. Pen PenInfo = new Pen(Brushes.White, 4);
  110. Graphics Graph = Graphics.FromImage(cameraCapture);
  111. //Graph.DrawRectangle(PenForFace, face.X, face.Y, face.Width, face.Height);
  112. //позиция отрисовки имени человека
  113. Point PointName = new Point(face.X, face.Y - 25);
  114. Graph.DrawString($"{UserName}", MyFont, BrushForFace, PointName);
  115. }
  116. #endregion
  117. #region CameraCaptureFaceImage
  118. private Bitmap cameraCaptureFace;
  119. public Bitmap CameraCaptureFace
  120. {
  121. get { return cameraCaptureFace; }
  122. set
  123. {
  124. cameraCaptureFace = value;
  125. PbxEther.Image = cameraCapture;
  126. NotifyPropertyChanged();
  127. }
  128. }
  129. #endregion
  130. //уведомление о том, что пользователь распознан
  131. bool IsRecognized = false;
  132. //включена ли на данный момент камера
  133. bool IsWorking = false;
  134. //выбранная камера
  135. int SelectedCameraID = 0;
  136. //доступные видеокамеры
  137. private DsDevice[] WebCams = null;
  138. int CountCams = -1;
  139. //множество для хранения списка камер
  140. HashSet<string> HtBefore = new HashSet<string>();
  141. SqlConnection SCon = new SqlConnection(Properties.Settings.Default.ImpulseVisionAppConnectionString);
  142. #endregion
  143. /// <summary>
  144. /// получение данных об изображениях
  145. /// </summary>
  146. public void GetFacesList()
  147. {
  148. //файл Хаара
  149. if (!File.Exists(Config.HaarCascadePath))
  150. {
  151. string text = "Не удаётся найти файл данных - каскад Хаара:\n\n";
  152. text += Config.HaarCascadePath;
  153. DialogResult result = MessageBox.Show(text, "Ошибка",
  154. MessageBoxButtons.OK, MessageBoxIcon.Error);
  155. }
  156. HaarCascade = new CascadeClassifier(Config.HaarCascadePath);
  157. FaceList.Clear();
  158. FaceData FaceItem = null;
  159. // создание директории если она отсутствовала
  160. if (!Directory.Exists(Config.FacePhotosPath))
  161. {
  162. Directory.CreateDirectory(Config.FacePhotosPath);
  163. }
  164. //получить из БД инфо о пользователе (id, имя, фамилия, путь до фото)
  165. SCon.Open();
  166. string QueryGetInfoAboutUser = $@"select Users.ID ,Users.Lastname, Users.Firstname, Users.Patronymic, FaceImages.Picture
  167. from Users join FaceImages on Users.ID = FaceImages.UserID";
  168. SqlCommand Cmd = new SqlCommand(QueryGetInfoAboutUser, SCon);
  169. SqlDataReader Res = Cmd.ExecuteReader();
  170. if(Res.HasRows)
  171. {
  172. while (Res.Read())
  173. {
  174. try
  175. {
  176. FaceItem = new FaceData();
  177. FaceItem.FaceImage = new Image<Gray, byte>(Application.StartupPath + "\\" + Res["Picture"].ToString());
  178. FaceItem.PersonName = Res["Firstname"].ToString();
  179. FaceItem.LastName = Res["Lastname"].ToString();
  180. FaceItem.Patronymic = Res["Patronymic"].ToString();
  181. FaceItem.UserID = Res["ID"].ToString();
  182. FaceList.Add(FaceItem);
  183. }
  184. catch { }
  185. }
  186. }
  187. else
  188. {
  189. SCon.Close();
  190. MessageBox.Show("Данные о пользователях отсутствуют!", "ImpulseVision", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
  191. return;
  192. }
  193. SCon.Close();
  194. int i = 0;
  195. foreach (var face in FaceList)
  196. {
  197. ImageList.Push(face.FaceImage.Mat);
  198. NameList.Add(face.PersonName + " " + face.LastName);
  199. LabelList.Push(new[] { i++ });
  200. }
  201. // Тренировка распознавания
  202. if (ImageList.Size > 0)
  203. {
  204. recognizer = new EigenFaceRecognizer(ImageList.Size);
  205. recognizer.Train(ImageList, LabelList);
  206. }
  207. }
  208. private Image<Bgr, Byte> CurrentFrame = null;
  209. /// <summary>
  210. /// получение данных с камеры и обработка изображени
  211. /// </summary>
  212. private void ProcessFrame()
  213. {
  214. BgrFrame = Capture.QueryFrame().ToImage<Bgr, Byte>().Flip(FlipType.Horizontal);
  215. if (BgrFrame != null)
  216. {
  217. try
  218. {
  219. Image<Gray, byte> grayframe = BgrFrame.Convert<Gray, byte>();
  220. Rectangle[] faces = HaarCascade.DetectMultiScale(grayframe, 1.2, 10, new System.Drawing.Size(50, 50), new System.Drawing.Size(200, 200));
  221. FaceName = "Лицо не обнаружено";
  222. foreach (var face in faces)
  223. {
  224. CurrentFace = face;
  225. BgrFrame.Draw(face, new Bgr(53, 23, 247), 2);
  226. DetectedFace = BgrFrame.Copy(face).Convert<Gray, byte>();
  227. FaceRecognition();
  228. break;
  229. }
  230. CameraCapture = BgrFrame.ToBitmap();
  231. }
  232. catch (Exception ex)
  233. {
  234. MessageBox.Show(ex.Message);
  235. }
  236. }
  237. }
  238. /// <summary>
  239. /// распознавание лица
  240. /// </summary>
  241. private void FaceRecognition()
  242. {
  243. if (ImageList.Size != 0)
  244. {
  245. FaceRecognizer.PredictionResult result = recognizer.Predict(DetectedFace.Resize(100, 100, Inter.Cubic));
  246. FaceName = NameList[result.Label];
  247. CurrentUserID = FaceList[result.Label].UserID;
  248. CameraCaptureFace = DetectedFace.ToBitmap();
  249. PbxSourceImage.Image = FaceList[result.Label].FaceImage.ToBitmap();
  250. }
  251. else
  252. {
  253. FaceName = "Пожалуйста добавьте лицо";
  254. }
  255. }
  256. protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
  257. {
  258. var handler = PropertyChanged;
  259. if (handler != null)
  260. handler(this, new PropertyChangedEventArgs(propertyName));
  261. }
  262. private void FormGuard_Load(object sender, EventArgs e)
  263. {
  264. // TODO: This line of code loads data into the 'impulseVisionAppDataSet1.Staffs' table. You can move, or remove it, as needed.
  265. this.staffsTableAdapter.Fill(this.impulseVisionAppDataSet1.Staffs);
  266. LblID.Hide();
  267. GetCams();
  268. Task.Factory.StartNew(() => {
  269. PbxEther.Image = Properties.Resources.loading_7;
  270. PbxSourceImage.Image = Properties.Resources.loading_7;
  271. });
  272. IsWorking = true;//камера включена
  273. GetFacesList();
  274. Capture = new VideoCapture(SelectedCameraID);
  275. //настройка кадров
  276. Capture.SetCaptureProperty(CapProp.Fps, 30);
  277. Capture.SetCaptureProperty(CapProp.FrameHeight, 450);
  278. Capture.SetCaptureProperty(CapProp.FrameWidth, 370);
  279. CaptureTimer.Start();
  280. GetVisits();
  281. }
  282. /// <summary>
  283. /// получение списка доступных камер
  284. /// </summary>
  285. private void GetCams()
  286. {
  287. WebCams = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
  288. CountCams = WebCams.Length;
  289. CmbCams.Items.Clear();
  290. for (int i = 0; i < CountCams; i++)
  291. {
  292. CmbCams.Items.Add(WebCams[i].Name);
  293. HtBefore.Add(WebCams[i].Name);
  294. }
  295. if (CountCams > 0)
  296. {
  297. CmbCams.SelectedIndex = 0;
  298. SelectedCameraID = 0;
  299. }
  300. }
  301. private void CmbCams_SelectedIndexChanged(object sender, EventArgs e)
  302. {
  303. try
  304. {
  305. if (BgrFrame != null)
  306. {
  307. BgrFrame = null;
  308. Capture.Dispose();
  309. CaptureTimer.Tick -= CaptureTimer_Tick;
  310. }
  311. }
  312. catch (Exception ex)
  313. {
  314. MessageBox.Show(ex.Message, "Внимание!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
  315. }
  316. SelectedCameraID = CmbCams.SelectedIndex;
  317. Capture = new VideoCapture(SelectedCameraID);
  318. //настройка кадров
  319. Capture.SetCaptureProperty(CapProp.Fps, 30);
  320. Capture.SetCaptureProperty(CapProp.FrameHeight, 450);
  321. Capture.SetCaptureProperty(CapProp.FrameWidth, 370);
  322. CaptureTimer.Tick += CaptureTimer_Tick;
  323. CaptureTimer.Start();
  324. }
  325. private void TimerCam_Tick(object sender, EventArgs e)
  326. {
  327. DsDevice[] Cams = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
  328. int Count = Cams.Length;
  329. //если количество подключённых камер изменилось
  330. if (Count != CountCams)
  331. {
  332. //if (Count > CountCams)
  333. //{
  334. // StatusAddNewDevice(Cams);
  335. //}
  336. //else if (Count < CountCams)
  337. //{
  338. // StatusOffDevice(Cams);
  339. //}
  340. GetCams();
  341. }
  342. }
  343. private void CameraOff()
  344. {
  345. if (BgrFrame != null)
  346. {
  347. BgrFrame = null;
  348. Capture.Dispose();
  349. CaptureTimer.Tick -= CaptureTimer_Tick;
  350. }
  351. PbxEther.Image = Properties.Resources._9110852_video_no_icon;
  352. CaptureTimer.Tick -= CaptureTimer_Tick;
  353. }
  354. private void DgbOutput_CellContentClick(object sender, DataGridViewCellEventArgs e)
  355. {
  356. }
  357. private void FormGuard_FormClosing(object sender, FormClosingEventArgs e)
  358. {
  359. CameraOff();
  360. }
  361. /// <summary>
  362. /// добавление в БД информации о входе
  363. /// </summary>
  364. private void BtnSkip_Click(object sender, EventArgs e)
  365. {
  366. RegisterVisit(1);
  367. GetVisits();
  368. }
  369. /// <summary>
  370. /// регистрация посещения
  371. /// </summary>
  372. /// <param name="IsIdentification">успешность идентификации (1-успешно, 0- нет)</param>
  373. private void RegisterVisit(int IsIdentification)
  374. {
  375. if (!IsRecognized)
  376. {
  377. MessageBox.Show("Убедитесь, что лицо находится в кадре и обведено красным прямоугольником!", "Ошибка распознавания!");
  378. return;
  379. }
  380. if (RbtIn.Checked)
  381. {
  382. if (IsIdentification == 1)
  383. {
  384. #region Предупреждение при попытке повторной идентификации пользователя за один день
  385. SCon.Open();
  386. string QueryCheckExistsVisit = $@"select Count(ID) as Cnt
  387. from UserTraffic
  388. where UserID = '{CurrentUserID}' and [Date] = CAST(GETDATE() as date)";
  389. SqlCommand CmdCheckVisit = new SqlCommand(QueryCheckExistsVisit, SCon);
  390. SqlDataReader Res = CmdCheckVisit.ExecuteReader();
  391. Res.Read();
  392. int n = int.Parse(Res["Cnt"].ToString());
  393. if (n >= 1)
  394. {
  395. MessageBox.Show("Сегодня данный пользователь уже прошёл идентификацию при входе!", "ImpulseVision", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
  396. SCon.Close();
  397. return;
  398. }
  399. SCon.Close();
  400. #endregion
  401. }
  402. SCon.Open();
  403. string QueryAddVisit = $@"set dateformat dmy insert into UserTraffic (UserID,TimeEntrance,Identification,[Date])
  404. values ('{CurrentUserID}','{DateTime.Now.ToString("HH:mm:ss")}','{IsIdentification}','{DateTime.Now.ToString("dd.MM.yyyy")}')";
  405. SqlCommand Cmd = new SqlCommand(QueryAddVisit, SCon);
  406. Cmd.ExecuteNonQuery();
  407. SCon.Close();
  408. }
  409. else
  410. {
  411. SCon.Open();
  412. string QueryCheckRecord = $@"set dateformat dmy
  413. select *
  414. from UserTraffic
  415. where UserID = '{CurrentUserID}' and [Date] = cast(GETDATE() as date) and TimeExit is null
  416. ";
  417. SqlCommand CmdCheck = new SqlCommand(QueryCheckRecord, SCon);
  418. SqlDataReader Res = CmdCheck.ExecuteReader();
  419. if (!Res.HasRows)
  420. {
  421. MessageBox.Show("Данный пользователь уже вышел!", "ImpulseVision", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
  422. SCon.Close();
  423. return;
  424. }
  425. SCon.Close();
  426. //!!! Выход пользователя, который не прошёл идентификацию не регистрируется
  427. SCon.Open();
  428. string QueryTimeExit = $@"update UserTraffic
  429. set TimeExit = '{DateTime.Now.ToString("HH:mm:ss")}'
  430. where UserID = '{CurrentUserID}' and [Date] = cast(GETDATE() as date) and Identification != 0 ";
  431. SqlCommand Cmd = new SqlCommand(QueryTimeExit, SCon);
  432. Cmd.ExecuteNonQuery();
  433. SCon.Close();
  434. }
  435. }
  436. /// <summary>
  437. /// получение списка посещений за текущую дату
  438. /// </summary>
  439. private void GetVisits()
  440. {
  441. SCon.Open();
  442. string QueryGetVisits = $@"select Users.Lastname+' '+Users.Firstname + ' ' + Users.Patronymic as FIO, TimeEntrance,TimeExit, Identification
  443. from UserTraffic ut join Users on ut.UserID = Users.ID
  444. where ut.[Date] = cast(GETDATE() as date)";
  445. SqlCommand Cmd = new SqlCommand(QueryGetVisits, SCon);
  446. SqlDataReader Res = Cmd.ExecuteReader();
  447. if (Res.HasRows)
  448. {
  449. LstVisitInput.Clear();
  450. while (Res.Read())
  451. {
  452. VisitInput visit = new VisitInput();
  453. visit.FIO = Res["FIO"].ToString();
  454. visit.TimeEntrance = Res["TimeEntrance"].ToString();
  455. visit.TimeExit = Res["TimeExit"].ToString();
  456. visit.Identification = Res["Identification"].ToString();
  457. LstVisitInput.Add(visit);
  458. }
  459. }
  460. SCon.Close();
  461. DgbInput.Rows.Clear();
  462. foreach (VisitInput item in LstVisitInput)
  463. {
  464. DgbInput.Rows.Add(item.FIO + $" {item.TimeEntrance}");
  465. if (item.Identification.ToLower() == "false")
  466. {
  467. DgbInput.Rows[DgbInput.RowCount - 1].DefaultCellStyle.BackColor = ColorTranslator.FromHtml("#E84855");
  468. DgbInput.Rows[DgbInput.RowCount - 1].DefaultCellStyle.ForeColor = Color.White;
  469. }
  470. }
  471. DgbOutput.Rows.Clear();
  472. string STime = string.Empty;
  473. foreach (VisitInput item in LstVisitInput)
  474. {
  475. STime = item.TimeExit;
  476. if (STime != string.Empty)
  477. {
  478. DgbOutput.Rows.Add(item.FIO + $" {STime}");
  479. if (item.Identification.ToLower() == "false")
  480. {
  481. DgbInput.Rows[DgbInput.RowCount - 1].DefaultCellStyle.BackColor = ColorTranslator.FromHtml("#E84855");
  482. DgbInput.Rows[DgbInput.RowCount - 1].DefaultCellStyle.ForeColor = Color.White;
  483. }
  484. }
  485. }
  486. }
  487. private void BtnReject_Click(object sender, EventArgs e)
  488. {
  489. RegisterVisit(0);
  490. if (!IsRecognized)
  491. return;
  492. try
  493. {
  494. Image<Bgr, byte> Img = BgrFrame.Copy();
  495. if(!Directory.Exists(Application.StartupPath+@"\Source\log"))
  496. {
  497. Directory.CreateDirectory(Application.StartupPath + @"\Source\log");
  498. }
  499. string PathLog = Application.StartupPath + $"\\Source\\log\\{"User_" + CurrentUserID + "_" + DateTime.Now.ToString("HHmmss") + "_" + DateTime.Now.ToString("dd.MM.yyyy") + "_" + Guid.NewGuid().ToString().Substring(0, 4)}.bmp";
  500. Img.Save(PathLog);
  501. }
  502. catch(Exception ex)
  503. {
  504. MessageBox.Show(ex.Message, "ImpulseVision", MessageBoxButtons.OK, MessageBoxIcon.Error);
  505. }
  506. GetVisits();
  507. }
  508. private void FormGuard_KeyDown(object sender, KeyEventArgs e)
  509. {
  510. if(e.KeyCode == Keys.S)
  511. {
  512. BtnSkip_Click(sender, e);
  513. }
  514. if(e.KeyCode == Keys.D)
  515. {
  516. BtnReject_Click(sender, e);
  517. }
  518. }
  519. }
  520. }