using Bro.Common.Helper;
|
using Bro.UI.Config.Helper;
|
using Bro.UI.Config.MenuForms;
|
using Bro.UI.Model.Winform;
|
using HalconDotNet;
|
using HDisplay;
|
using HDisplay.ViewROI;
|
using System;
|
using System.Collections;
|
using System.Collections.Generic;
|
using System.ComponentModel;
|
using System.Data;
|
using System.Drawing;
|
using System.IO;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using System.Windows.Forms;
|
|
namespace Bro.UI.Config
|
{
|
[MenuNode("EditModel", "模板编辑", 4, "View2", true)]
|
public partial class FrmEditModel : MenuFrmBase
|
{
|
CanvasImage canvas = new CanvasImage();
|
HTuple modelId = null;
|
HalconDisplay hDisplay = new HalconDisplay();
|
|
CreateShapeModelConfig modelConfig = new CreateShapeModelConfig();
|
FindShapeModelConfig findConfig = new FindShapeModelConfig();
|
public FrmEditModel()
|
{
|
InitializeComponent();
|
|
//canvas.Dock = DockStyle.Fill;
|
//plImage.Controls.Clear();
|
//plImage.Controls.Add(canvas);
|
|
hDisplay.Dock = DockStyle.Fill;
|
plImage.Controls.Clear();
|
plImage.Controls.Add(hDisplay);
|
|
pgCreateModel.SelectedObject = modelConfig;
|
pgFindConfig.SelectedObject = findConfig;
|
|
dgvImages.AutoGenerateColumns = false;
|
}
|
|
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
|
{
|
if (tabControl1.SelectedIndex == 1)
|
{
|
canvas.Elements.Clear();
|
}
|
}
|
|
private void btnLoadImage_Click(object sender, EventArgs e)
|
{
|
OpenFileDialog ofd = new OpenFileDialog();
|
ofd.Multiselect = false;
|
ofd.Filter = "图片文件|*.bmp;*.png;*.jpg;*.jpeg;*.tif|所有文件|*.*";
|
|
if (ofd.ShowDialog() == DialogResult.OK)
|
{
|
//Bitmap map = (Bitmap)Bitmap.FromFile(ofd.FileName);
|
//canvas.LoadImage(map);
|
|
LoadImage(ofd.FileName);
|
}
|
}
|
|
private void LoadImage(string imgFileName)
|
{
|
hDisplay.ClearDisplay();
|
|
hDisplay.Image?.Dispose();
|
HImage hImage = new HImage();
|
hImage.ReadImage(imgFileName);
|
hDisplay.Image = hImage;
|
hDisplay.Refresh();
|
}
|
|
private void btnCreateModel_Click(object sender, EventArgs e)
|
{
|
if (hDisplay.Image == null)
|
{
|
MessageBox.Show("请先载入产品图片");
|
return;
|
}
|
|
if (hDisplay.ROIController.ROIList.Count != 1)
|
{
|
MessageBox.Show("请仅设置一个矩阵ROI");
|
return;
|
}
|
|
HOperatorSet.ClearAllShapeModels();
|
modelId = null;
|
|
HObject rectObj = null;
|
HOperatorSet.HomMat2dIdentity(out HTuple identityMatrix);
|
HTuple transformMatrix = null;
|
|
if (hDisplay.ROIController.ROIList[0] is ROIRectangle1 rect1)
|
{
|
HOperatorSet.GenRectangle1(out rectObj, rect1.Row1, rect1.Col1, rect1.Row2, rect1.Col2);
|
HOperatorSet.HomMat2dTranslate(identityMatrix, (rect1.Row1 + rect1.Row2) / 2.0, (rect1.Col1 + rect1.Col2) / 2.0, out transformMatrix);
|
}
|
else if (hDisplay.ROIController.ROIList[0] is ROIRectangle2 rect2)
|
{
|
HOperatorSet.GenRectangle2(out rectObj, rect2.MidR, rect2.MidC, rect2.Phi, rect2.Length1, rect2.Length2);
|
HOperatorSet.HomMat2dTranslate(identityMatrix, rect2.MidR, rect2.MidC, out transformMatrix);
|
}
|
else
|
{
|
MessageBox.Show("请仅设置一个矩阵ROI");
|
return;
|
}
|
|
HOperatorSet.ReduceDomain(hDisplay.Image, rectObj, out HObject imageReduced);
|
|
HTuple config = modelConfig.GetHTuple();
|
HOperatorSet.CreateShapeModel(imageReduced, config[0], config[1], config[2], config[3], config[4], config[5], config[6], config[7], out modelId);
|
|
HOperatorSet.GetShapeModelContours(out HObject modelContours, modelId, 1);
|
HOperatorSet.AffineTransContourXld(modelContours, out HObject countoursAffinTrans, transformMatrix);
|
|
hDisplay.ClearGraphicStack();
|
hDisplay.AddObjectToGraphicStack(hDisplay.Image);
|
hDisplay.AddObjectToGraphicStack(countoursAffinTrans);
|
hDisplay.Refresh();
|
|
MessageBox.Show("模板创建成功");
|
}
|
|
private void btnSaveModel_Click(object sender, EventArgs e)
|
{
|
if (modelId == null)
|
{
|
MessageBox.Show("模板句柄为空,无法保存");
|
return;
|
}
|
|
SaveFileDialog sfd = new SaveFileDialog();
|
sfd.SupportMultiDottedExtensions = false;
|
sfd.Filter = "模板文件|*.shm";
|
sfd.DefaultExt = "shm";
|
|
if (sfd.ShowDialog() == DialogResult.OK)
|
{
|
HOperatorSet.WriteShapeModel(modelId, sfd.FileName);
|
MessageBox.Show("模板保存完成");
|
}
|
}
|
|
private void btnLoadModel_Click(object sender, EventArgs e)
|
{
|
OpenFileDialog ofd = new OpenFileDialog();
|
ofd.Filter = "模板文件|*.shm";
|
ofd.Multiselect = false;
|
|
if (ofd.ShowDialog() == DialogResult.OK)
|
{
|
lblModel.Text = "";
|
|
HOperatorSet.ReadShapeModel(ofd.FileName, out modelId);
|
lblModel.Text = ofd.FileName;
|
}
|
}
|
|
private void btnLoadImageFolder_Click(object sender, EventArgs e)
|
{
|
FolderBrowserDialog fbd = new FolderBrowserDialog();
|
fbd.ShowNewFolderButton = false;
|
if (fbd.ShowDialog() == DialogResult.OK)
|
{
|
lblImageFolder.Text = fbd.SelectedPath;
|
DirectoryInfo dir = new DirectoryInfo(fbd.SelectedPath);
|
List<string> suffixList = new List<string>() { "bmp", "png", "jpg", "jpeg", "tif" };
|
var imgFileNames = dir.GetFiles().ToList().Where(u => suffixList.Any(s => u.Name.ToLower().EndsWith(s)))
|
.Select(u =>
|
{
|
TestCase tc = new TestCase();
|
tc.Name = u.Name;
|
tc.FullName = u.FullName;
|
tc.ModelMatchNum = 0;
|
return tc;
|
}).ToList();
|
|
dgvImages.DataSource = imgFileNames;
|
}
|
}
|
|
private void dgvImages_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
|
{
|
if (dgvImages.Rows[e.RowIndex].DataBoundItem is TestCase tc)
|
{
|
LoadImage(tc.FullName);
|
}
|
}
|
|
private void btnTestCurrent_Click(object sender, EventArgs e)
|
{
|
if (!CheckBeforeTest())
|
{
|
return;
|
}
|
|
RunSelected();
|
}
|
|
private bool RunSelected(int step = 0)
|
{
|
int selectedIndex = dgvImages.SelectedRows[0].Index;
|
if (step != 0)
|
{
|
int afterIndex = selectedIndex + step;
|
|
if (afterIndex < 0)
|
{
|
//MessageBox.Show("当前已经是第一项");
|
return false;
|
}
|
|
if (afterIndex >= dgvImages.Rows.Count)
|
{
|
//MessageBox.Show("当前已经是最后一项");
|
return false;
|
}
|
|
dgvImages.Rows[selectedIndex].Selected = false;
|
dgvImages.Rows[afterIndex].Selected = true;
|
dgvImages.FirstDisplayedScrollingRowIndex = afterIndex;
|
}
|
|
TestCase tc = dgvImages.SelectedRows[0].DataBoundItem as TestCase;
|
LoadImage(tc.FullName);
|
|
HTuple findConfigTuple = findConfig.GetHTuple();
|
HOperatorSet.FindShapeModel(hDisplay.Image, modelId, findConfigTuple[0], findConfigTuple[1], findConfigTuple[2], findConfigTuple[3], findConfigTuple[4], findConfigTuple[5], findConfigTuple[6], findConfigTuple[7], out HTuple rows, out HTuple cols, out HTuple angles, out HTuple scores);
|
|
tc.ModelMatchNum = scores.Length;
|
HOperatorSet.GetShapeModelContours(out HObject modelContours, modelId, 1);
|
HOperatorSet.CountObj(modelContours, out HTuple objCount);
|
HOperatorSet.HomMat2dIdentity(out HTuple identityMatrix);
|
for (int i = 0; i < tc.ModelMatchNum; i++)
|
{
|
HOperatorSet.CopyObj(modelContours, out HObject model, 1, objCount);
|
|
HOperatorSet.HomMat2dRotate(identityMatrix, angles[i], 0, 0, out HTuple transformMatrix);
|
HOperatorSet.HomMat2dTranslate(transformMatrix, rows[i], cols[i], out transformMatrix);
|
HOperatorSet.AffineTransContourXld(model, out HObject affinedContour, transformMatrix);
|
|
hDisplay.AddObjectToGraphicStack(affinedContour);
|
}
|
hDisplay.Refresh();
|
dgvImages.Invalidate();
|
|
return true;
|
}
|
|
private bool CheckBeforeTest()
|
{
|
if (modelId == null)
|
{
|
MessageBox.Show("当前未编辑或者载入模板,请创建模板或者选择模板文件载入");
|
return false;
|
}
|
|
if (dgvImages.SelectedRows.Count == 0)
|
{
|
MessageBox.Show("当前图片列表未选择测试项,请选择需要测试的图片");
|
return false;
|
}
|
|
return true;
|
}
|
|
private void btnTestNext_Click(object sender, EventArgs e)
|
{
|
if (!CheckBeforeTest())
|
{
|
return;
|
}
|
|
if (!RunSelected(1))
|
{
|
MessageBox.Show("当前已经是最后一项");
|
}
|
}
|
|
private void btnTestAll_Click(object sender, EventArgs e)
|
{
|
if (dgvImages.Rows.Count <= 0)
|
{
|
MessageBox.Show("当前无待检测图片");
|
return;
|
}
|
|
dgvImages.FirstDisplayedScrollingRowIndex = 0;
|
dgvImages.Rows[0].Selected = true;
|
|
bool flag = RunSelected(0);
|
while (flag)
|
{
|
flag = RunSelected(1);
|
}
|
MessageBox.Show("检测完成");
|
}
|
}
|
|
public class TestCase
|
{
|
public string Name { get; set; }
|
public string FullName { get; set; }
|
public int ModelMatchNum { get; set; } = 0;
|
}
|
|
#region Halcon Model
|
public abstract class StrictedListConvert : StringConverter
|
{
|
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
{
|
return true;
|
}
|
|
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
{
|
return true;
|
}
|
|
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
{
|
List<string> portNames = GetSupportedValueList();
|
return new StandardValuesCollection(portNames);
|
}
|
|
protected abstract List<string> GetSupportedValueList();
|
}
|
|
public abstract class ModelConfig
|
{
|
public abstract HTuple GetHTuple();
|
|
protected void IntCheck(ref ArrayList list, string desc)
|
{
|
if (int.TryParse(desc, out int convertValue))
|
{
|
list.Add(convertValue);
|
}
|
else
|
{
|
list.Add(desc);
|
}
|
}
|
|
protected void DoubleCheck(ref ArrayList list, string desc)
|
{
|
if (double.TryParse(desc, out double convertValue))
|
{
|
list.Add(convertValue);
|
}
|
else
|
{
|
list.Add(desc);
|
}
|
}
|
|
}
|
|
#region Create Model
|
public class NumLevelsConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "auto", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
|
}
|
}
|
|
public class AngleStartConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "-3.14", "-1.57", "-0.79", "-0.39", "-0.20", "0.0" };
|
}
|
}
|
|
public class AngleExtentConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "6.29", "3.14", "1.57", "0.79", "0.39" };
|
}
|
}
|
|
public class AngleStepConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "auto", "0.0175", "0.0349", "0.0524", "0.0698", "0.0873" };
|
}
|
}
|
|
public enum Optimization
|
{
|
auto,
|
no_pregeneration,
|
none,
|
point_reduction_high,
|
point_reduction_low,
|
point_reduction_medium,
|
pregeneration,
|
}
|
|
public enum Metric
|
{
|
use_polarity,
|
ignore_color_polarity,
|
ignore_global_polarity,
|
ignore_local_polarity,
|
}
|
|
public class ContrastConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "auto", "auto_contrast", "auto_contrast_hyst", "auto_min_size", "10", "20", "30", "40", "60", "80", "100", "120", "140", "160" };
|
}
|
}
|
|
public class MinContrastConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "auto", "1", "2", "3", "5", "7", "10", "20", "30", "40" };
|
}
|
}
|
|
public class CreateShapeModelConfig : ModelConfig
|
{
|
[Category("建立模板配置")]
|
[DisplayName("层次数量")]
|
[TypeConverter(typeof(NumLevelsConvert))]
|
public string NumLevles { get; set; } = "auto";
|
|
[Category("建立模板配置")]
|
[DisplayName("起始角度")]
|
[TypeConverter(typeof(AngleStartConvert))]
|
public string AngleStart { get; set; } = "-0.39";
|
|
[Category("建立模板配置")]
|
[DisplayName("角度范围")]
|
[TypeConverter(typeof(AngleExtentConvert))]
|
public string AngleExtent { get; set; } = "0.79";
|
|
[Category("建立模板配置")]
|
[DisplayName("角度步长")]
|
[TypeConverter(typeof(AngleStepConvert))]
|
public string AngleStep { get; set; } = "auto";
|
|
[Category("建立模板配置")]
|
[DisplayName("最优化选择")]
|
public Optimization Optimization { get; set; } = Optimization.auto;
|
|
[Category("建立模板配置")]
|
[DisplayName("极性")]
|
public Metric Metric { get; set; } = Metric.use_polarity;
|
|
[Category("建立模板配置")]
|
[DisplayName("对比度")]
|
[TypeConverter(typeof(ContrastConvert))]
|
public string Contrast { get; set; } = "auto";
|
|
[Category("建立模板配置")]
|
[DisplayName("最小对比度")]
|
[TypeConverter(typeof(MinContrastConvert))]
|
public string MinContrast { get; set; } = "auto";
|
|
public override HTuple GetHTuple()
|
{
|
ArrayList list = new ArrayList();
|
IntCheck(ref list, NumLevles);
|
DoubleCheck(ref list, AngleStart);
|
DoubleCheck(ref list, AngleExtent);
|
DoubleCheck(ref list, AngleStep);
|
|
list.Add(Optimization.ToString());
|
list.Add(Metric.ToString());
|
|
IntCheck(ref list, Contrast);
|
IntCheck(ref list, MinContrast);
|
|
HTuple hTuple = new HTuple(list.ToArray());
|
return hTuple;
|
}
|
|
}
|
#endregion
|
|
#region Find Model
|
public class AngleStartConvert_Find : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "-3.14", "-1.57", "-0.78", "-0.39", "-0.20", "0.0" };
|
}
|
}
|
|
public class AngleExtentConvert_Find : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "6.29", "3.14", "1.57", "0.78", "0.39", "0.0" };
|
}
|
}
|
|
public class MinScoreConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0" };
|
}
|
}
|
|
public class NumMatchesConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "0", "1", "2", "3", "4", "5", "10", "20" };
|
}
|
}
|
|
public class MaxOverlapConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0" };
|
}
|
}
|
|
public enum SubPixel
|
{
|
interpolation,
|
least_squares,
|
least_squares_high,
|
least_squares_very_high,
|
[Description("max_deformation 1")]
|
max_deformation_1,
|
[Description("max_deformation 2")]
|
max_deformation_2,
|
[Description("max_deformation 3")]
|
max_deformation_3,
|
[Description("max_deformation 4")]
|
max_deformation_4,
|
[Description("max_deformation 5")]
|
max_deformation_5,
|
[Description("max_deformation 6")]
|
max_deformation_6,
|
none,
|
}
|
|
public class NumLevelsConvert_Find : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
|
}
|
}
|
|
public class GreedinessConvert : StrictedListConvert
|
{
|
protected override List<string> GetSupportedValueList()
|
{
|
return new List<string>() { "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0" };
|
}
|
}
|
|
public class FindShapeModelConfig : ModelConfig
|
{
|
[Category("定位模板配置")]
|
[DisplayName("起始角度")]
|
[TypeConverter(typeof(AngleStartConvert_Find))]
|
public string AngleStart { get; set; } = "-0.39";
|
|
[Category("定位模板配置")]
|
[DisplayName("角度范围")]
|
[TypeConverter(typeof(AngleExtentConvert_Find))]
|
public string AngleExtent { get; set; } = "0.78";
|
|
[Category("定位模板配置")]
|
[DisplayName("最小得分")]
|
[TypeConverter(typeof(MinScoreConvert))]
|
public string MinScore { get; set; } = "0.7";
|
|
[Category("定位模板配置")]
|
[DisplayName("匹配数量")]
|
[TypeConverter(typeof(NumMatchesConvert))]
|
public string NumMatches { get; set; } = "0";
|
|
[Category("定位模板配置")]
|
[DisplayName("最大重叠设置")]
|
[TypeConverter(typeof(MaxOverlapConvert))]
|
public string MaxOverlap { get; set; } = "0.5";
|
|
[Category("定位模板配置")]
|
[DisplayName("亚像素设置")]
|
public SubPixel SubPixel { get; set; } = SubPixel.least_squares;
|
|
[Category("定位模板配置")]
|
[DisplayName("层次数量")]
|
[TypeConverter(typeof(NumLevelsConvert_Find))]
|
public string NumLevles { get; set; } = "0";
|
|
[Category("定位模板配置")]
|
[DisplayName("Greediness")]
|
[TypeConverter(typeof(GreedinessConvert))]
|
public string Greediness { get; set; } = "0.9";
|
|
public override HTuple GetHTuple()
|
{
|
ArrayList list = new ArrayList();
|
DoubleCheck(ref list, AngleStart);
|
DoubleCheck(ref list, AngleExtent);
|
DoubleCheck(ref list, MinScore);
|
IntCheck(ref list, NumMatches);
|
DoubleCheck(ref list, MaxOverlap);
|
list.Add(SubPixel.GetEnumDescription());
|
IntCheck(ref list, NumLevles);
|
DoubleCheck(ref list, Greediness);
|
|
HTuple hTuple = new HTuple(list.ToArray());
|
return hTuple;
|
}
|
}
|
#endregion
|
#endregion
|
}
|