FormGuard.cs 23 KB

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