在工业流水线实际项目之中,用到图像识别器件的例子还蛮多的,今天主要是通过复习昨日的C#封装函数,并开始扩展新的板块,本文主要内容是,提取图片文件夹(包含多张图片)--灰度处理--区域打散--特征筛选--模板匹配--计算器件总数--显示个数并标记,话不多说 直接开搞!
一、项目准备
1.创建窗体项目
创建窗口项目之后,设计一个窗口用于显示图片以及处理结果,一个按钮完成提取图片文件夹(包含多张图片)--灰度处理--区域打散--特征筛选--模板匹配--计算器件总数--显示个数整个流程:
2.添加所需类库
同样,在创建项目中右键选择添加,创建三个类库,一个命名为ImageProcess作为图片处理类,一个DrawRectangle类作为在模板匹配之后添加最小外接矩形。
二、根据项目流程编码
1.图片提取处理
在ImageProcess类里面创建一个名为ReadImage的函数,作为显示图片的方法:
public static bool ReadImage(out HObject hImage, string filename, HWindow window)
{
try
{
HOperatorSet.ReadImage(out hImage, filename);
HTuple width, height;
//获得图片的宽高
HOperatorSet.GetImageSize(hImage, out width, out height);
//设置显示范围
//HWindowControl.HalconWindow -->控件的句柄 设置显示范围
HOperatorSet.SetPart(window, 0, 0, (height - 1), (width - 1));
//显示
HOperatorSet.DispObj(hImage, window);
return true;
}
catch
{
hImage = null;
return false;
}
}
FolderBrowserDialog是一个获取文件夹路径的控件或者对象,在本文需要他的SelectedPath属性,主要是获取选择的文件夹路径:
//获取文件夹控件方法
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
folderBrowserDialog.ShowDialog();
//获得具体路径
string path = folderBrowserDialog.SelectedPath;
Console.WriteLine(path);
FileInfo[] files = new DirectoryInfo(path).GetFiles();
for (int i = 0; i < files.Length; i++)
{
//Console.WriteLine(files[i]);
HObject hImage;
ImageProcess.ReadImage(out hImage, files[i].FullName, hWindowControl1.HalconWindow);
}
这样运行会发现 当读取图片之后只会显示文件夹里面的最后一张图片,这是因为程序运行的非常快,只需要加入以下两行代码做一个显示延时即可:
System.Windows.Forms.Application.DoEvents();
Thread.Sleep(1000);
2.图片阈值处理
在阈值处理里面,我封装了关于自动与手动函数的参数类,命名为ThreSholdDef类,里面包含了手动阈值处理的灰度值参数,自动阈值分割的大津法/三角法,对于图片的亮暗处理。代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WindowsFormsApp1
{
public enum 阈值分割方式
{
Threshold = 0,//普通阈值分割
BinaryThreshold = 1,//自动阈值分割
}
public enum 自动阈值分割方法
{
max_separability = 0,
smooth_histo = 1,
}
public enum 亮或暗
{
dark = 0,
light = 1
}
/// <summary>
/// 阈值分割参数类
/// </summary>
public class ThreSholdDef
{
/// <summary>
/// 普通阈值分割的构造函数
/// </summary>
/// <param name="minGray"></param>
/// <param name="maxGray"></param>
public ThreSholdDef(int minGray, int maxGray)
{
_Segment = 阈值分割方式.Threshold;
_MaxGray = maxGray;
_MinGray = minGray;
}
/// <summary>
/// 自动阈值分割参数类的构造函数
/// </summary>
/// <param name="segmentMothd"></param>
/// <param name="lightOrDark"></param>
public ThreSholdDef(自动阈值分割方法 segmentMothd, 亮或暗 lightOrDark)
{
_Segment = 阈值分割方式.BinaryThreshold;
_SegmentMothd = segmentMothd;
_LightOrDark = lightOrDark;
}
/// <summary>
/// 确定分割方式是普通阈值分割还是自动阈值分割
/// </summary>
public 阈值分割方式 _Segment;
/// <summary>
/// 普通阈值分割的最大灰度值
/// </summary>
public int _MaxGray;
/// <summary>
/// 普通阈值分割的最小灰度值
/// </summary>
public int _MinGray;
/// <summary>
/// 确实自定阈值分割为三角法还是大津法
/// </summary>
public 自动阈值分割方法 _SegmentMothd;
/// <summary>
/// 确定自动阈值分割是找亮还是找暗
/// </summary>
public 亮或暗 _LightOrDark;
}
}
封装好之后,方便后续的直接调用使用。
在ImageProcess类里面手动Threshold和B自动inaryThreshold阈值处理方式,这里选择手动阈值分割处理:
#region 【阈值分割】
public static bool Threshold(HObject hImage, out HObject Region, int Min, int Max)
{
HOperatorSet.Threshold(hImage, out Region, Min, Max);
return true;
}
public static bool BinaryThreshold(HObject hImage, out HObject Region, 自动阈值分割方法 method, 亮或暗 lightAndDark)
{
HTuple use;
HOperatorSet.BinaryThreshold(hImage, out Region, method.ToString(), lightAndDark.ToString(), out use);
return true;
}
public static bool ThresholdALL(HObject hImage, out HObject Region, ThreSholdDef threSholdDef)
{
Region = null;
switch (threSholdDef._Segment)
{
case 阈值分割方式.Threshold:
//调用了普通阈值分割
return Threshold(hImage, out Region, threSholdDef._MinGray, threSholdDef._MaxGray);
case 阈值分割方式.BinaryThreshold:
//调用了自动阈值分割
return BinaryThreshold(hImage, out Region, threSholdDef._SegmentMothd, threSholdDef._LightOrDark);
default:
return false;
}
}
在主窗口界面进行调用阈值处理方法:
//对读取到的图片进行打散分割
HObject Region;
ThreSholdDef threSholdDef = new ThreSholdDef(0, 60);
ImageProcess.ThresholdALL(hImage, out Region, threSholdDef);
HObject ConnectionRegion;
HOperatorSet.Connection(Region, out ConnectionRegion);
HObject RRegion;
3.模板处理并绘制最小外接矩形
(1)方法介绍
参考Halcon提供的帮助文档,可以知道该运算符生成一个或多个矩形 以中心为中心 (GenRectangle2row,column) , 方向phi和 半边长度length1和length2. 方向以弧度量给出,表示两者之间的角度 水平轴和边(带有length1) (数学上为正)。 坐标系从 (0,0)(左上角)到 (宽度-1,高度-1)。看GetSystem和ResetObjDb在这种情况下。 通过传递一个角点元组,可以创建多个区域。
结合封装函数的思路,建议新建一个绘制矩形的数据存储类,也就是之前所创建的 DrawRectangle类,将中心坐标,旋转角度封装在里面:
internal class DrawRectangle
{
/// <summary>
/// 矩形的中行坐标
/// </summary>
public HTuple _CenterRow;
/// <summary>
/// 矩形的中心列坐标
/// </summary>
public HTuple _CenterCol;
/// <summary>
/// 矩形的旋转角度
/// </summary>
public HTuple Angle;
public HTuple len1;
public HTuple len2;
}
在 ImageProcess类直接调用参数即可,这里要注意你新创建的类库的命名空间(namespace)一定是于主窗口界面的命名空间一样,这样方便调用,当然可以直接声明进行调用,我这里为避免出错,使用同一命名控件:
public static void GenRectangle(HObject Region, out HObject ExternalRegion, out DrawRectangle rectangleDef, bool isMargin, HWindow hWindow)
{
if (isMargin == true)
{
//为了让填充区域变为框线显示
HOperatorSet.SetDraw(hWindow, "margin");
}
else
{
HOperatorSet.SetDraw(hWindow, "fill");
}
rectangleDef = new DrawRectangle();
//绘制最小矩形
HOperatorSet.SmallestRectangle2(Region, out rectangleDef._CenterRow, out rectangleDef._CenterCol, out rectangleDef.Angle, out rectangleDef.len1, out rectangleDef.len2);
HOperatorSet.GenRectangle2(out ExternalRegion, rectangleDef._CenterRow, rectangleDef._CenterCol, rectangleDef.Angle, rectangleDef.len1, rectangleDef.len2);
}
(2)主窗口界面调用绘制矩形GenRectangle函数
//对进行的模板匹配绘制最小外接矩形
DrawRectangle rectangleDef;
ImageProcess.GenRectangle(ConnectionRegion, out RRegion, out rectangleDef, true, hWindowControl1.HalconWindow);
(3)显示处理结果
在 ImageProcess类撰写展示结果图片,设置一个位置在 ROW, COL的字体颜色为blue的文本框,在里面输入我们要显示的结果
public static void ShowText(string Content, HWindow hWindow, HTuple ROW, HTuple COL)
{
HOperatorSet.DispText(hWindow, Content, "window", ROW, COL, "blue", new HTuple(), new HTuple());
}
在主函数里面,使用 ImageProcess.ShowText(str, hWindowControl1.HalconWindow, 12, 12);带哦用即可,str可以自用定义要显示的文本。
最终运行效果:
三、完整代码文件
完整代码文件我做了压缩包,供各位大佬指正~~~
小结:本次项目就是实现从本地文件夹里面选择路径上传进行图像基本处理,通过一个按钮实现所有算法流程,一定程度上简洁了界面,比较重要的知识点还是如何封装我们所需的数据方便我们的直接调用,日后还会继续更新,加油!