本文重点主要在于如何实现类的封装以达到简洁代码、理清逻辑顺序,并借此联合Halcon算法库实现基本图像处理。
一、项目创建以及设置Windows窗口
1.项目创建:
首先仍然是老朋友Visual Studio创建一个Windows窗体项目:
创建类库以供后续封装:
由于涉及到类的封装,还需要创建其他类库,在后面将会根据实际参数进行创建。
2、窗口设置
窗口具体调节可以参考零基础小白实现C#与Halcon算法的项目梦幻联动-CSDN博客进行设置,具体需要一个上传图片按钮、一个手动阈值分割图片按钮、一个自动阈值分割按钮、两个TextBox输入框获得用户设定的灰度值、一个ComBoBox下拉框选择阈值分割里面的大津法或者三角法,一个ComBoBox调节明暗,最终设置效果如下:
为了避免出现运行System.BadImageFormatException:“试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)”错误,事先将首选32位取消勾选。
二、基本功能实现--无封装
1.图片读取
为方便用户自行选择图片进行上传,采用OpenFileDialog图片读取方法直接从本地上传并显示在窗口上。
//定义图片变量
HObject TestImage;
/// <summary>
/// 第一个按钮触发事件为用户自行上传图片
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_read_Click(object sender, EventArgs e)
{
//让用户提取本地图片上传
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
HOperatorSet.ReadImage(out TestImage, openFileDialog.FileName);
}
#region 设置图片显示范围
//定义变量接受图片宽高
HTuple width, height;
//获得图片的宽高
HOperatorSet.GetImageSize(TestImage, out width, out height);
//设置显示范围
//HWindowControl.HalconWindow -->控件的句柄 设置显示范围
HOperatorSet.SetPart(hWindowControl1.HalconWindow, 0, 0, (height - 1), (width - 1));
#endregion
HOperatorSet.DispObj(TestImage, hWindowControl1.HalconWindow);
}
运行效果:
运行之后没问题,用户可以自行选择上传。
2、图片手动阈值分割处理--无封装
对阈值分割按钮创建新的按钮点击事件,前期先实现手动阈值处理方法,创建参数获得用户的输入值,并对图片实现手动分割:
HObject Region;//设置输出变量
/// <summary>
/// 阈值分割触发事件为手动阈值分割方法,需要用户在输入框输入灰度值调节
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_SD_Click(object sender, EventArgs e)
{
int minGray= int.Parse(textBox_MinGray.Text);
int maxGray = int.Parse(textBox_MaxGray.Text);
HOperatorSet.Threshold(TestImage, out Region, minGray, maxGray);
HOperatorSet.DispObj(Region, hWindowControl1.HalconWindow);
}
输入相关灰度值之后,点击阈值分割运行效果:
3、图片自动阈值分割处理--无封装
对自动阈值分割按钮创建新的按钮点击事件,使用BinaryThreshold方法实现自动阈值处理方法,创建下拉框参数获得用户选择,并对图片实现自动分割:
(1)创建下拉框选项并设置默认值
//给ComBox添加选项选择
comboBox_Method.Items.Add("max_separability");
comboBox_Method.Items.Add("smooth_histo");
comboBox_light.Items.Add("dark");
comboBox_light.Items.Add("light");
//默认选择首个选项
comboBox_Method.SelectedIndex = 0;
comboBox_light.SelectedIndex = 0;
(2)inaryThreshold方法实现自动阈值处理
/// <summary>
/// 自动阈值分割触发事件为自动阈值分割方法,需要用户在下拉框进行选择
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_auto_Click(object sender, EventArgs e)
{
HTuple use;//作为BinaryThreshold中使用的阈值
HOperatorSet.BinaryThreshold(TestImage,out Region, comboBox_Method.SelectedItem.ToString(), comboBox_light.SelectedItem.ToString(),out use);
HOperatorSet.DispObj(Region, hWindowControl1.HalconWindow);
}
点击自动分割运行效果:
在无封装的函数设计里面,通常具有较长的参数设置,冗长的代码,可读性虽然高但是同时容易被随意更改,所以直接进入本文重心,封装我们所需函数!
三、基本功能实现--封装函数
1.封装定义
(1)封装的含义:
封装就是将我们所需的数据以及函数放在一个单元集(也就是一个类里面)。
(2)封装的作用: 封装的作用在于保护或者防止代码(数据)被我们无意中破坏。在面向对象程序设计中数据被看作是一个中心的元素并且和使用它的函数结合的很密切,从而保护它不被其它的函数意外的修改。
(3)封装思路
封装的思路一般是将数据在程序中定义为公用的(public)我们将它们(作用域)定义为私有的(private)在很多方面会更好。私有的数据可以用两种方式来间接的控制。第一种方法,我们使用传统的存、取方法。第二种方法我们用属性(property),使用属性不仅可以控制存取数据的合法性,同时也提供了“读写”、“只读”、“只写”灵活的操作方法。
2.项目封装
(1)读取图片封装
新建一个图片加载处理类,在这个类作为我的图像处理类,并在窗口界面代码调用,
在ImageProcess类里面,新建了一个函数bool类型的readImage方法,将读取图片的方法写入到函数里面,直接移植就好了,加一个try-catch分支,防止读取图片错误,虽然概率很小。
在主窗口Form1界面只需要调用我们的封装函数即可:
/// <summary>
/// 第一个按钮触发事件为用户自行上传图片
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_read_Click(object sender, EventArgs e)
{
bool result = ImageProcess.readImage(hWindowControl1.HalconWindow);
if (result != true)
{
MessageBox.Show(" ImageProcess.readImage 失败");
}
}
(2)阈值分割封装处理
通过之前写过的阈值分割处理方法,在手动阈值分割里面要使用到最小最大灰度值,在自动阈值分割里面要使用到选择利用大津法或者三角法、对选择灰度或者亮度方面的自动处理,可以将这些需要的函数方法通过封装到类的方式同一封装起来,在函数里面直接调用即可
/// <summary>
/// Threshold作为阈值分割方法直接调用
/// </summary>
/// <param name="window"></param>
/// <param name="min"></param>
/// <param name="max"></param>
public static void Threshold(HWindow window,int min,int max)
{
HOperatorSet.Threshold(testImage, out Region, min, max);
HOperatorSet.DispObj(Region, window);
}
/// <summary>
/// BinaryThreshold作为自动阈值分割方法直接调用
/// </summary>
/// <param name="window"></param>
/// <param name="method"></param>
/// <param name="light"></param>
public static void BinaryThreshold( HWindow window,string method,string light)
{
HTuple use;//作为BinaryThreshold中使用的阈值
HOperatorSet.BinaryThreshold(testImage, out Region, method, light, out use);
HOperatorSet.DispObj(Region, window);
}
这是封装在类 ImageProcess里面,在主窗口界面直接获取文本框值之后直接撰写调用函数:
HObject Region;//设置输出变量
/// <summary>
/// 阈值分割触发事件为手动阈值分割方法,需要用户在输入框输入灰度值调节
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_SD_Click(object sender, EventArgs e)
{
int max = int.Parse(textBox_MaxGray.Text);
int min = int.Parse(textBox_MinGray.Text);
bool result = ImageProcess.readImage(hWindowControl1.HalconWindow);//事先调用图片获取图片变量
ImageProcess.Threshold(hWindowControl1.HalconWindow, max, min);
}
/// <summary>
/// 自动阈值分割触发事件为自动阈值分割方法,需要用户在下拉框进行选择
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_auto_Click(object sender, EventArgs e)
{
string method = comboBox_Method.SelectedItem.ToString();
string light = comboBox_light.SelectedItem.ToString();
bool result = ImageProcess.readImage(hWindowControl1.HalconWindow);
ImageProcess.BinaryThreshold(hWindowControl1.HalconWindow, method, light);
}
至此,简单的封装小程序就完成了,在这里还需要注意的是,这个小程序还有很多可以优化的地方,比如可以通过本地选择图片的方法,只留下一个按钮用于选择是否是自动还是手动,可以将相关数据定义在枚举类型里面直接调用,后续会说明这个项目,如有不当之处,希望各位大佬指正~~~
(3)完整代码
ImageProcess类:
using HalconDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public class ImageProcess
{
static HObject testImage;
static HObject Region;
/// <summary>
/// readImage函数作为图片处理,在主界面直接调用即可
/// </summary>
/// <param name="window"></param>
/// <returns></returns>
public static bool readImage(HWindow window)
{
try
{
//让用户提取本地图片上传
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
HOperatorSet.ReadImage(out testImage, openFileDialog.FileName);
}
#region 设置图片显示范围
//定义变量接受图片宽高
HTuple width, height;
//获得图片的宽高
HOperatorSet.GetImageSize(testImage, out width, out height);
//设置显示范围
//HWindowControl.HalconWindow -->控件的句柄 设置显示范围
HOperatorSet.SetPart(window, 0, 0, (height - 1), (width - 1));
#endregion
HOperatorSet.DispObj(testImage, window);
return true;
}
catch
{
testImage = null;
return false;
}
}
/// <summary>
/// Threshold作为阈值分割方法直接调用
/// </summary>
/// <param name="window"></param>
/// <param name="min"></param>
/// <param name="max"></param>
public static void Threshold(HWindow window,int min,int max)
{
HOperatorSet.Threshold(testImage, out Region, min, max);
HOperatorSet.DispObj(Region, window);
}
/// <summary>
/// BinaryThreshold作为自动阈值分割方法直接调用
/// </summary>
/// <param name="window"></param>
/// <param name="method"></param>
/// <param name="light"></param>
public static void BinaryThreshold( HWindow window,string method,string light)
{
HTuple use;//作为BinaryThreshold中使用的阈值
HOperatorSet.BinaryThreshold(testImage, out Region, method, light, out use);
HOperatorSet.DispObj(Region, window);
}
}
}
Form1主窗口代码:
using HalconDotNet;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//给ComBox添加选项选择
comboBox_Method.Items.Add("max_separability");
comboBox_Method.Items.Add("smooth_histo");
comboBox_light.Items.Add("dark");
comboBox_light.Items.Add("light");
//默认选择首个选项
comboBox_Method.SelectedIndex = 0;
comboBox_light.SelectedIndex = 0;
}
//定义图片变量
static HObject TestImage;
/// <summary>
/// 第一个按钮触发事件为用户自行上传图片
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_read_Click(object sender, EventArgs e)
{
bool result = ImageProcess.readImage(hWindowControl1.HalconWindow);
if (result != true)
{
MessageBox.Show(" ImageProcess.readImage 失败");
}
}
HObject Region;//设置输出变量
/// <summary>
/// 阈值分割触发事件为手动阈值分割方法,需要用户在输入框输入灰度值调节
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_SD_Click(object sender, EventArgs e)
{
int max = int.Parse(textBox_MaxGray.Text);
int min = int.Parse(textBox_MinGray.Text);
bool result = ImageProcess.readImage(hWindowControl1.HalconWindow);//事先调用图片获取图片变量
ImageProcess.Threshold(hWindowControl1.HalconWindow, max, min);
}
/// <summary>
/// 自动阈值分割触发事件为自动阈值分割方法,需要用户在下拉框进行选择
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_auto_Click(object sender, EventArgs e)
{
string method = comboBox_Method.SelectedItem.ToString();
string light = comboBox_light.SelectedItem.ToString();
bool result = ImageProcess.readImage(hWindowControl1.HalconWindow);
ImageProcess.BinaryThreshold(hWindowControl1.HalconWindow, method, light);
}
}
}