using CalTools; using MaiMuAOI.ImageProcessing; using Newtonsoft.Json; using OpenCvSharp; using ProductionControl.UIExtend; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using static MaiMuAOI.ImageProcessing.DefectLib; namespace DefectInferTool { public partial class Form1 : Form { private string AIModelPath; private string _sizeItemsPath; private string _defectItemsPath; private string _defectOnnxName; private string _defectOnnxPath; private string _smallImagePath; private DefectLib defectLib; private int defectBmpNum; private int defectBmpNumResult; private List QualifiedCriterionList; private bool OpenAsideDefect; private OpenCvSharp.Size CutSize = new OpenCvSharp.Size(592, 532); private OpenCvSharp.Size tResize = new OpenCvSharp.Size(224, 224); private float Thresholds = 0.4f; private int tHoleCount; private int AllDefectCount; private List DefectCntInfoList = new List(); public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { AIModelPath = Path.Combine(Directory.GetCurrentDirectory(), "ConfigFiles\\onnxFiles"); numericUpDown1.Value = CutSize.Width; numericUpDown2.Value = CutSize.Height; numericUpDown4.Value = tResize.Width; numericUpDown3.Value = tResize.Height; numericUpDown6.Value = (decimal)Thresholds; GetModels(); defectLib = new DefectLib(); defectLib.WarningEvent = (warning, msg) => { AddTextEvent("缺陷处理", msg, warning); }; if (!defectLib.start()) MessageBox.Show("外观检测核心初始化失败!"); } #region 模型选择 private void GetModels() { var dirs = Directory.GetDirectories(AIModelPath); string[] pname = new string[dirs.Length]; int i = 0; foreach (var item in dirs) { pname[i++] = Path.GetFileNameWithoutExtension(item); } this.comboBox1.Items.Clear(); this.comboBox1.Items.AddRange(pname); } #endregion #region 缺陷检测项 #region 缺陷检测参数 public class DefectItemParam { /// /// 名称 /// public string Name { get; set; } /// /// 代码 /// public string Code { get; set; } /// /// 序号 /// public int Index { get; set; } /// /// 默认分值 /// public double Threshold { get; set; } /// /// RGB颜色代码 /// public int[] RGBcolor { get; set; } /// /// 对应类型 /// public string Type { get; set; } /// /// 尺寸 /// public float Size { get; set; } /// /// 最大允许缺陷数量 /// public int MaxDefectCount { get; set; } /// /// 显示 /// public int Show { get; set; } } #endregion public List DefectItemParamsList = new List(); public void ReadDefectItems(string path) { string _rootPath = Path.Combine(Directory.GetCurrentDirectory(), "ConfigFiles\\onnxFiles\\" + path); //_sizeItemsPath = Path.Combine(_rootPath, "尺寸测试项.json"); _defectItemsPath = Path.Combine(_rootPath, "缺陷测试项.json"); _defectOnnxPath = Directory.GetFiles(_rootPath, "*.onnx", SearchOption.TopDirectoryOnly)[0]; _defectOnnxName = Path.GetFileNameWithoutExtension(Directory.GetFiles(_rootPath, "*.onnx", SearchOption.TopDirectoryOnly )[0]); if(string.IsNullOrEmpty(_defectItemsPath)) { MessageBox.Show("缺少 缺陷测试项.json!"); return; } if (string.IsNullOrEmpty(_defectOnnxName)) { MessageBox.Show("缺少 onnx模型!"); return; } string str = File.ReadAllText(_defectItemsPath); DefectItemParamsList = JsonConvert.DeserializeObject>(str); } #endregion #region 记录 object AddTextLock = new object(); private void AddTextEvent(string tag, string msg, WarningEnum level = WarningEnum.Normal) { this.Invoke(new Action(() => { lock (AddTextLock) { if (tag != null && tag != "") tag = $" - [{tag}]"; var now = DateTime.Now; msg = now.ToString("HH:mm:ss fff") + tag + " - " + msg; msg = (level == WarningEnum.Normal ? "B" : level == WarningEnum.Low ? "Y" : "R") + msg; if (this.lstLog.Items.Count > 1000) this.lstLog.Items.Clear(); lstLog.Items.Insert(0, msg); } })); } #endregion #region 推理 private void button1_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(_defectOnnxName)) { MessageBox.Show($"没用模型", "警告"); return; } string imgfilePath; FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.Description = "选择处理图片路径"; //dlg.SelectedPath = (defaultPath != "" ? defaultPath : Path.GetFullPath(".")); if (dlg.ShowDialog() == DialogResult.OK) imgfilePath = dlg.SelectedPath; else return; textBox3.Text = imgfilePath; //string tp = radioButton1.Checked ? "*.bmp" : "*.jpg"; string tp = "*.bmp"; string[] files = Directory.GetFiles(imgfilePath, tp, SearchOption.TopDirectoryOnly); if (files == null || files.Length <= 0) { MessageBox.Show($"缺少推理的{tp}图片", "警告"); return; } this.gboxDefectList.Tag = 0; AllDefectCount = 0; defectBmpNum = 0; defectBmpNumResult = 0; _smallImagePath = textBox4.Text; OpenAsideDefect = checkBox1.Checked; CutSize.Width = (int)numericUpDown1.Value; CutSize.Height = (int)numericUpDown2.Value; tResize.Width = (int)numericUpDown4.Value; tResize.Height = (int)numericUpDown3.Value; Thresholds = (float)numericUpDown6.Value; tHoleCount = (int)numericUpDown5.Value; if (DefectCntInfoList == null) DefectCntInfoList = new List(); else DefectCntInfoList.Clear(); if (QualifiedCriterionList == null) QualifiedCriterionList = new List(); else QualifiedCriterionList.Clear(); foreach (DefectCountOfSizeControl defectControl in this.flpQualifiedPannel.Controls) { if (defectControl.Checked) { QualifiedCriterionList.Add( new QualifiedCriterion() { DefectCode = defectControl.Code, Size = (float)defectControl.SizeValue, MaxDefectCount = (int)defectControl.MaxDefectCount, Threshold = (double)defectControl.Threshold, }); } } defectLib.loadModelFile(_defectOnnxPath, checkBox2.Checked); for (int i = 0; i < files.Count(); i++) { Mat mat = new Mat(files[i], ImreadModes.Color); AddInfer(mat, mat); AddTextEvent("加载图片", files[i]); } while(true) { if (defectBmpNum >0 && defectBmpNumResult == defectBmpNum) break; Thread.Sleep(100); Application.DoEvents(); } MessageBox.Show("处理完成"); } /// /// 获取种类阈值 /// /// /// private string getProductThresholClass() { string thresStr = ""; foreach (var item in DefectItemParamsList) { if ((item.Type.Split(',').Count(p => p == "-1") > 0) || (item.Type.Split(',').Count(p => p == _defectOnnxName.Replace(".onnx", "")) > 0)) { bool have = false; if (QualifiedCriterionList != null) { foreach (var queItem in QualifiedCriterionList) { if (queItem.DefectCode == item.Code) { have = true; if (queItem.Threshold != null || queItem.Threshold > 0) thresStr += queItem.Threshold.ToString() + ","; else thresStr += item.Threshold.ToString() + ","; break; } } } if (!have) { thresStr += item.Threshold.ToString() + ","; } } } //移除最后的, if (thresStr.Length > 0) { thresStr = thresStr.Remove(thresStr.Length - 1); } return thresStr; } /// /// 获取缺陷项阈值 /// /// /// private Dictionary getProductAreaThreshol(int HoleCount) { Dictionary dic = new Dictionary(); if (!OpenAsideDefect) { foreach (var item in QualifiedCriterionList) dic.Add(item.DefectCode, (float)(item.Size * 25.4 * 25.4 / HoleCount / HoleCount));//网目 => mm^2 } else { foreach (var item in QualifiedCriterionList) dic.Add(item.DefectCode, (float)(item.Size * 25.4 / HoleCount));//网目 => mm^2 } //dic.Add(item.DefectCode, (float)(item.Size * 25.4 * 25.4 / m.HoleCount / m.HoleCount));//网目 => mm^2 //全缺陷项 foreach (var item in DefectItemParamsList) { string code = item.Code; if (!dic.ContainsKey(code)) dic.Add(code, 0); } return dic; } public void countDefectClass(List[] list) { string className; for (int i = 0; i < list.Length; i++) { var fd = DefectItemParamsList.Find(p => p.Code == list[i][4]); if (fd != null) { string name = fd.Name; //改变list中某个元素值 var model = DefectCntInfoList.Where(c => c.Name == name).FirstOrDefault(); if (model != null) { model.DefectCnt++; } else { DefectCntInfoList.Add(new DefectCntInfo() { Code = fd.Code, Name = fd.Name, DefectCnt = 1, }); } } } } private void showDefectSmallBmps(Bitmap[] bmps, Mat[] bmps_cut, double Xmm, double Ymm, List[]>> info) { this.Invoke(new Action(() => { //加载缺陷小图 //纵向显示 int imgWidth = this.pnlBmpList.ClientSize.Width - 50; int imgHeight = (int)((bmps[0].Height * 1.0f / bmps[0].Width) * imgWidth + 0.5); int splitWidth = 20; int pnlWidth = this.pnlBmpList.ClientSize.Width; int index = (int)this.gboxDefectList.Tag; //横向显示 //int imgHeight = this.pnlBmpList.Height - 50; //int imgWidth = (int)((e.Bitmaps[0].Width * 1.0f / e.Bitmaps[0].Height) * imgHeight + 0.5); //int splitWidth = 20; //int pnlWidth = this.pnlBmpList.Height; //int index = (int)this.gboxDefectList.Tag; int len = info.Count(); int range = 100; int uselen = len > 100 ? 100 : len; for (int x = 0; x < uselen / range + 1; x++) { for (int i = 0; i < (x < (uselen / range) ? range : uselen % range); i++) { foreach (var item in info[(x * range + i)]) //单个info[x] = {"1":[["92.7542","80.85799","99.54083","86.05363","dk","0.52"]]} { //统计缺陷类型 countDefectClass(item.Value);//[["92.7542","80.85799","99.54083","86.05363","dk","0.52"]] // PictureBox picbox = new PictureBox(); picbox.Width = imgWidth; picbox.Height = imgHeight; picbox.Image = (Bitmap)bmps[(x * range + i)].Clone(); picbox.Name = "imgDefect_" + index; picbox.Tag = item.Key + "," + Xmm + "," + Ymm; //picbox.Click += new EventHandler(defectBmpBox_Click); picbox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; picbox.BorderStyle = BorderStyle.FixedSingle; //picbox.Margin = new Padding((pnlWidth - picbox.Height) / 2, splitWidth, 0, 0); picbox.Margin = new Padding((pnlWidth - picbox.Width) / 2, splitWidth, 0, 0); //picbox.MouseHover += simpleTip_MouseHover; picbox.Visible = true; //this.Invoke(new MethodInvoker(() => // { this.pnlBmpList.Controls.Add(picbox); //this.Refresh(); //this.dgvProcess.Refresh(); //this.lstLog.Refresh(); //this.statusStrip1.Refresh(); // })); break; } // index++; //Thread.Sleep(10); //Application.DoEvents(); } //Application.DoEvents(); //this.Invoke(new MethodInvoker(() => //{ //this.Refresh(); pnlBmpList.VerticalScroll.Value = pnlBmpList.VerticalScroll.Maximum; //})); //Thread.Sleep(10); } //this.Invoke(new MethodInvoker(() => //{ //this.gboxDefectList.Tag = len + (int)this.gboxDefectList.Tag; this.gboxDefectList.Tag = index; this.gboxDefectList.Text = $"缺陷图像:{index} 张"; //return 0; })); } private void AddInfer(Mat bmpLoc, Mat toDefect) { string modeltp = "pt"; if (_defectOnnxName.Replace(".onnx", "").IndexOf("rj") >= 0) modeltp = "rj"; else modeltp = "pt"; defectLib.add(new DefectTask() { stepIndex = 0, processName = "模拟测试", modelType = modeltp, drawingPagePath = "", //index = defectBmpNum++, index = defectBmpNum, srcbmp = bmpLoc.Clone(), bmp = toDefect.Clone(), //bmp = mats[0].Clone(), Xmm = 0, Ymm = 0, cut_size = new System.Drawing.Size(CutSize.Width, CutSize.Height), resize = new System.Drawing.Size(tResize.Width, tResize.Height), thresholds = Thresholds, thresholdsClass = getProductThresholClass(), recAreaThreshold = getProductAreaThreshol(tHoleCount), //qxName,面积; qxName,面积; qxName,面积; finishEvent = (res) => { int err = 0; try { string path = ""; if (res.isSucceed) { //OnAutoRuning(new RunEventArgs(liStatocStepIndex, // $"源图索引:{res.index},缺陷数:{res.defectCount},处理时间(ms):{string.Join("->", res.stopwatch.Select(i => i.ToString()).ToArray())}")); AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测完成(源图索引:{res.index}),缺陷数:{res.defectCount},处理时间(ms):{string.Join("->", res.stopwatch.Select(i => i.ToString()).ToArray())}"); //string path = ""; if (res.defectCount > 0) { err = 1; //UI显示小图 (含统计缺陷类型数量) AllDefectCount += res.informationList.Count; showDefectSmallBmps(res.bmps_tag, res.bmps_cut, res.Xmm, res.Ymm, res.informationList); err = 2; //保存小图 if (_smallImagePath != "" && Directory.Exists(_smallImagePath)) { err = 8; //2024-03-07 图片index计算 List indexList = new List(); if (res.defectInfor2RestorationDeskPage != null && res.defectInfor2RestorationDeskPage.Count > 0) foreach (var item in res.defectInfor2RestorationDeskPage) { if (indexList.Count == 0) indexList.Add(item[0]); else { if (!indexList.Contains(item[0])) indexList.Add(item[0]); } } path = _smallImagePath; path += $"\\Defect_I{res.index}_test"; err = 9; for (int i = 0; i < res.bmps_tag.Count(); i++) { res.bmps_tag[i].Save(path + $"_i{i}.bmp", ImageFormat.Bmp); Thread.Sleep(10); } err = 10; } } else//没有缺陷 { } } else { AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测失败:{res.resultInfo}"); } } catch (Exception ex) { AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测回调处理异常-{err} index:{res.index},ex={ex.Message}"); } defectBmpNumResult++; try { foreach (var item in res.bmps_cut) item.Dispose(); res.bmp.Dispose(); res.bmp = null; res.bmps_tag = null; if (res.bmpCompress != null) { res.bmpCompress.Dispose(); res.bmpCompress = null; } if (res.srcbmp != null) { res.srcbmp.Dispose(); res.srcbmp = null; } } catch (Exception ex) { AddTextEvent($"{res.stepIndex + 1}-{res.processName}", $"缺陷检测回调释放内存异常 index:{res.index},ex={ex.Message}"); } System.GC.Collect(); } }); defectBmpNum++; } #endregion #region 模型选择 private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { ReadDefectItems(this.comboBox1.Text); foreach (var item in DefectItemParamsList) { string mdname = _defectOnnxName; if ((item.Type.Split(',').Count(p => p == "-1") > 0) || (item.Type.Split(',').Count(p => p == mdname.Replace(".onnx", "")) > 0)) { DefectCountOfSizeControl userCon = new DefectCountOfSizeControl(); userCon.Code = item.Code; userCon.Title = item.Name; userCon.Threshold = (decimal)item.Threshold; //不显示,是有默认值 if (item.Show == 0) { userCon.Checked = true; userCon.Threshold = (decimal)item.Threshold; userCon.SizeValue = (decimal)item.Size; userCon.MaxDefectCount = item.MaxDefectCount; userCon.Visible = false; } else { userCon.Checked = true; userCon.Threshold = (decimal)item.Threshold; userCon.SizeValue = (decimal)item.Size; userCon.MaxDefectCount = item.MaxDefectCount; } this.flpQualifiedPannel.Controls.Add(userCon); } } } #endregion #region 小图存储 private void button2_Click(object sender, EventArgs e) { FolderBrowserDialog dlg = new FolderBrowserDialog(); //dlg.SelectedPath = (Application.StartupPath != "" ? Application.StartupPath : Path.GetFullPath(".")); if (dlg.ShowDialog() == DialogResult.OK) { textBox4.Text = dlg.SelectedPath; } else return; } #endregion #region 标定 /// /// 选择文件 /// /// 如 图像文件|*.jpg|图像文件|*.png|所有文件|*.* /// public static string SelectFile(string filter = "所有文件|*.*", string defaultPath = "") { OpenFileDialog fie = new OpenFileDialog(); fie.Title = "选择文件"; //fie.InitialDirectory = (defaultPath != "" ? defaultPath : Path.GetFullPath(".")); fie.Filter = filter; //设置文件类型 if (fie.ShowDialog() == DialogResult.OK) return fie.FileName; return ""; } private void tbTZPath_Click(object sender, EventArgs e) { TextBox tb = (TextBox)sender; string path = SelectFile("所有文件|*.*", Application.StartupPath); if (!string.IsNullOrWhiteSpace(path)) { tb.Text = path.Remove(path.Length-4,4); } } /// /// 选择目录 /// /// public static string SelectFolder(string defaultPath = "") { FolderBrowserDialog dlg = new FolderBrowserDialog(); dlg.SelectedPath = (defaultPath != "" ? defaultPath : Path.GetFullPath(".")); if (dlg.ShowDialog() == DialogResult.OK) return dlg.SelectedPath; return ""; } private void tbSFPath_Click(object sender, EventArgs e) { TextBox tb = (TextBox)sender; string path = SelectFolder(Application.StartupPath); if (!string.IsNullOrWhiteSpace(path)) { tb.Text = path; } } private void btnMarkCal_Click(object sender, EventArgs e) { CalToolsClass calToolsClass = new CalToolsClass(); double[] sizeX = new double[4]; double[] sizeY = new double[4]; double[] defectX = new double[4]; double[] defectY = new double[4]; double zpos = (double)ZPos.Value; sizeX[0] = (double)markX1.Value; sizeX[1] = (double)markX2.Value; sizeX[2] = (double)markX3.Value; sizeX[3] = (double)markX4.Value; sizeY[0] = (double)MarkY1.Value; sizeY[1] = (double)MarkY2.Value; sizeY[2] = (double)MarkY3.Value; sizeY[3] = (double)MarkY4.Value; defectX[0] = (double)qx_markX1.Value; defectX[1] = (double)qx_markX2.Value; defectX[2] = (double)qx_markX3.Value; defectX[3] = (double)qx_markX4.Value; defectY[0] = (double)qx_markY1.Value; defectY[1] = (double)qx_markY2.Value; defectY[2] = (double)qx_markY3.Value; defectY[3] = (double)qx_markY4.Value; HalconDotNet.HTuple GerBerPath = new HalconDotNet.HTuple(tbTZPath.Text); HalconDotNet.HTuple FilePath = new HalconDotNet.HTuple(tbSFPath.Text); HalconDotNet.HTuple hv_PoseMarkX_CCDSize = new HalconDotNet.HTuple(sizeX); HalconDotNet.HTuple hv_PoseMarkY_CCDSize = new HalconDotNet.HTuple(sizeY); HalconDotNet.HTuple hv_PoseMarkX_CCDDefect = new HalconDotNet.HTuple(defectX); HalconDotNet.HTuple hv_PoseMarkY_CCDDefect = new HalconDotNet.HTuple(defectY); HalconDotNet.HTuple hv_PoseZ_Size = new HalconDotNet.HTuple(zpos); var ret = (bool)calToolsClass.CCDPoseCal_Mark(GerBerPath, FilePath, sizeX, sizeY, defectX, defectY, zpos); if(ret) { MessageBox.Show("主机台标定成功!", "标定"); } else { MessageBox.Show("主机台标定失败!", "标定", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void btnNoMarkCal_Click(object sender, EventArgs e) { CalToolsClass calToolsClass = new CalToolsClass(); double[] sizeX = new double[4]; double[] sizeY = new double[4]; double[] defectX = new double[4]; double[] defectY = new double[4]; double zpos = (double)ZPos.Value; sizeX[0] = (double)markX1.Value; sizeX[1] = (double)markX2.Value; sizeX[2] = (double)markX3.Value; sizeX[3] = (double)markX4.Value; sizeY[0] = (double)MarkY1.Value; sizeY[1] = (double)MarkY2.Value; sizeY[2] = (double)MarkY3.Value; sizeY[3] = (double)MarkY4.Value; defectX[0] = (double)qx_markX1.Value; defectX[1] = (double)qx_markX2.Value; defectX[2] = (double)qx_markX3.Value; defectX[3] = (double)qx_markX4.Value; defectY[0] = (double)qx_markY1.Value; defectY[1] = (double)qx_markY2.Value; defectY[2] = (double)qx_markY3.Value; defectY[3] = (double)qx_markY4.Value; HalconDotNet.HTuple GerBerPath = new HalconDotNet.HTuple(tbTZPath.Text); HalconDotNet.HTuple FilePath = new HalconDotNet.HTuple(tbSFPath.Text); HalconDotNet.HTuple hv_PoseMarkX_CCDSize = new HalconDotNet.HTuple(sizeX); HalconDotNet.HTuple hv_PoseMarkY_CCDSize = new HalconDotNet.HTuple(sizeY); HalconDotNet.HTuple hv_PoseMarkX_CCDDefect = new HalconDotNet.HTuple(defectX); HalconDotNet.HTuple hv_PoseMarkY_CCDDefect = new HalconDotNet.HTuple(defectY); HalconDotNet.HTuple hv_PoseZ_Size = new HalconDotNet.HTuple(zpos); var ret = (bool)calToolsClass.CCDPoseCal_Corner(GerBerPath, FilePath, sizeX, sizeY, defectX, defectY, zpos); if (ret) { MessageBox.Show("主机台无Mark标定成功!", "标定"); } else { MessageBox.Show("主机台无Mark标定失败!", "标定", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void btnCal_Click(object sender, EventArgs e) { CalToolsClass calToolsClass = new CalToolsClass(); double BeforeX = (double)bX1.Value; double BeforeY = (double)bY1.Value; double AfterX = (double)aX1.Value; double AfterY = (double)aY1.Value; HalconDotNet.HTuple hv_BeforeX = new HalconDotNet.HTuple(BeforeX); HalconDotNet.HTuple hv_BeforeY = new HalconDotNet.HTuple(BeforeY); HalconDotNet.HTuple hv_AfterX = new HalconDotNet.HTuple(AfterX); HalconDotNet.HTuple hv_AfterY = new HalconDotNet.HTuple(AfterY); var ret = (bool)calToolsClass.RepairCal_2(BeforeX, BeforeY, AfterX, AfterY); if (ret) { MessageBox.Show("修复台标定成功!", "标定"); } else { MessageBox.Show("修复台标定失败!", "标定", MessageBoxButtons.OK, MessageBoxIcon.Error); } } #endregion private void button3_Click(object sender, EventArgs e) { CalToolsClass calToolsClass = new CalToolsClass(); var ret = (bool)calToolsClass.RepairCal_1(); if (ret) { MessageBox.Show("修复台标定1成功!", "标定"); } else { MessageBox.Show("修复台标定1失败!", "标定", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void tbSFPath_TextChanged(object sender, EventArgs e) { } } public class DefectCntInfo { public int Pid { get; set; } //测试项名称 public string Name { get; set; } //测试项代码 public string Code { get; set; } //个数 public int DefectCnt { get; set; } } /// /// 合格标准 /// public class QualifiedCriterion { public int Pid { get; set; } public string DefectCode { get; set; }//pp,sx,... EnumUtil.Convert2Enum /// /// 阈值 20240409 /// public double Threshold { get; set; } /// /// 尺寸 /// public float Size { get; set; } /// /// 最大允许缺陷数量 /// public int MaxDefectCount { get; set; } } }