前言
本文主要实现基于C#实现视觉定位的基础框架,与前面的python版、MFC版、Qt版一样,可供不同的开发者进行学习使用。
本文也是包括多模板算法匹配:基于形状、基于灰度、基于相关性、基于可变比例等。
编程环境:dotnet4.7
halcon20.05
IDE: VisualStudio 2022
演示视频
本次项目的效果视频:
C# 调用halcon实现模板匹配
一、项目文件目录讲解
从项目文件内容可以分为:
1、界面Calibration:这里是相机标定(像素坐标与机器人坐标映射)
2、界面CameraSetting:这里是相机设置(连接相机并把图像传递到主界面)
3、界面ImageMatching:这里是软件主界面
4、PublicFunc.cs:这里是自己封装的标准函数,方便自己调用
5、StructData.cs:这里是定义了标准的结构体,为了后边的变量使用
二、软件界面设置
C#版的基于winform是比较简单的,界面拖拉拽基本可以完成
1.CameraSetting.cs界面设置
如图设置相机设置界面即可:
2.Calibration.cs界面设置
这里是标定页面的设置,与前面的qt版、MFC版、python版界面类似:
3.主界面设置
这里按步骤进行界面设置即可:
这里主界面基本设置完成,这里因为项目是使用4k屏幕设置的,因此显示有点问题,你们可以根据自己电脑设置即可,这个不是必须。
三、文件算法解析
这里会对上边项目工程的一些文件进行解释
1.StructData.cs文件的重要函数
这文件主要是定义了一系列用到的结构体:
//十字线
public struct CenterLine
{
public double center_X;
public double center_Y;
public double center_R;
public double timer;
public HObject Line1;
public HObject Line2;
public CenterLine(double default_x = -999.999, double default_y = -999.999, double default_r = -999.999, HObject line1 = null, HObject line2 = null, double t = 0)
{
center_X = default_x;
center_Y = default_y;
center_R = default_r;
if (line1 == null)
Line1 = new HObject();
else
Line1 = line1.Clone();
if (line2 == null)
Line2 = new HObject();
else
Line2 = line2.Clone();
timer = t;
}
}
//视觉识别的返回的结构数组
public struct VisionPoint
{
public double x;
public double y;
public double r;
public double score;
public double scale;
public HObject shape;
public HObject ROI_shape;
public VisionPoint(double default_x = -999.999, double default_y = -999.999, double default_r = -999.999, double default_s = -999.999, HObject a = null, HObject b = null, double default_scale = -999.999)
{
x = default_x;
y = default_y;
r = default_r;
scale = default_scale;
score = default_s;
shape = a;
ROI_shape = b;
}
};
//当前软件的鼠标状态
struct CurentMouseStatus
{
public bool MouseMoveFlag;
public bool MouseDownFlag;
public bool MouseScaleFlag;
public bool MouseBrushDrawingFlag;
public double old_X;
public double old_Y;
public double Scale;
public int MouseScaleNum;
};
//当前软件控制的一些变量信息
class CurrentControl
{
public HWindow HalconWindow;
public HObject ho_Image;
public HTuple Image_Width;
public HTuple Image_Height;
public int ROIRunType;
public HObject CurrentROI;
public int TemplateAlgorithm;
public int CurrentModelNum;
public double setPartPosition;
public CurentMouseStatus mouseStatus;
public Queue<VisionPoint> dataRes;
public CenterLine lineDataRes;
public CurrentControl()
{
HalconWindow = null;
HOperatorSet.GenEmptyObj(out ho_Image);
HOperatorSet.GenEmptyRegion(out CurrentROI);
Image_Width = new HTuple(-1);
Image_Height = new HTuple(-1);
ROIRunType = 0;
TemplateAlgorithm = 0;
CurrentModelNum = 0;
setPartPosition = 0.0;
mouseStatus = new CurentMouseStatus();
mouseStatus.MouseMoveFlag = false;
mouseStatus.MouseDownFlag = false;
mouseStatus.MouseScaleFlag = false;
mouseStatus.MouseBrushDrawingFlag = false;
mouseStatus.old_X = 0.0;
mouseStatus.old_Y = 0.0;
mouseStatus.Scale = 0.95;
mouseStatus.MouseScaleNum = 0;
dataRes = new Queue<VisionPoint>();
}
};
//相机变量属性
class CameraCom
{
public HTuple hv_AcqHandle;
public HObject CutROI;
public HObject ho_Image;
public HTuple ho_Width;
public HTuple ho_Height;
public HWindow HalconWindow;
public CameraCom()
{
hv_AcqHandle = new HTuple();
hv_AcqHandle.Dispose();
HOperatorSet.GenEmptyObj(out ho_Image);
HOperatorSet.GenEmptyRegion(out CutROI);
ho_Width = new HTuple(-1);
ho_Height = new HTuple(-1);
HalconWindow = null;
}
};
//模板参数变量,这里有序列化操作,因此有[Serializable]
[Serializable]
struct ModelCom
{
public bool EffectiveFlag; //-该模板号是否有模板 false
public int TemplateAlgorithm; //-该模板号使用的匹配算法 0
public HObject h_OriginImg; //-创建模板的原图 GenEmptyObj()
public HObject h_OriginRegion; //-创建模板的ROI GenEmptyRegion()
public int startAngle; //-起始角度 -180
public int endAngle; //-最终角度 180
public int ModelLevel; //-模型对比度 -1
public int Score; //-匹配分数 50
public int DeformationNum; //-允许变形
public int MatchNum; //-期待匹配数,0代表全部匹配 1
public HTuple hv_ModelID; //-模板ID
public int FindModelTimeOut; //-模板匹配超时 5000
public HObject ShapeModelRegions; //-模板轮廓(形状匹配有)
public HTuple hv_Orgin_Row; //-模板锚点的行
public HTuple hv_Orgin_Column; //-模板锚点的列
public HTuple hv_Orgin_Angle; //-模板锚点的角度
public HTuple hv_Target_Row; //-修改后锚点的行 -1
public HTuple hv_Target_Column; //-修改后锚点的列 -1
public HObject h_SearchROI; //-搜索区域
};
//标定参数变量,这里有序列化操作,因此有[Serializable]
[Serializable]
public struct PositionData
{
public HTuple image_X;
public HTuple image_Y;
public HTuple Robot_X;
public HTuple Robot_Y;
public HHomMat2D RobotHommat;
public int PointSum;
public bool TransFlag;
}
//解决方案变量,也是使用结构体数组
[Serializable]
class SlnModelCom
{
public const int MaxModelNum = 10;
public ModelCom[] MyModel = new ModelCom[MaxModelNum];
public const int PositionDataNum = 5;
public PositionData[] myPositionData = new PositionData[PositionDataNum];
public SlnModelCom()
{
for(int i=0;i< PositionDataNum;i++)
{
myPositionData[i].image_X = new HTuple();
myPositionData[i].image_Y = new HTuple();
myPositionData[i].Robot_X = new HTuple();
myPositionData[i].Robot_Y = new HTuple();
myPositionData[i].RobotHommat = new HHomMat2D();
myPositionData[i].PointSum = 0;
myPositionData[i].TransFlag = false;
}
for (int i = 0; i < MaxModelNum; i++)
{
MyModel[i].EffectiveFlag = false;
MyModel[i].TemplateAlgorithm = 0;
HOperatorSet.GenEmptyObj(out MyModel[i].h_OriginImg);
HOperatorSet.GenEmptyObj(out MyModel[i].h_OriginRegion);
MyModel[i].startAngle = -180;
MyModel[i].endAngle = 180;
MyModel[i].ModelLevel = -1;
MyModel[i].Score = 50;
MyModel[i].DeformationNum = 0;
MyModel[i].MatchNum = 1;
MyModel[i].hv_ModelID = null;
MyModel[i].FindModelTimeOut = 5000;
HOperatorSet.GenEmptyObj(out MyModel[i].ShapeModelRegions);
MyModel[i].hv_Orgin_Row = new HTuple(-1);
MyModel[i].hv_Orgin_Column = new HTuple(-1);
MyModel[i].hv_Orgin_Angle = new HTuple(-1);
MyModel[i].hv_Target_Row = new HTuple(-1);
MyModel[i].hv_Target_Column = new HTuple(-1);
HOperatorSet.GenEmptyObj(out MyModel[i].h_SearchROI);
}
}
}
2.PublicFunc.cs文件的重要函数
这里只是个人习惯的一些函数,不是必要的
3.CameraSetting.cs文件的重要函数
此文件主要是相机连接的参数
1.首先定义类成员
public delegate bool CameraImageHW(HObject hImage);
public event CameraImageHW CameraImageHWEvent;
private CameraCom myCameraCom = new CameraCom();
private bool ChangeValueFlag = false;
private bool DrawROIFlag = false;
2.查找相机
private void button_SearchCamera_Click(object sender, EventArgs e)
{
if (IsConnectCamera() || button_ConnectCamera.Text == "断开")
{
button_ConnectCamera_Click(null, null);
}
Combox_CameraList.Items.Clear();
HTuple hv_Information = new HTuple(), hv_ValueList = new HTuple();
HTuple hv_Length = new HTuple();
string[] deviceNames = { "GigEVision2"};
foreach (string names in deviceNames)
{
try
{
HOperatorSet.InfoFramegrabber(names, "device", out hv_Information, out hv_ValueList);
HOperatorSet.TupleLength(hv_ValueList, out hv_Length);
int length1 = Convert.ToInt32(hv_Length.ToString());
for (int i = 0; i < length1; i++)
{
string strDevice = hv_ValueList[i].S;
if (strDevice.Equals("default"))
{
break;
}
if (names == "GigEVision2")
{
string[] datalist = strDevice.Split('|');
foreach (string data in datalist)
{
if (data.Contains("unique_name"))
{
strDevice = data.Replace(" ", "").Split(':')[1];
break;
}
}
}
strDevice = names + ":" + strDevice;
Combox_CameraList.Items.Add(strDevice);
}
}
catch
{
}
}
if (Combox_CameraList.Items.Count > 0)
{
Combox_CameraList.SelectedIndex = 0;
button_ConnectCamera.Enabled = true;
}
else
button_ConnectCamera.Enabled = false;
}
3.连接相机
private bool OpenCamrea()
{
HTuple Information, ValueList;
string selctCam = Combox_CameraList.SelectedItem.ToString();
int StrSplit = selctCam.IndexOf(":");
string CameraType = selctCam.Substring(0, StrSplit);
string CamreaName = selctCam.Substring(StrSplit + 1).TrimEnd();
try
{
HOperatorSet.InfoFramegrabber(CameraType, "defaults", out Information, out ValueList);
HOperatorSet.OpenFramegrabber(CameraType, ValueList[0], ValueList[1], ValueList[2], ValueList[3], ValueList[4], ValueList[5], ValueList[6], ValueList[7], Combox_CameraColor.Text, ValueList[9], ValueList[10], ValueList[11], CamreaName, 0, ValueList[13], out myCameraCom.hv_AcqHandle);
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "clear_buffer", "enable");
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "[Stream]StreamAuxiliaryBufferCount", 0);
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "[Stream]StreamBufferHandlingMode", "NewestOnly");
HOperatorSet.GrabImageStart(myCameraCom.hv_AcqHandle, -1);
HOperatorSet.GrabImageAsync(out myCameraCom.ho_Image, myCameraCom.hv_AcqHandle, -1);
HOperatorSet.GetImageSize(myCameraCom.ho_Image, out myCameraCom.ho_Width, out myCameraCom.ho_Height);
CameraImageHWEvent(myCameraCom.ho_Image);
if (checkBox_AutoSize.Checked == true)
{
myCameraCom.HalconWindow.SetPart(0, 0, myCameraCom.ho_Height.I - 1, myCameraCom.ho_Width.I - 1);
}
else
{
ImageFunc.MySetPart(ref hWindowControl, ref myCameraCom.HalconWindow, myCameraCom.ho_Height, myCameraCom.ho_Width);
}
return true;
}
catch
{
MessageBox.Show("相机连接失败");
CloseCamera();
return false;
}
}
4.断开相机
private void CloseCamera()
{
PublicFunc.Delay(500);
try
{
HOperatorSet.GrabImage(out myCameraCom.ho_Image, myCameraCom.hv_AcqHandle);
PublicFunc.Delay(200);
HOperatorSet.CloseFramegrabber(myCameraCom.hv_AcqHandle);
myCameraCom.hv_AcqHandle = new HTuple();
myCameraCom.hv_AcqHandle.Dispose();
}
catch (HalconException HDevExpDefaultException)
{
throw HDevExpDefaultException;
}
}
5.获取相机属性
获取相机的像素格式
private void GetCameraPixelFormat()
{
try
{
HTuple hv_Value, hv_Length;
HOperatorSet.GetFramegrabberParam(myCameraCom.hv_AcqHandle, "PixelFormat", out hv_Value);
HOperatorSet.TupleLength(hv_Value, out hv_Length);
int length = (int)hv_Length[0];
if (length < 1)
{
comboBox_Combox_CameraPixelFormat.Enabled = false;
comboBox_Combox_CameraPixelFormat.SelectedIndex = -1;
}
else
{
int index = 0;
for (; index < comboBox_Combox_CameraPixelFormat.Items.Count; index++)
{
string data = comboBox_Combox_CameraPixelFormat.GetItemText(comboBox_Combox_CameraPixelFormat.Items[index]);
if (data == hv_Value[0].S)
{
comboBox_Combox_CameraPixelFormat.SelectedIndex = index;
break;
}
}
if (index == comboBox_Combox_CameraPixelFormat.Items.Count)
{
comboBox_Combox_CameraPixelFormat.Items.Add(hv_Value[0].S);
comboBox_Combox_CameraPixelFormat.SelectedIndex = comboBox_Combox_CameraPixelFormat.Items.Count - 1;
}
}
}
catch { }
}
获取相机的颜色空间
private void GetCameraColorSpace()
{
try
{
HTuple hv_Value, hv_Length;
HOperatorSet.GetFramegrabberParam(myCameraCom.hv_AcqHandle, "color_space", out hv_Value);
HOperatorSet.TupleLength(hv_Value, out hv_Length);
int length = (int)hv_Length[0];
if (length < 1)
{
Combox_CameraColor.SelectedIndex = 0;
}
else
{
int index = 0;
for (; index < Combox_CameraColor.Items.Count; index++)
{
string data = Combox_CameraColor.GetItemText(Combox_CameraColor.Items[index]);
if (data == hv_Value[0].S)
{
Combox_CameraColor.SelectedIndex = index;
break;
}
}
if (index == Combox_CameraColor.Items.Count)
{
Combox_CameraColor.Items.Add(hv_Value[0].S);
Combox_CameraColor.SelectedIndex = Combox_CameraColor.Items.Count - 1;
}
}
}
catch { }
}
获取相机的曝光时间
private void GetCameraExposure()
{
try
{
HTuple hv_Value;
HOperatorSet.GetFramegrabberParam(myCameraCom.hv_AcqHandle, "ExposureTime", out hv_Value);
trackBar_ExposeTimeOut.Minimum = 0;
trackBar_ExposeTimeOut.Maximum = 9999;
trackBar_ExposeTimeOut.Value = Convert.ToInt32(hv_Value[0].D / 1000);
label_ExposeTimeOut.Text = trackBar_ExposeTimeOut.Value.ToString();
checkBox_AutoExposeTimeOut.Checked = false;
}
catch { }
}
获取相机的增益
private void GetCameraGain()
{
try
{
HTuple hv_Value;
HOperatorSet.GetFramegrabberParam(myCameraCom.hv_AcqHandle, "Gain", out hv_Value);
trackBar_Gain.Minimum = 0;
trackBar_Gain.Maximum = 17;
trackBar_Gain.Value = Convert.ToInt32(hv_Value[0].D);
label_Gain.Text = trackBar_Gain.Value.ToString();
}
catch
{
}
}
6.设置相机属性
设置相机的像素格式
private void comboBox_Combox_CameraPixelFormat_SelectionChangeCommitted(object sender, EventArgs e)
{
if (IsConnectCamera())
{
try
{
HOperatorSet.GrabImage(out myCameraCom.ho_Image, myCameraCom.hv_AcqHandle);
string data = comboBox_Combox_CameraPixelFormat.Text;
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "PixelFormat", data);
HOperatorSet.GrabImageStart(myCameraCom.hv_AcqHandle, -1);
HOperatorSet.GrabImageAsync(out myCameraCom.ho_Image, myCameraCom.hv_AcqHandle, -1);
}
catch { }
GetCameraPixelFormat();
}
}
设置相机颜色空间
private void Combox_CameraColor_SelectionChangeCommitted(object sender, EventArgs e)
{
if (IsConnectCamera())
{
try
{
string data = Combox_CameraColor.Text;
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "color_space", data);
}
catch { }
GetCameraColorSpace();
}
}
设置相机曝光时间
private void trackBar_ExposeTimeOut_Scroll(object sender, EventArgs e)
{
if (checkBox_AutoExposeTimeOut.Checked == false && ChangeValueFlag == false)
{
ChangeValueFlag = true;
string exposure = trackBar_ExposeTimeOut.Value.ToString();
try
{
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "ExposureTime", Convert.ToInt32(exposure) * 1000);
}
catch { }
try
{
HTuple hv_Value;
HOperatorSet.GetFramegrabberParam(myCameraCom.hv_AcqHandle, "ExposureTime", out hv_Value);
trackBar_ExposeTimeOut.Value = Convert.ToInt32(hv_Value[0].D / 1000);
label_ExposeTimeOut.Text = trackBar_ExposeTimeOut.Value.ToString();
}
catch { }
ChangeValueFlag = false;
}
}
设置相机自动曝光
private void checkBox_AutoExposeTimeOut_Click(object sender, EventArgs e)
{
if (checkBox_AutoExposeTimeOut.Checked)
{
try
{
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "ExposureAuto", "Once");
PublicFunc.Delay(500);
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "ExposureAuto", "Off");
}
catch
{ }
checkBox_AutoExposeTimeOut.Checked = false;
}
GetCameraExposure();
}
设置增益
private void trackBar_Gain_Scroll(object sender, EventArgs e)
{
if (ChangeValueFlag == false)
{
ChangeValueFlag = true;
string gain = trackBar_Gain.Value.ToString();
try
{
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "Gain", Convert.ToInt32(gain));
}
catch { }
GetCameraGain();
ChangeValueFlag = false;
}
}
设置白平衡
private void checkBox_AutoWhiteBalence_Click(object sender, EventArgs e)
{
if (checkBox_AutoWhiteBalence.Checked == true)
{
try
{
HOperatorSet.SetFramegrabberParam(myCameraCom.hv_AcqHandle, "white_balance", "auto");
}
catch
{
}
PublicFunc.Delay(500);
checkBox_AutoWhiteBalence.Checked = false;
}
}
4.Calibration.cs文件的重要函数
这里主要是相机坐标与机器人坐标标定
1.生成标定数据
private void button_SaveTransData_Click(object sender, EventArgs e)
{
int SelectedIndex = comboBox_Position.SelectedIndex;
if (SelectedIndex <= -1)
{
MessageBox.Show("请选择工位编号");
return;
}
int RowsCount = dataGridView_DataVision.Rows.Count - 1;
int index = 0;
for (; index < RowsCount; index++)
{
if (dataGridView_DataVision.Rows[index].Cells[0].Value == null ||
dataGridView_DataVision.Rows[index].Cells[1].Value == null ||
dataGridView_DataVision.Rows[index].Cells[2].Value == null ||
dataGridView_DataVision.Rows[index].Cells[3].Value == null)
{
DataGridViewRow row = dataGridView_DataVision.Rows[index];
dataGridView_DataVision.Rows.Remove(row);
index--;
RowsCount--;
}
}
dataGridView_DataVision_RowsAdded(null, null);
RowsCount = dataGridView_DataVision.Rows.Count - 1;
myPositionData[SelectedIndex].image_X = new HTuple();
myPositionData[SelectedIndex].image_Y = new HTuple();
myPositionData[SelectedIndex].Robot_X = new HTuple();
myPositionData[SelectedIndex].Robot_Y = new HTuple();
for (index = 0; index < RowsCount; index++)
{
HOperatorSet.TupleConcat(myPositionData[SelectedIndex].image_X, new HTuple(Convert.ToDouble(dataGridView_DataVision.Rows[index].Cells[0].Value.ToString())), out myPositionData[SelectedIndex].image_X);
HOperatorSet.TupleConcat(myPositionData[SelectedIndex].image_Y, new HTuple(Convert.ToDouble(dataGridView_DataVision.Rows[index].Cells[1].Value.ToString())), out myPositionData[SelectedIndex].image_Y);
HOperatorSet.TupleConcat(myPositionData[SelectedIndex].Robot_X, new HTuple(Convert.ToDouble(dataGridView_DataVision.Rows[index].Cells[2].Value.ToString())), out myPositionData[SelectedIndex].Robot_X);
HOperatorSet.TupleConcat(myPositionData[SelectedIndex].Robot_Y, new HTuple(Convert.ToDouble(dataGridView_DataVision.Rows[index].Cells[3].Value.ToString())), out myPositionData[SelectedIndex].Robot_Y);
}
myPositionData[SelectedIndex].PointSum = RowsCount;
if (RowsCount < 3)
{
MessageBox.Show("数据量不够,最少3个点");
myPositionData[SelectedIndex].RobotHommat = new HHomMat2D();
myPositionData[SelectedIndex].TransFlag = false;
}
else
{
try
{
myPositionData[SelectedIndex].RobotHommat = new HHomMat2D();
myPositionData[SelectedIndex].RobotHommat.VectorToHomMat2d(myPositionData[SelectedIndex].image_X, myPositionData[SelectedIndex].image_Y, myPositionData[SelectedIndex].Robot_X, myPositionData[SelectedIndex].Robot_Y);
myPositionData[SelectedIndex].TransFlag = true;
MessageBox.Show(string.Format("机器人标定完成,工位:{0}", SelectedIndex + 1));
}
catch
{
myPositionData[SelectedIndex].RobotHommat = new HHomMat2D();
myPositionData[SelectedIndex].TransFlag = false;
MessageBox.Show("数据异常");
}
}
WriteData(SelectedIndex, SelectedIndex + 1);
comboBox_Position.Items.Clear();
for (int i = 0; i < SlnModelCom.PositionDataNum; i++)
comboBox_Position.Items.Add("仿射矩阵:" + (i + 1).ToString() + " <----> " + (myPositionData[i].TransFlag ? "OK" : "NG"));
comboBox_Position.SelectedIndex = SelectedIndex;
CalibDataSendEvent(SelectedIndex, myPositionData[SelectedIndex]);
}
2.获取标定数据
public bool PixelToRobot(int ID, double img_x, double img_y, out double robot_x, out double robot_y)
{
robot_x = robot_y = -999.999;
if (ID < 0 || ID >= SlnModelCom.PositionDataNum)
return false;
if (myPositionData[ID].TransFlag == true)
{
try
{
robot_x = myPositionData[ID].RobotHommat.AffineTransPoint2d(img_x, img_y, out robot_y);
return true;
}
catch
{
return false;
}
}
return false;
}
5.ImageMatching.cs文件的重要函数
1.定义类变量及一些属性
[DllImport("kernel32.dll",CallingConvention = CallingConvention.Winapi)]
extern static int GetTickCount();
private string FilePath;
CurrentControl MyCurrentControl = new CurrentControl();
SlnModelCom MySlnData = null;
CameraSetting MyCameraSetting;
Calibration MyCalibration;
SoftKeyPWD myDog = null;
TcpServer RobotTcp;
private void Check(bool res)
{
if (res == false)
{
System.Environment.Exit(System.Environment.ExitCode);
}
}
2.初始化halcon控件
HOperatorSet.SetSystem("clip_region", new HTuple("false"));
ReadModel(0, SlnModelCom.MaxModelNum);
HOperatorSet.SetSystem("clip_region", new HTuple("true"));
updateModelComboBox(0);
MyCurrentControl.HalconWindow = hWindowControl_Show.HalconWindow;
HDevWindowStack.Push(MyCurrentControl.HalconWindow);
HOperatorSet.SetDraw(HDevWindowStack.GetActive(), "margin");
MyCurrentControl.HalconWindow.SetColor("red");
MyCurrentControl.HalconWindow.SetLineWidth(1);
MyCurrentControl.HalconWindow.SetWindowParam("background_color", "gray");
HOperatorSet.SetWindowAttr("background_color", "gray");
3.载入方案
Object tempData;
DataSave.DeSerializeObject(fd.FileName.ToString(), out tempData);
MySlnData = (SlnModelCom)tempData;
for (int i = 0; i < SlnModelCom.PositionDataNum; i++)
{
MyCalibration.SlnUpdateCalib(i, MySlnData.myPositionData[i]);
}
updateModelComboBox(0);
ShowMessageLabel.Text = "载入方案成功!";
4.保存方案
DataSave.SerializeObject(sfd.FileName, MySlnData);
ShowMessageLabel.Text = "保存方案成功!";
5.绘制ROI区域
主入口
private void DrawRegion(int DrawROIType)
{
MyCurrentControl.dataRes = new Queue<VisionPoint>();
MyCurrentControl.lineDataRes = new CenterLine();
if (ImageFunc.IsEmptyImage(MyCurrentControl.ho_Image))
return;
MyCurrentControl.mouseStatus.MouseBrushDrawingFlag = true;
menuStrip1.Enabled = false;
HObject ROITemp = new HObject();
ShowMessageLabel.Text = "左键绘制,右键确定";
ReDraw(true);
HOperatorSet.SetColor(MyCurrentControl.HalconWindow, "red");
switch (DrawROIType)
{
case 0: //绘制矩形
HTuple row1, row2, col1, col2;
HOperatorSet.DrawRectangle1(MyCurrentControl.HalconWindow, out row1, out row2, out col1, out col2);
HOperatorSet.GenRectangle1(out ROITemp, row1, row2, col1, col2);
break;
case 1: //绘制旋转矩形
HTuple row, col, length1, length2, phi;
HOperatorSet.DrawRectangle2(MyCurrentControl.HalconWindow, out row, out col, out phi, out length1, out length2);
HOperatorSet.GenRectangle2(out ROITemp, row, col, phi, length1, length2);
break;
case 2: //绘制圆形
HTuple row_1, col_1, radius_1;
HOperatorSet.DrawCircle(MyCurrentControl.HalconWindow, out row_1, out col_1, out radius_1);
HOperatorSet.GenCircle(out ROITemp, row_1, col_1, radius_1);
break;
case 3: //绘制椭圆
HTuple row_e, col_e, phi_e, radius_e1, radius_e2;
HOperatorSet.DrawEllipse(MyCurrentControl.HalconWindow, out row_e, out col_e, out phi_e, out radius_e1, out radius_e2);
HOperatorSet.GenEllipse(out ROITemp, row_e, col_e, phi_e, radius_e1, radius_e2);
break;
case 4: //绘制任意区域
HOperatorSet.DrawRegion(out ROITemp, MyCurrentControl.HalconWindow);
break;
}
switch (MyCurrentControl.ROIRunType)
{
case 0:
HOperatorSet.Union2(MyCurrentControl.CurrentROI, ROITemp, out MyCurrentControl.CurrentROI);
break;
case 1:
HOperatorSet.Difference(MyCurrentControl.CurrentROI, ROITemp, out MyCurrentControl.CurrentROI);
break;
case 2:
HOperatorSet.Intersection(MyCurrentControl.CurrentROI, ROITemp, out MyCurrentControl.CurrentROI);
break;
}
menuStrip1.Enabled = true;
ReDraw(true);
ShowMessageLabel.Text = "绘制完成";
MyCurrentControl.mouseStatus.MouseBrushDrawingFlag = false;
}
6.创建模板
主创建模板入口
private void Action_CreateModel_Click(object sender, EventArgs e)
{
MyCurrentControl.dataRes = new Queue<VisionPoint>();
MyCurrentControl.lineDataRes = new CenterLine();
if (ImageFunc.IsEmptyImage(MyCurrentControl.ho_Image) || ImageFunc.IsEmptyRegion(MyCurrentControl.CurrentROI))
{
ShowMessageLabel.Text = "无原图或者ROI";
return;
}
if (MySlnData.MyModel[MyCurrentControl.CurrentModelNum].EffectiveFlag == true)
{
DialogResult result = MessageBox.Show("已经存在模板是否替换?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
if (result == DialogResult.Cancel)
{
return;
}
}
InitMyModel(MyCurrentControl.CurrentModelNum, MyCurrentControl.CurrentModelNum + 1);
MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginImg = MyCurrentControl.ho_Image.Clone();
MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginRegion = MyCurrentControl.CurrentROI.Clone();
MySlnData.MyModel[MyCurrentControl.CurrentModelNum].TemplateAlgorithm = MyCurrentControl.TemplateAlgorithm;
updateVisionParams(MyCurrentControl.CurrentModelNum);
bool Res = false;
ShowMessageLabel.Text = "正在创建模板...";
if (MyCurrentControl.TemplateAlgorithm == 0)
Res = CreateShapeModel();
else if (MyCurrentControl.TemplateAlgorithm == 1)
Res = CreateGrayModel();
else if (MyCurrentControl.TemplateAlgorithm == 2)
Res = CreateNCCModel();
else if (MyCurrentControl.TemplateAlgorithm == 3)
Res = CreateChangeShapeModel();
else
{ }
if (Res == false)
{
InitMyModel(MyCurrentControl.CurrentModelNum, MyCurrentControl.CurrentModelNum + 1);
ShowMessageLabel.Text = "模板创建失败!";
}
else
{
DataSave.SerializeObject(FilePath + string.Format(@"/Model_{0}.dat", MyCurrentControl.CurrentModelNum + 1), MySlnData.MyModel[MyCurrentControl.CurrentModelNum]);
ShowMessageLabel.Text = "模板创建成功!";
}
updateModelComboBox(MyCurrentControl.CurrentModelNum);
}
以灰度匹配为例
private bool CreateGrayModel()
{
if (ImageFunc.IsEmptyImage(MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginImg) || ImageFunc.IsEmptyRegion(MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginRegion))
return false;
HObject CreateModelImage = MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginImg.Clone();
HObject hv_ImageReduced;
HTuple hv_pi = ((new HTuple(0.0)).TupleAcos()) * 2;
HOperatorSet.ReduceDomain(CreateModelImage, MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginRegion, out hv_ImageReduced);
try
{
HOperatorSet.CreateTemplateRot(hv_ImageReduced, 4, (new HTuple(slider_StartAngel.Value)).TupleRad(), (new HTuple(slider_EndAngel.Value)).TupleRad(), 0.0982, "sort", "original", out MySlnData.MyModel[MyCurrentControl.CurrentModelNum].hv_ModelID);
}
catch { return false; }
MySlnData.MyModel[MyCurrentControl.CurrentModelNum].EffectiveFlag = true;
HOperatorSet.AreaCenter(MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginRegion, out MySlnData.MyModel[MyCurrentControl.CurrentModelNum].hv_Orgin_Angle, out MySlnData.MyModel[MyCurrentControl.CurrentModelNum].hv_Orgin_Row, out MySlnData.MyModel[MyCurrentControl.CurrentModelNum].hv_Orgin_Column);
HOperatorSet.GenEmptyRegion(out MyCurrentControl.CurrentROI);
ReDraw(true);
MyCurrentControl.HalconWindow.SetColor("blue");
HObject XLD_Region;
HOperatorSet.GenContourRegionXld(MySlnData.MyModel[MyCurrentControl.CurrentModelNum].h_OriginRegion, out XLD_Region, "border");
MyCurrentControl.HalconWindow.DispObj(XLD_Region);
HObject Cross;
HOperatorSet.GenCrossContourXld(out Cross, MySlnData.MyModel[MyCurrentControl.CurrentModelNum].hv_Orgin_Row, MySlnData.MyModel[MyCurrentControl.CurrentModelNum].hv_Orgin_Column, MyCurrentControl.Image_Width / 24, 0);
MyCurrentControl.HalconWindow.DispObj(Cross);
return true;
}
7.查找模板
查找模板主入口
private Queue<VisionPoint> FindModelRes(HObject img, int model_num)
{
Queue<VisionPoint> pp = new Queue<VisionPoint>();
MyCurrentControl.lineDataRes = new CenterLine();
HOperatorSet.GenEmptyRegion(out MyCurrentControl.CurrentROI);
MyCurrentControl.CurrentModelNum = model_num;
if (MySlnData.MyModel[model_num].TemplateAlgorithm == 0)
pp = FindModel_Shape(img, model_num);
else if (MySlnData.MyModel[model_num].TemplateAlgorithm == 1)
pp = FindModel_Gray(img, model_num);
else if (MySlnData.MyModel[model_num].TemplateAlgorithm == 2)
pp = FindModel_NCC(img, model_num);
else if (MySlnData.MyModel[MyCurrentControl.CurrentModelNum].TemplateAlgorithm == 3)
pp = FindModel_ChangeShape(img, model_num);
else { }
return pp;
}
以灰度匹配为例查找模板文章来源:https://www.toymoban.com/news/detail-460612.html
private Queue<VisionPoint> FindModel_Gray(HObject img, int currentModelNum)
{
Queue<VisionPoint> pp = new Queue<VisionPoint>();
MyCurrentControl.lineDataRes = new CenterLine();
if (ImageFunc.IsEmptyImage(img) || !MySlnData.MyModel[currentModelNum].EffectiveFlag || currentModelNum < 0 || currentModelNum > SlnModelCom.MaxModelNum)
{
ShowMessageLabel.Text = "无模板或无原图或模板号错误";
return pp;
}
HTuple row1 = 0, col1 = 0, row2 = 0, col2 = 0;
HObject CreateModelImage = img.Clone();
if (!ImageFunc.IsEmptyRegion(MySlnData.MyModel[currentModelNum].h_SearchROI))
{
HOperatorSet.ReduceDomain(CreateModelImage, MySlnData.MyModel[currentModelNum].h_SearchROI, out CreateModelImage);
HOperatorSet.CropDomain(CreateModelImage, out CreateModelImage);
}
HTuple hv_RowCheck = null, hv_ColumnCheck = null, hv_AngleCheck = null, hv_Error = null;
try
{
HOperatorSet.BestMatchRotMg(CreateModelImage, MySlnData.MyModel[currentModelNum].hv_ModelID, (new HTuple(MySlnData.MyModel[currentModelNum].startAngle)).TupleRad(), (new HTuple(MySlnData.MyModel[currentModelNum].endAngle)).TupleRad(), 100 - MySlnData.MyModel[currentModelNum].Score / 100.0, "true", 4, out hv_RowCheck, out hv_ColumnCheck, out hv_AngleCheck, out hv_Error);
}
catch
{
return pp;
}
if (0 < (int)((new HTuple(hv_Error.TupleLength()))))
{
try
{
if (MySlnData.MyModel[currentModelNum].Score / 100.0 > (100 - hv_Error[0].D))
return pp;
HObject ho_ImageAffinTrans;
HTuple hMat2D = null;
HOperatorSet.VectorAngleToRigid(MySlnData.MyModel[currentModelNum].hv_Orgin_Row, MySlnData.MyModel[currentModelNum].hv_Orgin_Column, 0, hv_RowCheck[0].D, hv_ColumnCheck[0].D, hv_AngleCheck[0].D, out hMat2D);
HOperatorSet.AffineTransRegion(MySlnData.MyModel[currentModelNum].h_OriginRegion, out ho_ImageAffinTrans, hMat2D, "constant");
HTuple RowTrans = hv_RowCheck[0], ColumnTrans = hv_ColumnCheck[0];
if (MySlnData.MyModel[currentModelNum].hv_Target_Row[0].D != -1 && MySlnData.MyModel[currentModelNum].hv_Target_Column[0].D != -1)
HOperatorSet.AffineTransPixel(hMat2D, MySlnData.MyModel[currentModelNum].hv_Target_Row, MySlnData.MyModel[currentModelNum].hv_Target_Column, out RowTrans, out ColumnTrans);
if (!ImageFunc.IsEmptyRegion(MySlnData.MyModel[currentModelNum].h_SearchROI))
{
RowTrans = RowTrans + row1;
ColumnTrans = ColumnTrans + col1;
HOperatorSet.MoveRegion(ho_ImageAffinTrans, out ho_ImageAffinTrans, row1, col1);
}
pp.Enqueue(new VisionPoint(RowTrans.D, ColumnTrans.D, hv_AngleCheck[0].D * 57.3, 100 - hv_Error[0].D, null, ho_ImageAffinTrans));
}
catch { }
}
return pp;
}
总结
该工程篇幅比较多,不过具体的函数在文章中已经表标明,具体的查看源码使用。文章来源地址https://www.toymoban.com/news/detail-460612.html
到了这里,关于基于C#调用halcon实现模板匹配【附部分源码】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!