Blob是一个非常经典实用的算法,常用于定位及缺陷检测,关于Blob的详细介绍可以参考下面这篇博客。
一、效果展示
二、Blob分析流程
三、代码
关于ROI的绘制请看我上篇内容
1.界面控件
2.变量声明
HDrawingObject HD_Roi;
HObject ho_Img,ho_DispObj;
3.变量参数初始化
public Form1()
{
InitializeComponent();
InitParm();
}
public void InitParm()
{
//预处理参数控件初始化
comboBox_Filter.SelectedIndex = 0;
comboBox1.SelectedIndex = 0;
label1.Visible = false;
label2.Visible = false;
textBox1.Visible = false;
textBox2.Visible = false;
comboBox1.Visible = false;
//形态学参数控件初始化
comboBox2.SelectedIndex = 0;
comboBox3.SelectedIndex = 0;
comboBox3.Visible = false;
label6.Visible = false;
textBox6.Visible = false;
//变量初始化
HOperatorSet.GenEmptyObj(out ho_Img);
HOperatorSet.GenEmptyObj(out ho_DispObj);
HD_Roi =new HDrawingObject();
HD_Roi.CreateDrawingObjectRectangle1(0, 0, 100, 100);
}
4.界面控件交互代码
//显示 隐藏ROI
private void checkBox_DispRoi_CheckedChanged(object sender, EventArgs e)
{
if (checkBox_DispRoi.Checked)
{ //显示roi至窗口
hWindowControl1.HalconWindow.AttachDrawingObjectToWindow(HD_Roi);
}
else
{
//隐藏roi
hWindowControl1.HalconWindow.DetachDrawingObjectFromWindow(HD_Roi);
}
}
private void button1_Click(object sender, EventArgs e)
{
try
{
string imgPath;
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "c:\\";//注意这里写路径时要用c:\\而不是c:\
openFileDialog.Filter = "jpg|*.jpg|bmp|*.bmp|*.png|*.png";
openFileDialog.FilterIndex = 1;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
int t = System.Environment.TickCount;
imgPath = openFileDialog.FileName;
HOperatorSet.ReadImage(out ho_Img, imgPath);
ShowImage();
ShowMessage("打开" + imgPath);
ShowRunTime(System.Environment.TickCount - t);
}
}
catch (Exception ee)
{
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
int parm;
if (textBox1.Text == "")
textBox1.Text = 0.ToString();
parm = int.Parse(textBox1.Text);
if (parm < 0)
textBox1.Text = 0.ToString();
if (parm >999999)
textBox1.Text = 255.ToString();
}
private void comboBox3_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox2.SelectedIndex == 0)
return;
if (comboBox3.SelectedIndex==0)
{
comboBox3.Visible = true;
label6.Text = "结构元半径";
label6.Visible = true;
textBox6.Visible = true;
textBox6.Text = "3";
label7.Visible = false;
textBox7.Visible = false;
}
else
{
comboBox3.Visible = true;
label6.Text = "结构元宽";
label6.Visible = true;
textBox6.Visible = true;
label7.Text = "结构元高";
label7.Visible = true;
textBox7.Visible = true;
textBox6.Text = "3";
textBox7.Text = "3";
}
}
private void comboBox_Filter_SelectedIndexChanged(object sender, EventArgs e)
{
//先将控件隐藏
label1.Visible = false;
label2.Visible = false;
textBox1.Visible = false;
textBox2.Visible = false;
comboBox1.Visible = false;
switch (comboBox_Filter.SelectedIndex)
{
case 0:
break;
case 1:
//显示中值滤波的参数控件
label1.Text = "掩模半径";
label1.Visible=true;
textBox1.Visible = true;
textBox1.Text = "1";
comboBox1.Visible = true;
break;
case 2:
//显示均值滤波的参数控件
label1.Text = "掩模宽度";
label1.Visible = true;
textBox1.Visible = true;
textBox1.Text = "9";
label2.Text = "掩模高度";
label2.Visible = true;
textBox2.Visible = true;
textBox2.Text = "9";
break;
case 3:
//显示高斯滤波的参数控件
label1.Text = "滤波器大小";
label1.Visible = true;
textBox1.Visible = true;
textBox1.Text = "5";
break;
default:
break;
}
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
//先将控件隐藏
comboBox3.Visible = false;
label6.Visible = false;
textBox6.Visible = false;
if (comboBox2.SelectedIndex == 0)
return;
comboBox3.Visible = true;
label6.Text = "结构元半径";
label6.Visible = true;
textBox6.Visible = true;
textBox6.Text = "3";
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
//如果不是数字、回车,则屏蔽
if (!(Char.IsNumber(e.KeyChar)) && e.KeyChar != (char)8)
e.Handled = true;
}
private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
{
//如果不是数字、回车,则屏蔽
if (!(Char.IsNumber(e.KeyChar)) && e.KeyChar != (char)8)
e.Handled = true;
}
private void textBox1_TextChanged_1(object sender, EventArgs e)
{
int parm;
switch (comboBox_Filter.SelectedIndex)
{
case 0:
break;
case 1:
//中值滤波参数值限制
if (textBox1.Text == "")
textBox1.Text = 1.ToString();
parm= int.Parse(textBox1.Text);
if (parm<1)
textBox1.Text = 1.ToString();
if (parm > 4095)
textBox1.Text = 4095.ToString();
break;
case 2:
//均值滤波参数值限制
if (textBox1.Text == "")
textBox1.Text = 1.ToString();
parm = int.Parse(textBox1.Text);
if (parm < 1)
textBox1.Text = 1.ToString();
if (parm > 501)
textBox1.Text = 501.ToString();
break;
case 3:
//高斯滤波参数值限制
if (textBox1.Text == "")
textBox1.Text = 3.ToString();
parm = int.Parse(textBox1.Text);
if (parm < 3)
textBox1.Text = 3.ToString();
if (parm > 11)
textBox1.Text = 11.ToString();
break;
default:
break;
}
int number = int.Parse(textBox1.Text);
if ((number >= 1) && (number <= 8))
{
}
else if (number < 1)
{
number = 1;
}
else if (number > 8)
{
number = 8;
}
textBox1.Text = number.ToString();
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
int parm;
if (textBox2.Text == "")
textBox2.Text = 1.ToString();
parm = int.Parse(textBox2.Text);
if (parm < 1)
textBox2.Text = 1.ToString();
if (parm > 501)
textBox2.Text = 501.ToString();
}
private void textBox6_KeyPress(object sender, KeyPressEventArgs e)
{
//如果不是数字、回车,则屏蔽
if (!(Char.IsNumber(e.KeyChar)) && e.KeyChar != (char)8)
e.Handled = true;
}
private void textBox7_KeyPress(object sender, KeyPressEventArgs e)
{
//如果不是数字、回车,则屏蔽
if (!(Char.IsNumber(e.KeyChar)) && e.KeyChar != (char)8)
e.Handled = true;
}
private void textBox_MaxArea_KeyPress(object sender, KeyPressEventArgs e)
{
//如果不是数字、回车,则屏蔽
if (!(Char.IsNumber(e.KeyChar)) && e.KeyChar != (char)8)
e.Handled = true;
}
private void textBox_MinArea_KeyPress(object sender, KeyPressEventArgs e)
{
//如果不是数字、回车,则屏蔽
if (!(Char.IsNumber(e.KeyChar)) && e.KeyChar != (char)8)
e.Handled = true;
}
private void textBox6_TextChanged(object sender, EventArgs e)
{
int parm;
if (textBox6.Text == "")
textBox6.Text = 1.ToString();
parm = int.Parse(textBox6.Text);
if (parm < 1)
textBox6.Text = 1.ToString();
if (parm > 511)
textBox6.Text = 501.ToString();
}
private void textBox7_TextChanged(object sender, EventArgs e)
{
int parm;
if (textBox7.Text == "")
textBox7.Text = 1.ToString();
parm = int.Parse(textBox7.Text);
if (parm < 1)
textBox7.Text = 1.ToString();
if (parm > 501)
textBox7.Text = 501.ToString();
}
private void textBox_MaxArea_TextChanged(object sender, EventArgs e)
{
int parm;
if (textBox_MaxArea.Text == "")
textBox_MaxArea.Text = 0.ToString();
parm = int.Parse(textBox_MaxArea.Text);
if (parm < 0)
textBox_MaxArea.Text = 0.ToString();
if (parm > 999999)
textBox_MaxArea.Text = 255.ToString();
}
private void textBox_MinArea_TextChanged(object sender, EventArgs e)
{
int parm;
if (textBox_MinArea.Text == "")
textBox_MinArea.Text = 0.ToString();
parm = int.Parse(textBox_MinArea.Text);
if (parm < 0)
textBox_MinArea.Text = 0.ToString();
if (parm > 999999)
textBox_MinArea.Text = 255.ToString();
}
获取ROI参数信息
/// <summary>
/// 获取绘制对象参数参数
/// </summary>
/// <param name="drawid"></param>
/// <param name="window"></param>
/// <param name="type"></param>
private void CallbackDrwingObjParm(HDrawingObject drawid, HWindow window, string type)
{
HTuple hv_Parms="";
string[] parms;
string text="";
try
{
//矩形roi参数获取
parms = new string[] { "row1", "column1", "row2", "column2" };
hv_Parms = HD_Roi.GetDrawingObjectParams(parms);
text = "\r\n行1:" + hv_Parms[0] + "\r\n列1:" + hv_Parms[1] + "\r\n行2:" + hv_Parms[2] + "\r\n列2:" + hv_Parms[3] ;
label1.Text = "Roi信息:" + text;
}
catch (Exception)
{
}
}
///
显示图像以及图像处理结果
/// <summary>
/// 显示图片以及图像处理结果
/// </summary>
public void ShowImage()
{
try
{
if (!ho_Img.IsInitialized()) { return; }
HTuple hW, hH;
HOperatorSet.GetImageSize(ho_Img, out hW, out hH);
HOperatorSet.SetPart(hWindowControl1.HalconWindow, 0, 0, hH, hW);
hWindowControl1.HalconWindow.SetColor("green");
hWindowControl1.HalconWindow.SetDraw("margin");
hWindowControl1.HalconWindow.SetLineWidth(1);
hWindowControl1.HalconWindow.ClearWindow();
ho_Img.DispObj(hWindowControl1.HalconWindow);
ho_DispObj.DispObj(hWindowControl1.HalconWindow);
}
catch (Exception ee)
{
}
}
5.Blob算法的实现
public bool Blob(HObject image, out HObject obj)
{
string []parms = new string[] { "row1", "column1", "row2", "column2" };
HTuple hv_Area,hv_RoiParms,hv_Row1,hv_Col1, hv_Row2, hv_Col2, hv_Threshold,hv_Dist;
HObject obj2, ho_Cross,ho_Rect;
HOperatorSet.GenEmptyObj(out obj2);
HOperatorSet.GenEmptyObj(out ho_Cross);
HOperatorSet.GenEmptyObj(out ho_Rect);
obj = obj2;
try
{
//没有图像返回false
if (image.CountObj() == 0 || !image.IsInitialized())
{
obj = obj2;
return false;
}
// -------------2.分割ROI-------------------
//获取ROI参数
hv_RoiParms = HD_Roi.GetDrawingObjectParams(parms);
HOperatorSet.GenRectangle1(out ho_Rect, hv_RoiParms[0], hv_RoiParms[1], hv_RoiParms[2], hv_RoiParms[3]);
HOperatorSet.ReduceDomain(image, ho_Rect, out image);
// -------------2.预处理 滤波-------------------
switch (comboBox_Filter.SelectedIndex)
{
case 0://不处理
break;
case 1://中值滤波
HTuple maskTyple;
maskTyple = "circle";
if (comboBox1.SelectedIndex==2)
maskTyple = "square";
HOperatorSet.MedianImage(image, out image, maskTyple, int.Parse(textBox1.Text), "mirrored");
break;
case 2://均值滤波
HOperatorSet.MeanImage(image, out image, int.Parse(textBox1.Text), int.Parse(textBox2.Text));
break;
case 3://高斯滤波
HOperatorSet.GaussFilter(image, out image, int.Parse(textBox1.Text));
break;
default:
break;
}
// -------------3.二值化-------------------
//这里为了简单一点用的BinaryThreshold算子,大家可以根据自己的实际情况选择
if (radioButton_Black.Checked)
{
//黑色目标
//HOperatorSet.Threshold(image, out obj2, 0, int.Parse(textBox_Threshold.Text));
HOperatorSet.BinaryThreshold(image, out obj2, "max_separability", "dark",out hv_Threshold);
}
else
{
//白色目标
//HOperatorSet.Threshold(image, out obj2,int.Parse(textBox_Threshold.Text),255);
HOperatorSet.BinaryThreshold(image, out obj2, "max_separability", "light", out hv_Threshold);
}
// -------------4.形态学-------------------
switch (comboBox2.SelectedIndex)
{
case 0://不处理
break;
case 1://腐蚀
if (comboBox3.SelectedIndex == 0)//圆形腐蚀
HOperatorSet.ErosionCircle(obj2, out obj2, int.Parse(textBox6.Text));
else//矩形腐蚀
HOperatorSet.ErosionRectangle1(obj2, out obj2, int.Parse(textBox6.Text),int.Parse(textBox7.Text));
break;
case 2://膨胀
if (comboBox3.SelectedIndex == 0)//圆形膨胀
HOperatorSet.DilationCircle(obj2, out obj2, int.Parse(textBox6.Text));
else//矩形膨胀
HOperatorSet.DilationRectangle1(obj2, out obj2, int.Parse(textBox6.Text), int.Parse(textBox7.Text));
break;
case 3://开运算
if (comboBox3.SelectedIndex == 0)//圆形开运算
HOperatorSet.OpeningCircle(obj2, out obj2, int.Parse(textBox6.Text));
else//矩形开运算
HOperatorSet.OpeningRectangle1(obj2, out obj2, int.Parse(textBox6.Text), int.Parse(textBox7.Text));
break;
case 4://闭运算
if (comboBox3.SelectedIndex == 0)//圆形闭运算
HOperatorSet.ClosingCircle(obj2, out obj2, int.Parse(textBox6.Text));
else//矩形闭运算
HOperatorSet.ClosingRectangle1(obj2, out obj2, int.Parse(textBox6.Text), int.Parse(textBox7.Text));
break;
default:
break;
}
// -------------5.筛选-------------------
//二值化后的区域是一块整体,先要将它打散变成多个区域
HOperatorSet.Connection(obj2, out obj2);
//这里为了方便演示 只使用面积进行筛选
//实际项目中 会用到其他特征 比如圆度 矩形度这些,这里根据自己实际情况来修改即可
HOperatorSet.SelectShape(obj2, out obj2, "area", "and", int.Parse(textBox_MinArea.Text), int.Parse(textBox_MaxArea.Text));
//生成一个小十字架用来显示
HOperatorSet.SmallestRectangle1(obj2, out hv_Row1, out hv_Col1, out hv_Row2, out hv_Col2);
HOperatorSet.DistancePp(hv_Row1, hv_Col1, hv_Row2, hv_Col2, out hv_Dist);
HOperatorSet.AreaCenter(obj2, out hv_Area, out hv_Row1, out hv_Col1);
HOperatorSet.GenCrossContourXld(out ho_Cross, hv_Row1, hv_Col1, hv_Dist/5, 0);
HOperatorSet.ConcatObj(obj2, ho_Cross, out obj);
obj2.Dispose(); ho_Cross.Dispose(); ho_Rect.Dispose();
if (hv_Row1.TupleLength()<1)
{
return false;
}
return true;
}
catch (Exception)
{
obj2.Dispose(); ho_Cross.Dispose(); ho_Rect.Dispose();
return false;
}
}
执行按钮的单击事件
private void button2_Click(object sender, EventArgs e)
{
ho_DispObj.Dispose();
int t = System.Environment.TickCount;
ShowRunTime(System.Environment.TickCount - t);
bool b = Blob(ho_Img, out ho_DispObj);
if (!b)
Console.WriteLine("执行失败");
label_RunTime.Text = (System.Environment.TickCount - t).ToString() + "ms";
ShowImage();
}
如果这篇内容对你有帮助的话,请给我点个赞让我开心一下吧。关注我后续会更新更多c#以及halcon方面的内容