1. 软件运行效果演示
halcon联合C#模板匹配软件(图片自适应显示缩放移动)
2. 软件界面
2.1 绘制ROI区域
2.2 模板区域
2.3 模板匹配参数设置
2.4 模板匹配识别
2.5 日志
3. 程序编写
3.1 图像的自适应显示
关键算子:dev_setpart算子,可以查看halcon官方的帮助文档,对于后面的图像的缩小和放大很重要
private void btn_readimage_Click(object sender, EventArgs e)
{
try
{
HOperatorSet.ClearWindow(hWindowControl1.HalconWindow);
double rateimage, ratewin;
double row1, column1, row2, column2;
HTuple imagewidth, imageheigth, partwidth, partheigth;
HOperatorSet.GenEmptyObj(out ho_Image);
HOperatorSet.ReadImage(out ho_Image, "C:/Users/Public/Documents/MVTec/HALCON-17.12-Progress/examples/images/board/board-01.png");
HOperatorSet.GetImageSize(ho_Image, out imagewidth, out imageheigth);
HTuple Row0, Column0, Row00, Column00, Ht, Wt, r1, c1, r2, c2;
//读取显示控件尺寸
partwidth = hWindowControl1.Width;
partheigth = hWindowControl1.Height;
//显示控件尺寸
textBox1.Text = partwidth.ToString();
textBox2.Text = partheigth.ToString();
//显示图像尺寸
textBox3.Text = imagewidth.ToString();
textBox4.Text = imageheigth.ToString();
//计算图像比率和窗体比例
rateimage = 1.0 * imagewidth / imageheigth;
ratewin = 1.0 * partwidth / partheigth;
//自适应显示图像
if (rateimage <= ratewin)
{
row1 = 0;
column1 = -(1.0 * imageheigth / partheigth * partwidth - imagewidth) * 1.0 / 2;
row2 = row1 + imageheigth;
column2 = column1 + 1.0 * partwidth * imageheigth / partheigth;
HOperatorSet.SetPart(hWindowControl1.HalconWindow, row1, column1, row2, column2);
HOperatorSet.DispObj(ho_Image, hWindowControl1.HalconWindow);
textBox5.Text = row1.ToString();
textBox6.Text = column1.ToString();
textBox7.Text = row2.ToString();
textBox8.Text = column2.ToString();
HOperatorSet.GetPart(hWindowControl1.HalconWindow, out Row0, out Column0, out Row00, out Column00);
textBox8.Text = Column0.ToString();
//图片显示成功标志
readimage_flag = 1;
}
}
catch (Exception)
{
MessageBox.Show("图像加载错误");
}
}
3.2 图像的放大缩小
使用get_mposition算子获取鼠标在hwindowcontrol控件上的位置,以该位置为放大缩小中心,滚轮向前滑动,图像依次放大1.5倍;滚轮向后滑动,图像依次缩小0.5倍,通过setpart函数实现图像的放大和缩小。
private void hWindowControl2_HMouseWheel(object sender, HMouseEventArgs e)
{
try
{
if (readimage_flag1 == 1)
{
HTuple Zoom, Row, Col, Button;
HTuple Row0, Column0, Row00, Column00, Ht, Wt, r1, c1, r2, c2;
if (e.Delta > 0)
{
Zoom = 1.5;
}
else
{
Zoom = 0.5;
}
//获取鼠标点位置
HOperatorSet.GetMposition(hWindowControl2.HalconWindow, out Row, out Col, out Button);
//获取显示窗口位置
HOperatorSet.GetPart(hWindowControl2.HalconWindow, out Row0, out Column0, out Row00, out Column00);
//获得窗体宽度和长度
Ht = Row00 - Row0;
Wt = Column00 - Column0;
//实现图像放大和缩小
if (Ht * Wt < 32000 * 32000 || Zoom == 1.5)//普通版halcon能处理的图像最大尺寸是32K*32K。如果无限缩小原图像,导致显示的图像超出限制,则会造成程序崩溃
{
r1 = (Row0 + ((1 - (1.0 / Zoom)) * (Row - Row0)));
c1 = (Column0 + ((1 - (1.0 / Zoom)) * (Col - Column0)));
r2 = r1 + (Ht / Zoom);
c2 = c1 + (Wt / Zoom);
HOperatorSet.SetPart(hWindowControl2.HalconWindow, r1, c1, r2, c2);
HOperatorSet.ClearWindow(hWindowControl2.HalconWindow);
HOperatorSet.DispObj(templateregion, hWindowControl2.HalconWindow);
}
}
}
catch (Exception)
{
throw;
}
3.3 图像移动
基本原理:监控鼠标按下的事件,获取鼠标按下的x、y坐标位置;监控鼠标抬起的事件,获取鼠标最终抬起的x1、y1坐标,通过一定的比例换算得到需要显示的图像的左上角、右下角坐标位置,通过dev_setpart算子实现图像移动的效果。
private void hWindowControl1_HMouseDown(object sender, HMouseEventArgs e)
{
if (e.Button == MouseButtons.Left && roidrawingflag==0)
{
HTuple Row, Column, Button;
HOperatorSet.GetMposition(hWindowControl1.HalconWindow, out Row, out Column, out Button);
RowDown = Row; //鼠标按下时的行坐标
ColDown = Column; //鼠标按下时的列坐标
}
}
private void hWindowControl1_HMouseUp(object sender, HMouseEventArgs e)
{
if (e.Button == MouseButtons.Left && roidrawingflag == 0)
{
HTuple row1, col1, row2, col2, Row, Column, Button;
HOperatorSet.GetMposition(hWindowControl1.HalconWindow, out Row, out Column, out Button);
double RowMove = Row - RowDown; //鼠标弹起时的行坐标减去按下时的行坐标,得到行坐标的移动值
double ColMove = Column - ColDown;//鼠标弹起时的列坐标减去按下时的列坐标,得到列坐标的移动值
HOperatorSet.GetPart(hWindowControl1.HalconWindow, out row1, out col1, out row2, out col2);//得到当前的窗口坐标
HOperatorSet.SetPart(hWindowControl1.HalconWindow, row1 - RowMove, col1 - ColMove, row2 - RowMove, col2 - ColMove);//这里可能有些不好理解。以左上角原点为参考点
HOperatorSet.SetSystem("flush_graphic", "false");
HOperatorSet.ClearWindow(hWindowControl1.HalconWindow);
HOperatorSet.SetSystem("flush_graphic", "true");
HOperatorSet.DispObj(ho_Image, hWindowControl1.HalconWindow);
}
}
3.4 绘制图像roi区域
ROI区域绘制代码获取:在halcon软件中编写ROI绘制程序并导出成C#程序
private void btn_circle_draw_Click(object sender, EventArgs e)
{
roidrawingflag = 1;
HObject ho_Circle;
HTuple hv_Row = null;
HTuple hv_Column = null, hv_Radius = null;
// Initialize local and output iconic variables
hWindowControl1.Focus();
HOperatorSet.GenEmptyObj(out ho_Circle);
HOperatorSet.GenEmptyObj(out templateregion);
HOperatorSet.SetLineWidth(hWindowControl1.HalconWindow, 2);
HOperatorSet.SetColor(hWindowControl1.HalconWindow, "red");
HOperatorSet.DrawCircle(hWindowControl1.HalconWindow, out hv_Row, out hv_Column, out hv_Radius);
ho_Circle.Dispose();
HOperatorSet.GenCircle(out ho_Circle, hv_Row, hv_Column, hv_Radius);
templateregion.Dispose();
HOperatorSet.ReduceDomain(ho_Image, ho_Circle, out templateregion);
HOperatorSet.GetImageSize(templateregion,out templatewidth,out templateheight);
roidrawingflag = 0;
}
注意:软件目前只做了圆形ROI绘制,其他的矩形、仿射矩形等ROI绘制方法一样。
3.5 开启多线程实现模板匹配(包含有参数设置、仿射变换、模板匹配)
form1占用了主线程,如果不开子线程用于模板匹配,主线程同时运行form1和模板匹配程序,会导致软件整体卡死;因此,需要使用Thread类开一个子线程用于模板匹配识别。
private void btn_serialreadimage_Click(object sender, EventArgs e)
{
//开启线程
read_image = new Thread(read_image_serial);
read_image.Start();
}
/// <summary>
/// 模板匹配识别批量图片
/// </summary>
private void read_image_serial()
{
HTuple anglestart = null, angleend = null, numlevels = null, anglestep = null, optimization = null, metric = null, contrast = null, minconstrast = null, modelid = null;
HObject outputcontours;
HTuple hommate2d = null;
if (templateregion != null)
{
HTuple templaterow, templatecolumn, templateangel, templatescore;
//设置模板匹配算法参数
if (ckb_numberpa.Checked)
{
numlevels = "auto";
}
else
{
numlevels = (int)numericUpDown1.Value;
}
HOperatorSet.TupleRad((int)numericUpDown2.Value, out anglestart);
HOperatorSet.TupleRad((int)numericUpDown3.Value, out angleend);
if (ckb_anglestep.Checked)
{
anglestep = "auto";
}
else
{
HOperatorSet.TupleRad((int)numericUpDown4.Value, out anglestep);
}
if (ckb_optimization.Checked)
{
optimization = "auto";
}
else
{
optimization = comboBox1.Text.ToString();
}
if (ckb_metric.Checked)
{
metric = "use_polarity";
}
else
{
metric = comboBox2.Text.ToString();
}
if (ckb_contrast.Checked)
{
contrast = "auto";
}
else
{
contrast = trackBar1.Value;
}
if (ckb_mincontrast.Checked)
{
minconstrast = "auto";
}
else
{
minconstrast = trackBar2.Value;
}
HOperatorSet.CreateShapeModel(templateregion, numlevels, anglestart, angleend, anglestep, optimization, metric, contrast, minconstrast, out modelid);
HOperatorSet.GenEmptyObj(out outputcontours);
outputcontours.Dispose();
HOperatorSet.HomMat2dIdentity(out hommate2d);
HObject ho_Image = null, outputregion;
// Local control variables
HTuple hv_ImageFiles = null, hv_Index = null, displayfont;
HTuple homat2dtras, homat2drotate;
double rateimage, ratewin;
double row1, column1, row2, column2;
HTuple imagewidth, imageheigth, partwidth, partheigth;
// Initialize local and output iconic variables
HOperatorSet.GenEmptyObj(out ho_Image);
//Image Acquisition 01: Code generated by Image Acquisition 01
HOperatorSet.ListFiles("C:/Users/Public/Documents/MVTec/HALCON-17.12-Progress/examples/images/board", "files", out hv_ImageFiles);
HOperatorSet.TupleRegexpSelect(hv_ImageFiles, ".*png", out hv_ImageFiles);
for (hv_Index = 0; (int)hv_Index <= (int)((new HTuple(hv_ImageFiles.TupleLength()
)) - 1); hv_Index = (int)hv_Index + 1)
{
HOperatorSet.GenEmptyObj(out ho_Image);
ho_Image.Dispose();
HOperatorSet.ReadImage(out ho_Image, hv_ImageFiles.TupleSelect(hv_Index));
HOperatorSet.GetImageSize(ho_Image, out imagewidth, out imageheigth);
partwidth = hWindowControl1.Width;
partheigth = hWindowControl1.Height;
//计算图像比率和窗体比例
rateimage = 1.0 * imagewidth / imageheigth;
ratewin = 1.0 * partwidth / partheigth;
//自适应显示图像
HOperatorSet.ClearWindow(hWindowControl1.HalconWindow);
if (rateimage <= ratewin )
{
row1 = 0;
column1 = -(1.0 * imageheigth / partheigth * partwidth - imagewidth) * 1.0 / 2;
row2 = row1 + imageheigth;
column2 = column1 + 1.0 * partwidth * imageheigth / partheigth;
HOperatorSet.SetPart(hWindowControl1.HalconWindow, row1, column1, row2, column2);
HOperatorSet.FindShapeModel(ho_Image, modelid, anglestart, angleend, 0.5, 1, 0, "least_squares", 1, 0.7, out templaterow, out templatecolumn, out templateangel, out templatescore);
HOperatorSet.GetShapeModelContours(out outputcontours, modelid, 1);
try
{
HOperatorSet.HomMat2dTranslate(hommate2d, templaterow, templatecolumn, out homat2dtras);
HOperatorSet.HomMat2dRotate(homat2dtras, templateangel, templaterow, templatecolumn, out homat2drotate);
HOperatorSet.AffineTransContourXld(outputcontours, out outputregion, homat2drotate);
HOperatorSet.SetColor(hWindowControl1.HalconWindow, "blue");
HOperatorSet.SetLineWidth(hWindowControl1.HalconWindow, 1.5);
HOperatorSet.DispObj(ho_Image, hWindowControl1.HalconWindow);
HOperatorSet.DispObj(outputregion, hWindowControl1.HalconWindow);
if (outputcontours != null)
{
HOperatorSet.SetTposition(hWindowControl1.HalconWindow, 20, 20);
HOperatorSet.SetColor(hWindowControl1.HalconWindow, "red");
HOperatorSet.QueryFont(hWindowControl1.HalconWindow,out displayfont);
HOperatorSet.SetFont(hWindowControl1.HalconWindow, displayfont[0]+ "-Bold-25");
HOperatorSet.WriteString(hWindowControl1.HalconWindow, "The result is OK");
}
}
catch (Exception)
{
throw;
}
}
HOperatorSet.WaitSeconds(1);
if (hv_Index == (hv_ImageFiles.TupleLength() - 1))
{
HOperatorSet.ClearWindow(hWindowControl1.HalconWindow);
read_image.Abort();
}
}
}
else
{
MessageBox.Show("请添加模板图像");
read_image.Abort();
}
}
3.2.1 模板匹配参数设置
if (ckb_numberpa.Checked)
{
numlevels = "auto";
}
else
{
numlevels = (int)numericUpDown1.Value;
}
HOperatorSet.TupleRad((int)numericUpDown2.Value, out anglestart);
HOperatorSet.TupleRad((int)numericUpDown3.Value, out angleend);
if (ckb_anglestep.Checked)
{
anglestep = "auto";
}
else
{
HOperatorSet.TupleRad((int)numericUpDown4.Value, out anglestep);
}
if (ckb_optimization.Checked)
{
optimization = "auto";
}
else
{
optimization = comboBox1.Text.ToString();
}
if (ckb_metric.Checked)
{
metric = "use_polarity";
}
else
{
metric = comboBox2.Text.ToString();
}
if (ckb_contrast.Checked)
{
contrast = "auto";
}
else
{
contrast = trackBar1.Value;
}
if (ckb_mincontrast.Checked)
{
minconstrast = "auto";
}
else
{
minconstrast = trackBar2.Value;
}
HOperatorSet.CreateShapeModel(templateregion, numlevels, anglestart, angleend, anglestep, optimization, metric, contrast, minconstrast, out modelid);
HOperatorSet.GenEmptyObj(out outputcontours);
outputcontours.Dispose();
3.2.2 仿射变换
HOperatorSet.HomMat2dTranslate(hommate2d, templaterow, templatecolumn, out homat2dtras);
HOperatorSet.HomMat2dRotate(homat2dtras, templateangel, templaterow, templatecolumn, out homat2drotate);
HOperatorSet.AffineTransContourXld(outputcontours, out outputregion, homat2drotate);
注意:模板匹配算法找到的轮廓起始坐标为窗口左上角坐标,角度为0度
3.2.3 模板匹配
HOperatorSet.CreateShapeModel(templateregion, numlevels, anglestart, angleend, anglestep, optimization, metric, contrast, minconstrast, out modelid);
HOperatorSet.FindShapeModel(ho_Image, modelid, anglestart, angleend, 0.5, 1, 0, "least_squares", 1, 0.7, out templaterow, out templatecolumn, out templateangel, out templatescore);
HOperatorSet.GetShapeModelContours(out outputcontours, modelid, 1);