No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

644 líneas
26 KiB

  1. using MaiMuAOI.ImageProcessing;
  2. using Newtonsoft.Json;
  3. using OpenCvSharp;
  4. using ProductionControl.UIExtend;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.ComponentModel;
  8. using System.Data;
  9. using System.Drawing;
  10. using System.Drawing.Imaging;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using System.Windows.Forms;
  17. using static MaiMuAOI.ImageProcessing.DefectLib;
  18. namespace DefectInferTool
  19. {
  20. public partial class Form1 : Form
  21. {
  22. private string AIModelPath;
  23. private string _sizeItemsPath;
  24. private string _defectItemsPath;
  25. private string _defectOnnxName;
  26. private string _defectOnnxPath;
  27. private string _smallImagePath;
  28. private DefectLib defectLib;
  29. private int defectBmpNum;
  30. private int defectBmpNumResult;
  31. private List<QualifiedCriterion> QualifiedCriterionList;
  32. private bool OpenAsideDefect;
  33. private OpenCvSharp.Size CutSize = new OpenCvSharp.Size(592, 532);
  34. private OpenCvSharp.Size tResize = new OpenCvSharp.Size(224, 224);
  35. private float Thresholds = 0.4f;
  36. private int tHoleCount;
  37. private int AllDefectCount;
  38. private List<DefectCntInfo> DefectCntInfoList = new List<DefectCntInfo>();
  39. public Form1()
  40. {
  41. InitializeComponent();
  42. }
  43. private void Form1_Load(object sender, EventArgs e)
  44. {
  45. AIModelPath = Path.Combine(Directory.GetCurrentDirectory(), "ConfigFiles\\onnxFiles");
  46. numericUpDown1.Value = CutSize.Width;
  47. numericUpDown2.Value = CutSize.Height;
  48. numericUpDown4.Value = tResize.Width;
  49. numericUpDown3.Value = tResize.Height;
  50. numericUpDown6.Value = (decimal)Thresholds;
  51. GetModels();
  52. defectLib = new DefectLib();
  53. defectLib.WarningEvent = (warning, msg) =>
  54. {
  55. AddTextEvent("缺陷处理", msg, warning);
  56. };
  57. if (!defectLib.start())
  58. MessageBox.Show("外观检测核心初始化失败!");
  59. }
  60. #region 模型选择
  61. private void GetModels()
  62. {
  63. var dirs = Directory.GetDirectories(AIModelPath);
  64. string[] pname = new string[dirs.Length];
  65. int i = 0;
  66. foreach (var item in dirs)
  67. {
  68. pname[i++] = Path.GetFileNameWithoutExtension(item);
  69. }
  70. this.comboBox1.Items.Clear();
  71. this.comboBox1.Items.AddRange(pname);
  72. }
  73. #endregion
  74. #region 缺陷检测项
  75. #region 缺陷检测参数
  76. public class DefectItemParam
  77. {
  78. /// <summary>
  79. /// 名称
  80. /// </summary>
  81. public string Name { get; set; }
  82. /// <summary>
  83. /// 代码
  84. /// </summary>
  85. public string Code { get; set; }
  86. /// <summary>
  87. /// 序号
  88. /// </summary>
  89. public int Index { get; set; }
  90. /// <summary>
  91. /// 默认分值
  92. /// </summary>
  93. public double Threshold { get; set; }
  94. /// <summary>
  95. /// RGB颜色代码
  96. /// </summary>
  97. public int[] RGBcolor { get; set; }
  98. /// <summary>
  99. /// 对应类型
  100. /// </summary>
  101. public string Type { get; set; }
  102. /// <summary>
  103. /// 尺寸
  104. /// </summary>
  105. public float Size { get; set; }
  106. /// <summary>
  107. /// 最大允许缺陷数量
  108. /// </summary>
  109. public int MaxDefectCount { get; set; }
  110. /// <summary>
  111. /// 显示
  112. /// </summary>
  113. public int Show { get; set; }
  114. }
  115. #endregion
  116. public List<DefectItemParam> DefectItemParamsList = new List<DefectItemParam>();
  117. public void ReadDefectItems(string path)
  118. {
  119. string _rootPath = Path.Combine(Directory.GetCurrentDirectory(), "ConfigFiles\\onnxFiles\\" + path);
  120. //_sizeItemsPath = Path.Combine(_rootPath, "尺寸测试项.json");
  121. _defectItemsPath = Path.Combine(_rootPath, "缺陷测试项.json");
  122. _defectOnnxPath = Directory.GetFiles(_rootPath, "*.onnx", SearchOption.TopDirectoryOnly)[0];
  123. _defectOnnxName = Path.GetFileNameWithoutExtension(Directory.GetFiles(_rootPath, "*.onnx", SearchOption.TopDirectoryOnly )[0]);
  124. if(string.IsNullOrEmpty(_defectItemsPath))
  125. {
  126. MessageBox.Show("缺少 缺陷测试项.json!");
  127. return;
  128. }
  129. if (string.IsNullOrEmpty(_defectOnnxName))
  130. {
  131. MessageBox.Show("缺少 onnx模型!");
  132. return;
  133. }
  134. string str = File.ReadAllText(_defectItemsPath);
  135. DefectItemParamsList = JsonConvert.DeserializeObject<List<DefectItemParam>>(str);
  136. }
  137. #endregion
  138. #region 记录
  139. object AddTextLock = new object();
  140. private void AddTextEvent(string tag, string msg, WarningEnum level = WarningEnum.Normal)
  141. {
  142. this.Invoke(new Action(() =>
  143. {
  144. lock (AddTextLock)
  145. {
  146. if (tag != null && tag != "")
  147. tag = $" - [{tag}]";
  148. var now = DateTime.Now;
  149. msg = now.ToString("HH:mm:ss fff") + tag + " - " + msg;
  150. msg = (level == WarningEnum.Normal ? "B" : level == WarningEnum.Low ? "Y" : "R") + msg;
  151. if (this.lstLog.Items.Count > 1000)
  152. this.lstLog.Items.Clear();
  153. lstLog.Items.Insert(0, msg);
  154. }
  155. }));
  156. }
  157. #endregion
  158. #region 推理
  159. private void button1_Click(object sender, EventArgs e)
  160. {
  161. if (string.IsNullOrEmpty(_defectOnnxName))
  162. {
  163. MessageBox.Show($"没用模型", "警告");
  164. return;
  165. }
  166. string imgfilePath;
  167. FolderBrowserDialog dlg = new FolderBrowserDialog();
  168. dlg.Description = "选择处理图片路径";
  169. //dlg.SelectedPath = (defaultPath != "" ? defaultPath : Path.GetFullPath("."));
  170. if (dlg.ShowDialog() == DialogResult.OK)
  171. imgfilePath = dlg.SelectedPath;
  172. else
  173. return;
  174. textBox3.Text = imgfilePath;
  175. //string tp = radioButton1.Checked ? "*.bmp" : "*.jpg";
  176. string tp = "*.bmp";
  177. string[] files = Directory.GetFiles(imgfilePath, tp, SearchOption.TopDirectoryOnly);
  178. if (files == null || files.Length <= 0)
  179. {
  180. MessageBox.Show($"缺少推理的{tp}图片", "警告");
  181. return;
  182. }
  183. this.gboxDefectList.Tag = 0;
  184. AllDefectCount = 0;
  185. defectBmpNum = 0;
  186. defectBmpNumResult = 0;
  187. _smallImagePath = textBox4.Text;
  188. OpenAsideDefect = checkBox1.Checked;
  189. CutSize.Width = (int)numericUpDown1.Value;
  190. CutSize.Height = (int)numericUpDown2.Value;
  191. tResize.Width = (int)numericUpDown4.Value;
  192. tResize.Height = (int)numericUpDown3.Value;
  193. Thresholds = (float)numericUpDown6.Value;
  194. tHoleCount = (int)numericUpDown5.Value;
  195. if (DefectCntInfoList == null)
  196. DefectCntInfoList = new List<DefectCntInfo>();
  197. else
  198. DefectCntInfoList.Clear();
  199. if (QualifiedCriterionList == null)
  200. QualifiedCriterionList = new List<QualifiedCriterion>();
  201. else
  202. QualifiedCriterionList.Clear();
  203. foreach (DefectCountOfSizeControl defectControl in this.flpQualifiedPannel.Controls)
  204. {
  205. if (defectControl.Checked)
  206. {
  207. QualifiedCriterionList.Add(
  208. new QualifiedCriterion()
  209. {
  210. DefectCode = defectControl.Code,
  211. Size = (float)defectControl.SizeValue,
  212. MaxDefectCount = (int)defectControl.MaxDefectCount,
  213. Threshold = (double)defectControl.Threshold,
  214. });
  215. }
  216. }
  217. defectLib.loadModelFile(_defectOnnxPath, checkBox2.Checked);
  218. for (int i = 0; i < files.Count(); i++)
  219. {
  220. Mat mat = new Mat(files[i], ImreadModes.Color);
  221. AddInfer(mat, mat);
  222. AddTextEvent("加载图片", files[i]);
  223. }
  224. while(true)
  225. {
  226. if (defectBmpNum >0 && defectBmpNumResult == defectBmpNum)
  227. break;
  228. Thread.Sleep(100);
  229. Application.DoEvents();
  230. }
  231. MessageBox.Show("处理完成");
  232. }
  233. /// <summary>
  234. /// 获取种类阈值
  235. /// </summary>
  236. /// <param name="m"></param>
  237. /// <returns></returns>
  238. private string getProductThresholClass()
  239. {
  240. string thresStr = "";
  241. foreach (var item in DefectItemParamsList)
  242. {
  243. if ((item.Type.Split(',').Count(p => p == "-1") > 0) ||
  244. (item.Type.Split(',').Count(p => p == _defectOnnxName.Replace(".onnx", "")) > 0))
  245. {
  246. bool have = false;
  247. if (QualifiedCriterionList != null)
  248. {
  249. foreach (var queItem in QualifiedCriterionList)
  250. {
  251. if (queItem.DefectCode == item.Code)
  252. {
  253. have = true;
  254. if (queItem.Threshold != null || queItem.Threshold > 0)
  255. thresStr += queItem.Threshold.ToString() + ",";
  256. else
  257. thresStr += item.Threshold.ToString() + ",";
  258. break;
  259. }
  260. }
  261. }
  262. if (!have)
  263. {
  264. thresStr += item.Threshold.ToString() + ",";
  265. }
  266. }
  267. }
  268. //移除最后的,
  269. if (thresStr.Length > 0)
  270. {
  271. thresStr = thresStr.Remove(thresStr.Length - 1);
  272. }
  273. return thresStr;
  274. }
  275. /// <summary>
  276. /// 获取缺陷项阈值
  277. /// </summary>
  278. /// <param name="m"></param>
  279. /// <returns></returns>
  280. private Dictionary<string, float> getProductAreaThreshol(int HoleCount)
  281. {
  282. Dictionary<string, float> dic = new Dictionary<string, float>();
  283. if (!OpenAsideDefect)
  284. {
  285. foreach (var item in QualifiedCriterionList)
  286. dic.Add(item.DefectCode, (float)(item.Size * 25.4 * 25.4 / HoleCount / HoleCount));//网目 => mm^2
  287. }
  288. else
  289. {
  290. foreach (var item in QualifiedCriterionList)
  291. dic.Add(item.DefectCode, (float)(item.Size * 25.4 / HoleCount));//网目 => mm^2
  292. }
  293. //dic.Add(item.DefectCode, (float)(item.Size * 25.4 * 25.4 / m.HoleCount / m.HoleCount));//网目 => mm^2
  294. //全缺陷项
  295. foreach (var item in DefectItemParamsList)
  296. {
  297. string code = item.Code;
  298. if (!dic.ContainsKey(code))
  299. dic.Add(code, 0);
  300. }
  301. return dic;
  302. }
  303. public void countDefectClass(List<string>[] list)
  304. {
  305. string className;
  306. for (int i = 0; i < list.Length; i++)
  307. {
  308. var fd = DefectItemParamsList.Find(p => p.Code == list[i][4]);
  309. if (fd != null)
  310. {
  311. string name = fd.Name;
  312. //改变list中某个元素值
  313. var model = DefectCntInfoList.Where(c => c.Name == name).FirstOrDefault();
  314. if (model != null)
  315. {
  316. model.DefectCnt++;
  317. }
  318. else
  319. {
  320. DefectCntInfoList.Add(new DefectCntInfo()
  321. {
  322. Code = fd.Code,
  323. Name = fd.Name,
  324. DefectCnt = 1,
  325. });
  326. }
  327. }
  328. }
  329. }
  330. private void showDefectSmallBmps(Bitmap[] bmps, Mat[] bmps_cut, double Xmm, double Ymm, List<Dictionary<int, List<string>[]>> info)
  331. {
  332. this.Invoke(new Action(() =>
  333. {
  334. //加载缺陷小图
  335. //纵向显示
  336. int imgWidth = this.pnlBmpList.ClientSize.Width - 50;
  337. int imgHeight = (int)((bmps[0].Height * 1.0f / bmps[0].Width) * imgWidth + 0.5);
  338. int splitWidth = 20;
  339. int pnlWidth = this.pnlBmpList.ClientSize.Width;
  340. int index = (int)this.gboxDefectList.Tag;
  341. //横向显示
  342. //int imgHeight = this.pnlBmpList.Height - 50;
  343. //int imgWidth = (int)((e.Bitmaps[0].Width * 1.0f / e.Bitmaps[0].Height) * imgHeight + 0.5);
  344. //int splitWidth = 20;
  345. //int pnlWidth = this.pnlBmpList.Height;
  346. //int index = (int)this.gboxDefectList.Tag;
  347. int len = info.Count();
  348. int range = 100;
  349. int uselen = len > 100 ? 100 : len;
  350. for (int x = 0; x < uselen / range + 1; x++)
  351. {
  352. for (int i = 0; i < (x < (uselen / range) ? range : uselen % range); i++)
  353. {
  354. foreach (var item in info[(x * range + i)]) //单个info[x] = {"1":[["92.7542","80.85799","99.54083","86.05363","dk","0.52"]]}
  355. {
  356. //统计缺陷类型
  357. countDefectClass(item.Value);//[["92.7542","80.85799","99.54083","86.05363","dk","0.52"]]
  358. //
  359. PictureBox picbox = new PictureBox();
  360. picbox.Width = imgWidth;
  361. picbox.Height = imgHeight;
  362. picbox.Image = (Bitmap)bmps[(x * range + i)].Clone();
  363. picbox.Name = "imgDefect_" + index;
  364. picbox.Tag = item.Key + "," + Xmm + "," + Ymm;
  365. //picbox.Click += new EventHandler(defectBmpBox_Click);
  366. picbox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
  367. picbox.BorderStyle = BorderStyle.FixedSingle;
  368. //picbox.Margin = new Padding((pnlWidth - picbox.Height) / 2, splitWidth, 0, 0);
  369. picbox.Margin = new Padding((pnlWidth - picbox.Width) / 2, splitWidth, 0, 0);
  370. //picbox.MouseHover += simpleTip_MouseHover;
  371. picbox.Visible = true;
  372. //this.Invoke(new MethodInvoker(() =>
  373. // {
  374. this.pnlBmpList.Controls.Add(picbox);
  375. //this.Refresh();
  376. //this.dgvProcess.Refresh();
  377. //this.lstLog.Refresh();
  378. //this.statusStrip1.Refresh();
  379. // }));
  380. break;
  381. }
  382. //
  383. index++;
  384. //Thread.Sleep(10);
  385. //Application.DoEvents();
  386. }
  387. //Application.DoEvents();
  388. //this.Invoke(new MethodInvoker(() =>
  389. //{
  390. //this.Refresh();
  391. pnlBmpList.VerticalScroll.Value = pnlBmpList.VerticalScroll.Maximum;
  392. //}));
  393. //Thread.Sleep(10);
  394. }
  395. //this.Invoke(new MethodInvoker(() =>
  396. //{
  397. //this.gboxDefectList.Tag = len + (int)this.gboxDefectList.Tag;
  398. this.gboxDefectList.Tag = index;
  399. this.gboxDefectList.Text = $"缺陷图像:{index} 张";
  400. //return 0;
  401. }));
  402. }
  403. private void AddInfer(Mat bmpLoc, Mat toDefect)
  404. {
  405. string modeltp = "pt";
  406. if (_defectOnnxName.Replace(".onnx", "").IndexOf("rj") >= 0)
  407. modeltp = "rj";
  408. else
  409. modeltp = "pt";
  410. defectLib.add(new DefectTask()
  411. {
  412. stepIndex = 0,
  413. processName = "模拟测试",
  414. modelType = modeltp,
  415. drawingPagePath = "",
  416. //index = defectBmpNum++,
  417. index = defectBmpNum,
  418. srcbmp = bmpLoc.Clone(),
  419. bmp = toDefect.Clone(),
  420. //bmp = mats[0].Clone(),
  421. Xmm = 0,
  422. Ymm = 0,
  423. cut_size = new System.Drawing.Size(CutSize.Width, CutSize.Height),
  424. resize = new System.Drawing.Size(tResize.Width, tResize.Height),
  425. thresholds = Thresholds,
  426. thresholdsClass = getProductThresholClass(),
  427. recAreaThreshold = getProductAreaThreshol(tHoleCount), //qxName,面积; qxName,面积; qxName,面积;
  428. finishEvent = (res) =>
  429. {
  430. int err = 0;
  431. try
  432. {
  433. string path = "";
  434. if (res.isSucceed)
  435. {
  436. //OnAutoRuning(new RunEventArgs(liStatocStepIndex,
  437. // $"源图索引:{res.index},缺陷数:{res.defectCount},处理时间(ms):{string.Join("->", res.stopwatch.Select(i => i.ToString()).ToArray())}"));
  438. AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测完成(源图索引:{res.index}),缺陷数:{res.defectCount},处理时间(ms):{string.Join("->", res.stopwatch.Select(i => i.ToString()).ToArray())}");
  439. //string path = "";
  440. if (res.defectCount > 0)
  441. {
  442. err = 1;
  443. //UI显示小图 (含统计缺陷类型数量)
  444. AllDefectCount += res.informationList.Count;
  445. showDefectSmallBmps(res.bmps_tag, res.bmps_cut, res.Xmm, res.Ymm, res.informationList);
  446. err = 2;
  447. //保存小图
  448. if (_smallImagePath != "" && Directory.Exists(_smallImagePath))
  449. {
  450. err = 8;
  451. //2024-03-07 图片index计算
  452. List<string> indexList = new List<string>();
  453. if (res.defectInfor2RestorationDeskPage != null && res.defectInfor2RestorationDeskPage.Count > 0)
  454. foreach (var item in res.defectInfor2RestorationDeskPage)
  455. {
  456. if (indexList.Count == 0)
  457. indexList.Add(item[0]);
  458. else
  459. {
  460. if (!indexList.Contains(item[0]))
  461. indexList.Add(item[0]);
  462. }
  463. }
  464. path = _smallImagePath;
  465. path += $"\\Defect_I{res.index}_test";
  466. err = 9;
  467. for (int i = 0; i < res.bmps_tag.Count(); i++)
  468. {
  469. res.bmps_tag[i].Save(path + $"_i{i}.bmp", ImageFormat.Bmp);
  470. Thread.Sleep(10);
  471. }
  472. err = 10;
  473. }
  474. }
  475. else//没有缺陷
  476. {
  477. }
  478. }
  479. else
  480. {
  481. AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测失败:{res.resultInfo}");
  482. }
  483. }
  484. catch (Exception ex)
  485. {
  486. AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测回调处理异常-{err} index:{res.index},ex={ex.Message}");
  487. }
  488. defectBmpNumResult++;
  489. try
  490. {
  491. foreach (var item in res.bmps_cut)
  492. item.Dispose();
  493. res.bmp.Dispose();
  494. res.bmp = null;
  495. res.bmps_tag = null;
  496. if (res.bmpCompress != null)
  497. {
  498. res.bmpCompress.Dispose();
  499. res.bmpCompress = null;
  500. }
  501. if (res.srcbmp != null)
  502. {
  503. res.srcbmp.Dispose();
  504. res.srcbmp = null;
  505. }
  506. }
  507. catch (Exception ex)
  508. {
  509. AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测回调释放内存异常 index:{res.index},ex={ex.Message}");
  510. }
  511. System.GC.Collect();
  512. }
  513. });
  514. defectBmpNum++;
  515. }
  516. #endregion
  517. #region 模型选择
  518. private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
  519. {
  520. ReadDefectItems(this.comboBox1.Text);
  521. foreach (var item in DefectItemParamsList)
  522. {
  523. string mdname = _defectOnnxName;
  524. if ((item.Type.Split(',').Count(p => p == "-1") > 0) ||
  525. (item.Type.Split(',').Count(p => p == mdname.Replace(".onnx", "")) > 0))
  526. {
  527. DefectCountOfSizeControl userCon = new DefectCountOfSizeControl();
  528. userCon.Code = item.Code;
  529. userCon.Title = item.Name;
  530. userCon.Threshold = (decimal)item.Threshold;
  531. //不显示,是有默认值
  532. if (item.Show == 0)
  533. {
  534. userCon.Checked = true;
  535. userCon.Threshold = (decimal)item.Threshold;
  536. userCon.SizeValue = (decimal)item.Size;
  537. userCon.MaxDefectCount = item.MaxDefectCount;
  538. userCon.Visible = false;
  539. }
  540. else
  541. {
  542. userCon.Checked = true;
  543. userCon.Threshold = (decimal)item.Threshold;
  544. userCon.SizeValue = (decimal)item.Size;
  545. userCon.MaxDefectCount = item.MaxDefectCount;
  546. }
  547. this.flpQualifiedPannel.Controls.Add(userCon);
  548. }
  549. }
  550. }
  551. #endregion
  552. #region 小图存储
  553. private void button2_Click(object sender, EventArgs e)
  554. {
  555. FolderBrowserDialog dlg = new FolderBrowserDialog();
  556. //dlg.SelectedPath = (Application.StartupPath != "" ? Application.StartupPath : Path.GetFullPath("."));
  557. if (dlg.ShowDialog() == DialogResult.OK)
  558. {
  559. textBox4.Text = dlg.SelectedPath;
  560. }
  561. else
  562. return;
  563. }
  564. #endregion
  565. }
  566. public class DefectCntInfo
  567. {
  568. public int Pid { get; set; }
  569. //测试项名称
  570. public string Name { get; set; }
  571. //测试项代码
  572. public string Code { get; set; }
  573. //个数
  574. public int DefectCnt { get; set; }
  575. }
  576. /// <summary>
  577. /// 合格标准
  578. /// </summary>
  579. public class QualifiedCriterion
  580. {
  581. public int Pid { get; set; }
  582. public string DefectCode { get; set; }//pp,sx,... EnumUtil.Convert2Enum
  583. /// <summary>
  584. /// 阈值 20240409
  585. /// </summary>
  586. public double Threshold { get; set; }
  587. /// <summary>
  588. /// 尺寸
  589. /// </summary>
  590. public float Size { get; set; }
  591. /// <summary>
  592. /// 最大允许缺陷数量
  593. /// </summary>
  594. public int MaxDefectCount { get; set; }
  595. }
  596. }