1、加载并显示图像功能。
2、图像拖动缩放功能。
3、绘制ROI:矩形、方向矩形、圆形、椭圆形。
4、创建模板:参数修改、模板轮廓显示。
5、匹配模板:参数修改、匹配轮廓显示、匹配结果显示。
HalconModelSet_Ex: 该目录空间下存放halcon 算子相关模型(算子参数)。
HalconTools :该目录空间下存放图像转换、ROI相关(类、方法、事件)。
Models:该目录空间存放所有相关模型(参数);
MainForm:将所有控件放到了一个界面(应该拆分放到创建的Views目录)。
窗体类
private System.Windows.Forms.Panel panel_OperatorBar;
private System.Windows.Forms.Panel panel_MessageBar;
private System.Windows.Forms.Panel panel_ImageWindowBar;
private System.Windows.Forms.GroupBox GroupBox_Message;
private System.Windows.Forms.RichTextBox rtbx_MessageLog;
private System.Windows.Forms.GroupBox groupBox_Operator;
private System.Windows.Forms.GroupBox groupBox_ImageWin;
private System.Windows.Forms.TextBox tbx_LoadImage;
private System.Windows.Forms.Button btn_ImageLoad;
private System.Windows.Forms.Panel panel_ImageListBar;
private System.Windows.Forms.Panel panel_ImageWindow;
private System.Windows.Forms.Panel panel_ImageInfo;
private HalconDotNet.HSmartWindowControl ImageWindow;
private System.Windows.Forms.TextBox tbx_WindowState;
private System.Windows.Forms.ComboBox cbx_ImageList;
private System.Windows.Forms.Button btn_DrawROI;
private System.Windows.Forms.ContextMenuStrip cmsx_RightMenu;
private System.Windows.Forms.ToolStripMenuItem 清空内容ToolStripMenuItem;
private System.Windows.Forms.ComboBox cbx_DrawObjectList;
private System.Windows.Forms.ContextMenuStrip cmsx_RoiRightMenu;
private System.Windows.Forms.ToolStripMenuItem 删除ROItoolStripMenuItem;
private System.Windows.Forms.Panel panel_LoadImage;
private System.Windows.Forms.Panel panel_Match;
private System.Windows.Forms.TabControl tabControl_Match;
private System.Windows.Forms.TabPage tabPage_RoiList;
private System.Windows.Forms.ListBox lbx_RoiList;
private System.Windows.Forms.TabPage tabPage_RoiParams;
private System.Windows.Forms.TabPage tabPage_MatchParams;
private System.Windows.Forms.Button btn_CreateTemplate;
private System.Windows.Forms.Button btn_FindTeamplate;
private System.Windows.Forms.Panel panel_CreateTemplateOperate;
private System.Windows.Forms.Panel panel_CreateTemplateParam;
private System.Windows.Forms.Button btn_Eraser;
private System.Windows.Forms.NumericUpDown numUD_EraserSize;
private System.Windows.Forms.Panel panel_FindTemplateOperate;
private System.Windows.Forms.Panel panel_FindTemplateParam;
private System.Windows.Forms.TabPage tabPage_MatchResult;
private System.Windows.Forms.Panel panel_MatchItem;
private System.Windows.Forms.Panel panel_MathingResult;
private System.Windows.Forms.Button btn_ConfirmParam;
private System.Windows.Forms.Button btn_FlushCreateingParams;
private System.Windows.Forms.TextBox tbx_AngleExtent_Creating;
private System.Windows.Forms.TextBox tbx_AngleStep_Creating;
private System.Windows.Forms.TextBox tbx_AngleStart_Creating;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.ComboBox cbx_Metric_Creating;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.ComboBox cbx_Optimization_Creating;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.DomainUpDown dudx_Contrast_Creating;
private System.Windows.Forms.DomainUpDown dudx_MinContrast_Creating;
private System.Windows.Forms.DomainUpDown dudx__NumLevels_Creating;
private System.Windows.Forms.DataGridView dgvx_MatchResult;
private System.Windows.Forms.DomainUpDown dudx_NumLevels_Matching;
private System.Windows.Forms.DomainUpDown dudx_MinScore_Matching;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Label label11;
private System.Windows.Forms.ComboBox cbx_SubPixel_Matching;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.Label label15;
private System.Windows.Forms.Label label16;
private System.Windows.Forms.TextBox tbx_AngleStart_Matching;
private System.Windows.Forms.TextBox tbx_AngleExtent_Matching;
private System.Windows.Forms.NumericUpDown nudx_NumMatches_Matching;
private System.Windows.Forms.DomainUpDown dudx_MaxOverlap_Matching;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.DomainUpDown dudx_Greediness_Matching;
private System.Windows.Forms.Label label17;
private System.Windows.Forms.ComboBox cbx_DrawFindRegion;
private System.Windows.Forms.Button btn_DrawFindRegion;
private System.Windows.Forms.DomainUpDown dudx_MixScale_Matching;
private System.Windows.Forms.Label label14;
private System.Windows.Forms.DomainUpDown dudx_MaxScale_Matching;
private System.Windows.Forms.Label label18;
private System.Windows.Forms.ComboBox cbx_MatchingModelMethod;
private System.Windows.Forms.Label label19;
private System.Windows.Forms.DomainUpDown dudx_ScaledMax_Creating;
private System.Windows.Forms.Label label20;
private System.Windows.Forms.DomainUpDown dudx_ScaledMin_Creating;
private System.Windows.Forms.Label label21;
private System.Windows.Forms.Label label22;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.DataGridViewTextBoxColumn Column_Index;
private System.Windows.Forms.DataGridViewTextBoxColumn Column_Row;
private System.Windows.Forms.DataGridViewTextBoxColumn Column_Column;
private System.Windows.Forms.DataGridViewTextBoxColumn Column_Score;
private System.Windows.Forms.DataGridViewTextBoxColumn Column_Scaled;
#region 字段、属性、集合
private string InitImagePath = Environment.CurrentDirectory + "\\1.png"; //初始图像路径
private string CurrentImagePath; //当前图像路径
private HWindow WindowID = null; //窗口ID
private HImage _sourceImage; //原图
private HImage _grayImage; //灰度图
private HImage _currentImage; //当前图像
private HImage _backgroudImage; //空白背景图
private HImage _matchResultImage; //空白背景图
private int _imageChannels = 1; //图像通道
private int _drawObjIndex = -1; //绘制对象索引
private HTuple ModelID; //模板ID
private HObject ModelImage; //模板图像
private List<HObject> MatchingContoursAffinTrans; //匹配轮廓
private HObject CreateContours; //创建的模板轮廓
private HObject MatchUnitContours; //匹配到的合并的轮廓
private string removeItemID = ""; //移除ROI?
private string[] _imageName = { "原图", "灰度图","模板图像", "模板轮廓图", "匹配结果图" }; //图像选择显示
private string[] _drawObjectList = { "矩形1", "矩形2", "圆形", "椭圆", "多边形" }; //ROI显示选择
private RoiTools RoiTools = new RoiTools(); //ROI工具
private bool _isGrayConvert = true; //是否获取灰度图
//消息日志显示颜色
private Color[] MessageColors = new Color[] {System.Drawing.Color.Black, System.Drawing.Color.Aqua, System.Drawing.Color.DodgerBlue, System.Drawing.Color.DarkGreen };
///
/// 当前图像
///
public HImage CurrentImage
{
get { return _currentImage; }
private set {
_currentImage = value;
if (_imageChannels==1){
HImage tempImage = _currentImage.Clone();
HOperatorSet.GetImagePointer1(tempImage, out HTuple pointer, out HTuple type, out HTuple width, out HTuple height);
_matchResultImage = new HImage();
_backgroudImage = new HImage("byte", width, height);
_matchResultImage.GenImage3( type, width, height, pointer, pointer, pointer);
}else{
_matchResultImage = _currentImage.Clone();
HOperatorSet.GetImageSize(_currentImage, out HTuple width, out HTuple height);
_backgroudImage = new HImage("byte", width, height);
}
}
}
///
/// 原图
///
public HImage SourceImage
{
get { return _sourceImage; }
private set { _sourceImage = value; }
}
///
/// 灰度图
///
public HImage GrayImage
{
get { return _grayImage; }
private set { _grayImage = value; }
}
public HImage MatchResultImage { get => _matchResultImage; set => _matchResultImage = value; }
///
/// 图像集合
///
public List<ModelsBase> ImageModelList = new List<ModelsBase>();
///
/// 模板参数模型(创建模板时的参数)
///
private CreateScaledShapeModel createScaledShapeModel = new CreateScaledShapeModel();
///
/// 模板匹配参数模型(查找时的参数)
///
private FindScaledShapeModel findScaledShapeModel = new FindScaledShapeModel();
#endregion
#region 窗体初始化、加载
public MainForm()
{
InitializeComponent();
this.Height = 800;
this.CenterToParent();
this.CenterToScreen();
WindowID = ImageWindow.HalconWindow;
}
private void MainForm_Load(object sender, EventArgs e)
{
//事件绑定
this.MouseWheel += new System.Windows.Forms.MouseEventHandler(CustomMouseWheel);
this.ImageWindow.HMouseMove += ImageWindow_HMouseMove;
this.RoiTools.MessageUpdateEvents += OnUpdateMessageCallback;
this.RoiTools.DrawObjectChangedEvents += OnDrawObjectChangedCallback;
///初始化控件值
cbx_ImageList.DataSource = _imageName;
cbx_ImageList.SelectedIndex = 1;
cbx_DrawObjectList.DataSource = _drawObjectList;
cbx_DrawObjectList.SelectedIndex = 0;
tbx_LoadImage.Text = InitImagePath;
SetValueCreating();
}
///
/// 窗体关闭
///
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
RoiTools.UnEventBinding();
}
#endregion
#region 获取灰度值
///
/// 图像信息显示:移动显示鼠标位置及其灰度值
///
private void MoveShowImageInfo(HMouseEventArgs e)
{
if (SourceImage == null) return;
try
{
// 获取鼠标位置及所在位置的灰度值
if (GrayImage != null)
{
GetGrayValue((int)e.X, (int)e.Y);
}
}
catch (Exception ex)
{
UpdateMessage($"Mouse Move 异常:{ex.Message}");
}
}
///
/// 获取鼠标位置,及所在位置的灰度值。
///
private void GetGrayValue(int mouseX, int mouseY)
{
HOperatorSet.GetImageSize((HObject)GrayImage, out HTuple width, out HTuple height);
// 确保鼠标位置在图像范围内
if (mouseX >= 0 && mouseX < width && mouseY >= 0 && mouseY < height)
{
HTuple hv_GrayValue;
HOperatorSet.GetGrayval((HObject)GrayImage, mouseY, mouseX, out hv_GrayValue);
// 显示当前点位和灰度值
string text = $"$(W:{width},H:{height})\t" +
$"X: ({mouseX},Y: {mouseY}) " +
$"(R:{hv_GrayValue},G:{hv_GrayValue},B:{hv_GrayValue})";
Console.WriteLine(text);
tbx_WindowState.Text = text;
}
}
#endregion
#region 消息栏更新方法
///
/// 更新消息
///
private void UpdateMessage(string message)
{
rtbx_MessageLog.Invoke(new Action(() =>
{
if (rtbx_MessageLog.Lines.Length > 5000)
{
rtbx_MessageLog.Clear();
}
int index = rtbx_MessageLog.Lines.Length;
string msg = $"{(index == 0 ? 1 : index)}\t" + $"{DateTime.Now.ToString()}: {message}{Environment.NewLine}";
rtbx_MessageLog.AppendText(msg); ;
rtbx_MessageLog.SelectionStart = rtbx_MessageLog.Text.Length;
rtbx_MessageLog.Focus();
}));
}
///
/// 更新消息,异常显示红色(error level = 1)。正常显示其他颜色
///
private void UpdateMessage(string message,int errorLevel)
{
rtbx_MessageLog.Invoke(new Action(() =>
{
if (rtbx_MessageLog.Lines.Length > 5000)
{
rtbx_MessageLog.Clear();
}
int index = rtbx_MessageLog.Lines.Length;
string msg = $"{(index == 0 ? 1 : index)}\t" + $"{DateTime.Now.ToString()}: {message}{Environment.NewLine}";
int startIndex = rtbx_MessageLog.Text.Length;
rtbx_MessageLog.AppendText(msg);
int length = rtbx_MessageLog.Text.Length;
if (errorLevel == 1) //异常定义
{
// 设置选定文本的颜色为红色
rtbx_MessageLog.Select(startIndex, length); // 选择前 lengthToHighlight 个字符
rtbx_MessageLog.SelectionColor = Color.Red; // 设置颜色为红色
// 取消选择
rtbx_MessageLog.Select(rtbx_MessageLog.Text.Length, 0); // 取消选择
rtbx_MessageLog.SelectionColor = Color.Black; // 恢复后续文本颜色
}
else
{
Random random = new Random();
int indexColor = random.Next(MessageColors.Length);
// 设置选定文本的颜色为红色
rtbx_MessageLog.Select(startIndex, length); // 选择前 lengthToHighlight 个字符
rtbx_MessageLog.SelectionColor = MessageColors[indexColor]; // 设置颜色为红色
// 取消选择
rtbx_MessageLog.Select(rtbx_MessageLog.Text.Length, 0); // 取消选择
rtbx_MessageLog.SelectionColor = Color.Black; // 恢复后续文本颜色
}
rtbx_MessageLog.SelectionStart = rtbx_MessageLog.Text.Length;
rtbx_MessageLog.Focus();
}));
}
///
/// 消息更新回调
///
private void OnUpdateMessageCallback(object sender, string message)
{
UpdateMessage(message,0);
}
///
/// 响应:绘制对象变更回调(事件被触发后响应)
///
private void OnDrawObjectChangedCallback(object sender, DrawingObjectModel drawObj)
{
if (drawObj != null)
{
switch (drawObj.Operate)
{
case RoiOperateModel.None:
break;
case RoiOperateModel.添加:
lbx_RoiList.Items.Add($"ROI_{drawObj.DrawID.ToString()}");
break;
case RoiOperateModel.删除:
string id = drawObj.DrawID.ToString();
foreach (var item in lbx_RoiList.Items)
{
UpdateMessage(item.ToString());
}
lbx_RoiList.Items.Remove(id);
break;
case RoiOperateModel.修改:
break;
case RoiOperateModel.查询:
if (lbx_RoiList.Items.Contains($"ROI_{drawObj.DrawID}"))
{
lbx_RoiList.SelectedItem = $"ROI_{drawObj.DrawID}";
}
break;
default:
break;
}
}
}
#endregion
#region 控件触发
///
/// 加载图像按钮
///
private void btn_ImageLoad_Click(object sender, EventArgs e)
{
LoadImage(tbx_LoadImage.Text);
}
///
/// 选择显示的图像
///
private void cbx_ImageList_SelectedIndexChanged(object sender, EventArgs e)
{
if (CurrentImage == null) return;
switch (cbx_ImageList.SelectedIndex)
{
case 0:
CurrentImage = SourceImage.Clone();
UpdateMessage("设置当前图像为原图!");
break;
case 1:
CurrentImage = GrayImage.Clone();
UpdateMessage("设置当前图像为灰度图!");
break;
case 2:
if (ModelImage == null) return;
HOperatorSet.DispObj(_backgroudImage, WindowID);
HOperatorSet.DispObj(ModelImage, WindowID);
UpdateMessage("设置当前图像为模板图图像!");
return;
case 3:
HOperatorSet.DispObj(CurrentImage, WindowID);
DispContoursRegion(CreateContours);
UpdateMessage("设置当前图像为匹配结果图像!");
return;
case 4:
CurrentImage = MatchResultImage.Clone();
DispContoursRegion(MatchUnitContours);
UpdateMessage("设置当前图像为匹配结果图像!");
return;
default:
break;
}
HOperatorSet.DispObj(CurrentImage, WindowID);
}
///
/// 清空log消息
///
private void 清空内容ToolStripMenuItem_Click(object sender, EventArgs e)
{
rtbx_MessageLog.Clear();
}
///
/// 消息栏区域鼠标按下:显示右键菜单
///
private void rtbx_MessageLog_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
cmsx_RightMenu.Show(rtbx_MessageLog, e.X, e.Y);
}
}
///
/// 绘制ROI
///
private void btn_DrawROI_Click(object sender, EventArgs e)
{
if (SourceImage == null) return;
switch (_drawObjIndex)
{
case 0:
RoiTools.DrawROI(RoiTypeModel.RECTANGLE1, WindowID);
break;
case 1:
RoiTools.DrawROI(RoiTypeModel.RECTANGLE2, WindowID);
break;
case 2:
RoiTools.DrawROI(RoiTypeModel.CIRCLE, WindowID);
break;
case 3:
RoiTools.DrawROI(RoiTypeModel.ELLIPSE, WindowID);
break;
case 4:
RoiTools.DrawROI(RoiTypeModel.XLD_CONTOUR, WindowID);
break;
default:
RoiTools.DrawROI(RoiTypeModel.RECTANGLE1, WindowID);
break;
}
}
///
/// 绘制对象(ROI)类型索引
///
private void cbx_DrawObjectList_SelectedIndexChanged(object sender, EventArgs e)
{
_drawObjIndex = cbx_DrawObjectList.SelectedIndex;
}
///
/// 删除ROI
///
private void 删除ROItoolStripMenuItem_Click(object sender, EventArgs e)
{
Int64 id = Int64.Parse(removeItemID.Split('_')[1]);
var draw = RoiTools.RoiList.FirstOrDefault(obj => obj.DrawObject.ID == id);
if (draw != null)
{
lbx_RoiList.Items.Remove(removeItemID);
RoiTools.DetachtROI(WindowID, id);
}
}
///
/// ROI列表区域鼠标按下
///
private void lbx_RoiList_MouseDown(object sender, MouseEventArgs e)
{
// 检查是否是右键按下
if (e.Button == MouseButtons.Right)
{
// 获取鼠标点击的位置
int index = lbx_RoiList.IndexFromPoint(e.Location);
if (index != System.Windows.Forms.ListBox.NoMatches) // 确保点击的是有效项
{
if (index != System.Windows.Forms.ListBox.NoMatches) // 确保点击的是有效项
{
// 记录选中的项
removeItemID = lbx_RoiList.Items[index].ToString();
// 在鼠标位置显示右键菜单
cmsx_RoiRightMenu.Show(lbx_RoiList, e.Location);
}
}
}
}
///
/// 创建模板
///
private void btn_CreateTemplate_Click(object sender, EventArgs e)
{
UpdateMessage("创建模板", 0);
if (GrayImage == null) return;
if (RoiTools.RoiList.Count < 1) return;
HOperatorSet.DispObj(CurrentImage, WindowID);
int result = -1;
switch (cbx_MatchingModelMethod.SelectedIndex)
{
case 0:
createScaledShapeModel.ModelCreatingMethod = 0;
result = RoiTools.CreateTeamplate(GrayImage, createScaledShapeModel, out CreateContours, out ModelID, out ModelImage);
break;
case 1:
createScaledShapeModel.ModelCreatingMethod = 1;
result = RoiTools.CreateTeamplate(GrayImage, createScaledShapeModel, out CreateContours,
out ModelID, out ModelImage);
break;
default:
break;
}
if (result == 0)
{
MatchingContoursAffinTrans = null;
DispContoursRegion(CreateContours);
}
}
///
/// 查找模板
///
private void btn_FindTeamplate_Click(object sender, EventArgs e)
{
//执行模板匹配
UpdateMessage("执行模板匹配", 0);
if (ModelImage == null || ModelID == null) return;
try
{
SetValueMatching();
HOperatorSet.DispObj(CurrentImage, WindowID);
HTuple row = new HTuple();
HTuple column = new HTuple();
HTuple angle = new HTuple();
HTuple score = new HTuple();
HTuple scaled = new HTuple();
switch (createScaledShapeModel.ModelCreatingMethod)
{
case 0:
findScaledShapeModel.ModelCreatingMethod = 0;
RoiTools.FindTeamplate(CurrentImage, ModelID, findScaledShapeModel, out MatchingContoursAffinTrans, out row, out column, out angle, out scaled, out score);
break;
case 1:
findScaledShapeModel.ModelCreatingMethod = 1;
RoiTools.FindTeamplate(CurrentImage, ModelID, findScaledShapeModel, out MatchingContoursAffinTrans, out row, out column, out angle, out scaled, out score);
break;
default:
break;
}
dgvx_MatchResult.Rows.Clear();
if (score.Length > 0)
{
if (scaled != null)
{
for (int i = 0; i < row.Length; i++)
{
UpdateMessage($"匹配结果:[{row[i].D.ToString("0.000")},{column[i].D.ToString("0.000")},{angle[i].D.ToString("0.000")}],分数: {score[i].D.ToString("0.000")},缩放:[{scaled.D.ToString("0.000")}]", 0);
dgvx_MatchResult.Rows.Add((i), row[i].D.ToString("0.000"), column[i].D.ToString("0.000"), score[i].D.ToString("0.000"), scaled[i].D.ToString("0.000"));
}
}
else
{
for (int i = 0; i < row.Length; i++)
{
UpdateMessage($"匹配结果:[{row[i].D.ToString("0.000")},{column[i].D.ToString("0.000")},{angle[i].D.ToString("0.000")}],分数: {score[i].D.ToString("0.000")}", 0);
dgvx_MatchResult.Rows.Add((i), row[i].D.ToString("0.000"), column[i].D.ToString("0.000"), score[i].D.ToString("0.000"), scaled[i].D.ToString("0.000"));
}
}
HOperatorSet.GenEmptyRegion(out MatchUnitContours);
foreach (var contours in MatchingContoursAffinTrans)
{
//生成轮廓xld区域
HOperatorSet.GenRegionContourXld(contours, out HObject Region, "margin");
HOperatorSet.Union2(MatchUnitContours, Region, out MatchUnitContours);
}
HOperatorSet.DispRegion(MatchUnitContours, WindowID);
}
else
UpdateMessage($"匹配失败,模板ID:{ModelID}。请检查参数?", 1);
}
catch (Exception ex)
{
UpdateMessage($"匹配失败,{ex.Message}", 1);
}
}
///
/// 刷新参数显示
///
private void btn_FlushCreateingParams_Click(object sender, EventArgs e)
{
DisplyValueCreating();
}
///
/// 确认创建模板参数
///
private void btn_ConfirmParam_Click(object sender, EventArgs e)
{
SetValueCreating();
}
///
/// ROI列表控件选择变更
///
private void lbx_RoiList_SelectedIndexChanged(object sender, EventArgs e)
{
Int64 drawID = long.Parse(lbx_RoiList.SelectedItem.ToString().Split('_')[1]);
var drawObj = RoiTools.RoiList.FirstOrDefault(x => x.DrawObject.ID.Equals(drawID));
//HOperatorSet.DetachDrawingObjectFromWindow(WindowID, drawObj.DrawObject);
}
///
/// 图像窗口移动鼠标
///
private void ImageWindow_HMouseMove(object sender, HMouseEventArgs e)
{
MoveShowImageInfo(e);
}
///
/// 滚动缩放图像
///
private void CustomMouseWheel(object sender, MouseEventArgs e)
{
System.Drawing.Point pt = this.Location;
int leftBorder = ImageWindow.Location.X;
int rightBorder = ImageWindow.Location.X + ImageWindow.Size.Width;
int topBorder = ImageWindow.Location.Y;
int bottomBorder = ImageWindow.Location.Y + ImageWindow.Size.Height;
//判断鼠标指针是否在控件内部
if (e.X > leftBorder && e.X < rightBorder && e.Y > topBorder && e.Y < bottomBorder)
{
MouseEventArgs newe = new MouseEventArgs(e.Button, e.Clicks, e.X - pt.X, e.Y - pt.Y, e.Delta);
ImageWindow.HSmartWindowControl_MouseWheel(sender, newe);
}
}
#endregion
#region ROI参数获取与设置
///
/// 设置创建模板参数
///
private void SetValueCreating()
{
createScaledShapeModel.AngleStart = double.Parse(tbx_AngleStart_Creating.Text);
createScaledShapeModel.AngleExtent = double.Parse(tbx_AngleExtent_Creating.Text);
createScaledShapeModel.AngleStep = double.Parse(tbx_AngleStep_Creating.Text);
createScaledShapeModel.Optimization = cbx_Optimization_Creating.Text;
createScaledShapeModel.Metric = cbx_Metric_Creating.Text;
createScaledShapeModel.NumLevels = int.Parse(dudx__NumLevels_Creating.Text);
createScaledShapeModel.Contrast = int.Parse(dudx_Contrast_Creating.Text);
createScaledShapeModel.MinContrast = int.Parse(dudx_MinContrast_Creating.Text);
createScaledShapeModel.ScaleMin = double.Parse(dudx_ScaledMin_Creating.Text);
createScaledShapeModel.ScaleMax = double.Parse(dudx_ScaledMax_Creating.Text);
createScaledShapeModel.ScaleStep = 0.01;
UpdateMessage($"设置对象参数:{createScaledShapeModel.GetType().Name}", 0);
}
///
/// 获取创建模板参数
///
private void DisplyValueCreating()
{
tbx_AngleStart_Creating.Text = createScaledShapeModel.AngleStart.ToString();
tbx_AngleExtent_Creating.Text = createScaledShapeModel.AngleExtent.ToString();
tbx_AngleStep_Creating.Text = createScaledShapeModel.AngleStep.ToString();
cbx_Optimization_Creating.Text = createScaledShapeModel.Optimization;
cbx_Metric_Creating.Text = createScaledShapeModel.Metric;
dudx__NumLevels_Creating.Text = createScaledShapeModel.NumLevels.ToString();
dudx_Contrast_Creating.Text = createScaledShapeModel.Contrast.ToString();
dudx_MinContrast_Creating.Text = createScaledShapeModel.MinContrast.ToString();
dudx_ScaledMin_Creating.Text = createScaledShapeModel.ScaleMin.ToString();
dudx_ScaledMax_Creating.Text = createScaledShapeModel.ScaleMax.ToString();
UpdateMessage($"更新参数值到UI:{createScaledShapeModel.GetType().Name}", 0);
}
///
/// 设置查找模板参数
///
private void SetValueMatching()
{
findScaledShapeModel.AngleStart = double.Parse(tbx_AngleStart_Matching.Text);
findScaledShapeModel.AngleExtent = double.Parse(tbx_AngleExtent_Matching.Text);
findScaledShapeModel.MinScore = double.Parse(dudx_MinScore_Matching.Text);
findScaledShapeModel.NumMatches = int.Parse(nudx_NumMatches_Matching.Value.ToString());
if (int.TryParse(dudx_NumLevels_Matching.Text, out int reuslt))
findScaledShapeModel.NumLevels = reuslt;
findScaledShapeModel.SubPixel = cbx_SubPixel_Matching.Text;
findScaledShapeModel.MaxOverlap = double.Parse(dudx_MaxOverlap_Matching.Text);
findScaledShapeModel.Greediness = double.Parse(dudx_Greediness_Matching.Text);
findScaledShapeModel.ScaleMin = double.Parse(dudx_MixScale_Matching.Text);
findScaledShapeModel.ScaleMax = double.Parse(dudx_MaxScale_Matching.Text);
}
#endregion
#region 图像加载
///
/// 加载图像
///
public void LoadImage(string path)
{
CurrentImagePath = path;
OpenFileDialog openFile = new OpenFileDialog();
//判断设置初始目录
openFile.InitialDirectory = Environment.CurrentDirectory;
if (Directory.Exists(CurrentImagePath)) openFile.InitialDirectory = CurrentImagePath;
if (Directory.Exists("D:\\Image")) openFile.InitialDirectory = "D:\\Image";
if (Directory.Exists(CurrentImagePath)) openFile.InitialDirectory = CurrentImagePath;
if (File.Exists(CurrentImagePath)) openFile.InitialDirectory = System.IO.Path.GetDirectoryName(CurrentImagePath);
//判断读取图像
if (openFile.ShowDialog() == DialogResult.OK)
{
CurrentImagePath = openFile.FileName;
tbx_LoadImage.Text = openFile.FileName;
try
{
ImageFormatTools.ReadImage(CurrentImagePath, out _sourceImage, out string info);
if (info != null)
{
UpdateMessage($"图像直接加载异常{info}!!");
UpdateMessage($"使用Bitmap转换图像,图像路径:{CurrentImagePath}");
}
//设置当前图像显示:原图
CurrentImage = SourceImage.Clone();
//灰度转换
if (_isGrayConvert)
{
_grayImage = _sourceImage.Rgb1ToGray();
CurrentImage = _grayImage.Clone(); //设置当前图像显示:原图
UpdateMessage($"执行图像灰度转换:{CurrentImagePath}");
}
HOperatorSet.DispObj(CurrentImage, WindowID);
UpdateMessage($"图像加载完成,图像路径:{CurrentImagePath}");
GetImageChannel(CurrentImage);
ImageModelList.Clear(); //先清空图像
//添加图像到集合
ImageModel imageModels = new ImageModel();
imageModels.FileName = CurrentImagePath;
imageModels.Directory = System.IO.Path.GetDirectoryName(CurrentImagePath);
imageModels.SourceImage = _sourceImage.Clone();
imageModels.GrayImage = _grayImage?.Clone();
imageModels.ID = ImageModelList.Count;
ImageModelList.Add(imageModels);
}
catch (Exception ex)
{
UpdateMessage($"图像加载失败:{ex.Message}");
}
}
}
#endregion
#region 其他方法
///
/// 显示轮廓
///
public void DispContoursRegion(HObject contours)
{
if (contours == null) return;
HOperatorSet.SetColor(WindowID, "green");
HOperatorSet.SetLineWidth(WindowID, 3);
if (!(contours is HRegion))
{
HOperatorSet.GenRegionContourXld(contours, out HObject region, "margin");
HOperatorSet.DispRegion(region, WindowID);
}
else
{
HOperatorSet.DispRegion(contours, WindowID);
}
}
///
/// 获取图像通道数
///
private void GetImageChannel(HImage hImage)
{
HOperatorSet.CountChannels(hImage, out HTuple channels);
_imageChannels = channels;
}
#endregion
ROI工具类
#region 事件定义、声明、绑定、触发方法
///
/// 消息更新事件
///
public event EventHandler<string> MessageUpdateEvents;
///
/// 绘制对象(ROI)变更事件
///
public event EventHandler<DrawingObjectModel> DrawObjectChangedEvents;
///
/// 取消所有事件绑定
///
public void UnEventBinding()
{
MessageUpdateEvents = null;
DrawObjectChangedEvents = null;
}
#region 触发事件
///
/// 触发事件:消息更新
///
public void OnMessageUpdateEvents(string message)
{
MessageUpdateEvents?.Invoke(this, message);
}
///
/// 触发事件:绘制对象变更
///
public void OnDrawObjectChangedEvents(DrawingObjectModel drawObj)
{
DrawObjectChangedEvents?.Invoke(this, drawObj);
}
#endregion
#endregion
#region 字段、属性、集合
//ROI模型集合
private List<DrawingObjectModel> _roiList = new List<DrawingObjectModel>();
///
/// ROI模型集合
///
public List<DrawingObjectModel> RoiList
{
get => _roiList;
private set => _roiList = value;
}
#endregion
#region ROI添加、删除
///
/// 绘制ROI:根据ROI类型,绘制ROI到指定窗体ID
///
public void DrawROI(RoiTypeModel type, HWindow windowID)
{
HTuple[] param;
HOperatorSet.SetColor(windowID, "blue");
HOperatorSet.SetLineWidth(windowID, 1);
switch (type)
{
case RoiTypeModel.RECTANGLE1:
param = new HTuple[] { 100, 100, 200, 200 };
GenROI(type, param, windowID);
break;
case RoiTypeModel.RECTANGLE2:
param = new HTuple[] { 200, 200, 0, 100, 100 };
GenROI(type, param, windowID);
break;
case RoiTypeModel.CIRCLE:
param = new HTuple[] { 100, 100, 100 };
GenROI(type, param, windowID);
break;
case RoiTypeModel.ELLIPSE:
param = new HTuple[] { 100, 100, 50, 50, 50 };
GenROI(type, param, windowID);
break;
case RoiTypeModel.CIRCLE_SECTOR:
break;
case RoiTypeModel.ELLIPSE_SECTOR:
break;
case RoiTypeModel.LINE:
break;
case RoiTypeModel.XLD_CONTOUR:
param = new HTuple[] { 200, 200 };
param[0].Append(new HTuple(100, 200));
param[1].Append(new HTuple(300, 300));
GenROI(type, param, windowID);
break;
case RoiTypeModel.TEXT:
break;
default:
param = new HTuple[] { 100, 100, 200, 200 };
GenROI(type, param, windowID);
break;
}
}
///
/// 移除ROI对象: 移除指定窗体上的指定ROI句柄。
///
public void DetachtROI(HWindow windowID, Int64 drawID)
{
var drawObj = RoiList.FirstOrDefault(drawobj => drawobj.DrawID == drawID);
if (drawObj != null)
{
HOperatorSet.DetachDrawingObjectFromWindow(windowID, drawObj.DrawObject);
RoiList.Remove(drawObj);
OnDrawObjectChangedEvents(new DrawingObjectModel(RoiOperateModel.删除, drawID));
}
}
///
/// 重新加载ROI
///
public void ReAttachROI(HWindow windowID, Int64 drawID)
{
var drawObj = RoiList.FirstOrDefault(drawobj => drawobj.DrawID == drawID);
if (drawObj != null)
{
HOperatorSet.AttachDrawingObjectToWindow(windowID, drawObj.DrawObject);
OnDrawObjectChangedEvents(new DrawingObjectModel(RoiOperateModel.重画, drawID));
}
}
///
/// 生成ROI:根据ROI类型和ROI参数,在指定窗体中生成ROI
///
public void GenROI(RoiTypeModel type, HTuple[] param, HWindow windowID)
{
if (RoiList.Count < 10)
{
HDrawingObject hDrawObject;
if (type == RoiTypeModel.XLD_CONTOUR)
{
HOperatorSet.CreateDrawingObjectXld(param[0], param[1], out HTuple drawID);
hDrawObject = new HDrawingObject(drawID.H);
}
else
{
hDrawObject = CreateROIObject(type, param);
}
///绑定事件:ROI:拖拽、调整大小、解除附加、选中
hDrawObject.OnDrag(OnDragDrawObjectCallback);
hDrawObject.OnResize(OnResizeDrawObjectCallback);
hDrawObject.OnDetach(OnDetachDrawObjectCallback);
hDrawObject.OnSelect(OnSelectDrawObjectCallback);
//创建并实例化绘制模型对象
DrawingObjectModel drawModelObject = new DrawingObjectModel(type, hDrawObject, hDrawObject.ID, param);
drawModelObject.Operate = RoiOperateModel.添加;
///添加到集合
RoiList.Add(drawModelObject);
//附加对象到窗体
HOperatorSet.AttachDrawingObjectToWindow(windowID, hDrawObject);
//打印日志
OnMessageUpdateEvents($"添加一个{type}ROI:ID = [{hDrawObject.ID}]");
OnDrawObjectChangedEvents(drawModelObject);
}
else
{
OnMessageUpdateEvents($"添加矩形ROI失败,最多创建10个,当前个数:{RoiList.Count}");
}
}
///
/// 创建ROI对象:根据ROI类型及参数,返回一个ROI(Halcon 绘制)对象。
///
public HDrawingObject CreateROIObject(RoiTypeModel type, HTuple[] param)
{
return HDrawingObject.CreateDrawingObject(EnumConvert(type), param);
}
///
/// 枚举转换:传入自定义ROI类型,转换成Halcon的ROI类型(Halcon的调用太长了...)。
///
private HDrawingObject.HDrawingObjectType EnumConvert(RoiTypeModel type)
{
return (HDrawingObject.HDrawingObjectType)type;
}
#endregion
#region 获取ROI参数
///
/// 获取ROI参数:输入:绘制对象,ROI类型; 输出:ROI参数结果,ROI形状。
///
public void GetROIParams(HDrawingObject drawingObject, RoiTypeModel roiType,
out double[] RoiShapeParamResult, out HObject RoiShapeObj)
{
HTuple paramName = new HTuple();
HTuple paramValue = new HTuple();
switch (roiType)
{
case RoiTypeModel.RECTANGLE1: //矩形1
paramName = DrawObjectParamModel.RECT1;
paramValue = drawingObject.GetDrawingObjectParams(paramName);
HOperatorSet.GenRectangle1(out RoiShapeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2], paramValue.DArr[3]);
RoiShapeParamResult = paramValue.DArr;
break;
case RoiTypeModel.RECTANGLE2: //方向矩形
paramName = DrawObjectParamModel.RECT2;
paramValue = drawingObject.GetDrawingObjectParams(paramName);
HOperatorSet.GenRectangle2(out RoiShapeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2], paramValue.DArr[3], paramValue.DArr[4]);
RoiShapeParamResult = paramValue.DArr;
break;
case RoiTypeModel.CIRCLE: //圆形
paramName = DrawObjectParamModel.CIRCLE;
paramValue = drawingObject.GetDrawingObjectParams(paramName);
HOperatorSet.GenCircle(out RoiShapeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2]);
RoiShapeParamResult = paramValue.DArr;
break;
case RoiTypeModel.ELLIPSE: //圆形
paramName = DrawObjectParamModel.ELLIPSE;
paramValue = drawingObject.GetDrawingObjectParams(paramName);
HOperatorSet.GenEllipse(out RoiShapeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2], paramValue.DArr[3], paramValue.DArr[4]);
RoiShapeParamResult = paramValue.DArr;
break;
case RoiTypeModel.XLD_CONTOUR: //圆形
paramName = DrawObjectParamModel.ELLIPSE;
paramValue = drawingObject.GetDrawingObjectParams(paramName);
HOperatorSet.GenEllipse(out RoiShapeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2], paramValue.DArr[3], paramValue.DArr[4]);
RoiShapeParamResult = paramValue.DArr;
break;
default:
RoiShapeParamResult = new double[] { 0 };
RoiShapeObj = null;
break;
}
}
///
/// 获取ROI参数:输入=>ROI的ID(句柄);输出<=ROI参数数组;返回:ROI形状对象
///
public HObject GetROIParams(Int64 roiID, out double[] RoiShapeParamResul)
{
//遍历获取集合中首个,与输入的ROI的ID相同绘制对象,如果没找到,返回null
var drawObj = RoiList.FirstOrDefault(id => id.DrawObject.ID.Equals(roiID));
if (drawObj != null)
{
GetROIParams(drawObj.DrawObject, drawObj.RoiType, out RoiShapeParamResul, out HObject roiShapeObj);
//消息:参数
string data = "[";
for (int i = 0; i < RoiShapeParamResul.Length; i++)
{
if (i != RoiShapeParamResul.Length - 1) data += RoiShapeParamResul[i].ToString("0.000") + ",";
else data += RoiShapeParamResul[i].ToString("0.000") + "]";
}
OnMessageUpdateEvents($"{drawObj.RoiType} ROI 参数 {data}");
return roiShapeObj;
}
else
{
RoiShapeParamResul = new double[] { 0 };
return null;
}
}
///
/// 获取ROI参数:输入=>ROI的ID(句柄);输出<=ROI参数数组、ROI形状对象;返回:ROI类型
///
public RoiTypeModel GetRoiObjParams(Int64 roiID,out double[] RoiShapeParamResul,out HObject roiShapeObj)
{
//遍历获取集合中首个,与输入的ROI的ID相同绘制对象,如果没找到,返回null
var drawObj = RoiList.FirstOrDefault(id => id.DrawObject.ID.Equals(roiID));
if (drawObj != null)
{
GetROIParams(drawObj.DrawObject, drawObj.RoiType, out RoiShapeParamResul, out roiShapeObj);
string data = "[";
for (int i = 0; i < RoiShapeParamResul.Length; i++)
{
if (i != RoiShapeParamResul.Length - 1) data += RoiShapeParamResul[i].ToString("0.000") + ",";
else data += RoiShapeParamResul[i].ToString("0.000") + "]";
}
OnMessageUpdateEvents($"{drawObj.RoiType} ROI 参数 {data}");
return drawObj.RoiType;
}
else
{
RoiShapeParamResul = new double[] { 0 };
roiShapeObj =null;
return RoiTypeModel.NONE;
}
}
#endregion
#region ROI信息变更回调(拖拽、调整大小、选择...)
///
/// 移除ROI时触发
///
private void OnDetachDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
OnMessageUpdateEvents($"移除矩形ROI:ID = [{drawid.ID}]");
}
///
/// 选中ROI时触发
///
private void OnSelectDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
OnMessageUpdateEvents($"选择矩形ROI:ID = [{drawid.ID}]");
var drawObj = RoiList.FirstOrDefault(drawobj => drawobj.DrawID == drawid.ID);
if (drawObj != null)
{
OnDrawObjectChangedEvents(new DrawingObjectModel(RoiOperateModel.查询, drawObj.DrawID));
}
}
///
/// 拖拽ROI时触发
///
private void OnDragDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
}
///
/// 调整ROI大小时触发
///
private void OnResizeDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
}
#endregion
#region 模板工具
///
/// 创建模板:输入 图像()
///
public int CreateTeamplate(HObject grayImage, TemplateBase templateObj, out HObject contoursAffinTrans,
out HTuple modelID, out HObject reduceImage )
{
modelID = null;
reduceImage =null;
RoiTypeModel roiType = RoiTypeModel.NONE ;
double[] drawObjResultParam=null;
foreach (DrawingObjectModel drawObj in RoiList)
{
roiType = GetRoiObjParams(drawObj.DrawObject.ID, out drawObjResultParam, out HObject region);
HOperatorSet.ReduceDomain(grayImage, region,out reduceImage);
}
CreateScaledShapeModel modelParam = templateObj as CreateScaledShapeModel;
if (templateObj.ModelCreatingMethod==1)
{
HOperatorSet.CreateShapeModel(reduceImage,
modelParam.NumLevels,
modelParam.AngleStart, modelParam.AngleExtent, modelParam.AngleStep,
modelParam.Optimization, modelParam.Metric,
modelParam.Contrast, modelParam.MinContrast,
out modelID);
}
else if(templateObj.ModelCreatingMethod == 0)
{
HOperatorSet.CreateScaledShapeModel(reduceImage, modelParam.NumLevels,
modelParam.AngleStart, modelParam.AngleExtent, modelParam.AngleStep,
modelParam.ScaleMin, modelParam.ScaleMax, modelParam.ScaleStep,
modelParam.Optimization, modelParam.Metric,
modelParam.Contrast, modelParam.MinContrast, out modelID);
}
HTuple row =new HTuple();
HTuple column = new HTuple();
HTuple angle = 0;
switch (roiType)
{
case RoiTypeModel.RECTANGLE1:
row = (drawObjResultParam[0] + drawObjResultParam[2]) / 2.0;
column = (drawObjResultParam[1] + drawObjResultParam[3]) / 2.0;
break;
case RoiTypeModel.RECTANGLE2:
row = drawObjResultParam[0];
column = drawObjResultParam[1];
angle = drawObjResultParam[2];
break;
case RoiTypeModel.CIRCLE:
row = drawObjResultParam[0];
column = drawObjResultParam[1];
break;
case RoiTypeModel.ELLIPSE:
row = drawObjResultParam[0];
column = drawObjResultParam[1];
angle = (drawObjResultParam[2] * 180 / Math.PI);
break;
case RoiTypeModel.XLD_CONTOUR:
break;
default:
break;
}
//创建变换矩阵
HOperatorSet.VectorAngleToRigid(0, 0, 0, row, column, angle, out HTuple hv_HomMat2D);
//获取轮廓
HOperatorSet.GetShapeModelContours(out HObject ho_ModelContours, modelID, 1);
//根据轮廓和变换矩阵获得变换后的轮廓。
HOperatorSet.AffineTransContourXld(ho_ModelContours, out contoursAffinTrans, hv_HomMat2D);
OnMessageUpdateEvents($"模板创建成功:ID = {modelID}");
return 0;
}
///
/// 查找模板
///
public void FindTeamplate(HObject findImage, HTuple modelID,TemplateBase templateObj ,out List<HObject> contoursAffinTrans,
out HTuple row, out HTuple column, out HTuple angle, out HTuple scale, out HTuple score)
{
HOperatorSet.SetShapeModelParam(modelID,"timeout",5000); //设置匹配超时时间5s
FindScaledShapeModel matchModel = templateObj as FindScaledShapeModel;
contoursAffinTrans = new List<HObject>();
if (templateObj.ModelCreatingMethod==1)
{
HOperatorSet.FindShapeModel(findImage, modelID,
matchModel.AngleStart, matchModel.AngleExtent,
matchModel.MinScore, matchModel.NumMatches, matchModel.MaxOverlap,
matchModel.SubPixel, matchModel.NumLevels, matchModel.Greediness,
out row, out column, out angle, out score);
scale = null;
}
else if(templateObj is FindScaledShapeModel)
{
HOperatorSet.FindScaledShapeModel(findImage, modelID,
matchModel.AngleStart,matchModel.AngleExtent,
matchModel.ScaleMin,matchModel.ScaleMax,
matchModel.MinScore,matchModel.NumMatches,matchModel.MaxOverlap,
matchModel.SubPixel,matchModel.NumLevels,matchModel.Greediness,
out row,out column,out angle,out scale,out score);
}
else
{
row = new HTuple();
column = new HTuple();
angle = new HTuple();
score = new HTuple();
scale =new HTuple();
}
if (row.Length > 0)
{
try
{
for (int i = 0; i < row.Length; i++)
{
HOperatorSet.VectorAngleToRigid(0, 0, 0, row[i], column[i], angle[i], out HTuple hv_HomMat2D);
HOperatorSet.GetShapeModelContours(out HObject ho_ModelContours, modelID, 1);
HOperatorSet.AffineTransContourXld(ho_ModelContours, out HObject contours, hv_HomMat2D);
contoursAffinTrans.Add(contours);
}
}
catch (HOperatorException ex)
{
throw new Exception($"仿射变换参数异常: {ex.Message}");
}
}
}
#endregion
///
/// 图像转换类
///
public class ImageFormatTools
{
#region 判断图像的正确格式
///
/// 图像格式工具:获取正确的图像格式,通过图像文件的二进制头部图像格式标识。
///
public static ImageFormat GetImageFormat(string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
using (BinaryReader br = new BinaryReader(fs))
{
// 读取文件的前几个字节
byte[] headerBytes = br.ReadBytes(16);
// 根据文件的前几个字节判断图像的实际格式
if (IsJpeg(headerBytes))
{
return ImageFormat.Jpeg;
}
else if (IsPng(headerBytes))
{
return ImageFormat.Png;
}
else if (IsGif(headerBytes))
{
return ImageFormat.Gif;
}
else if (IsBmp(headerBytes))
{
return ImageFormat.Bmp;
}
else
{
// 默认返回未知格式
return null;
}
}
}
}
///
/// 判断是否为 Jpeg 图像
///
private static bool IsJpeg(byte[] headerBytes)
{
// JPEG 文件的前两个字节是 0xFF, 0xD8
return headerBytes.Length >= 2 && headerBytes[0] == 0xFF && headerBytes[1] == 0xD8;
}
///
/// 判断是否为 Png 图像
///
private static bool IsPng(byte[] headerBytes)
{
// PNG 文件的前八个字节是固定的签名:137 80 78 71 13 10 26 10
return headerBytes.Length >= 8 && headerBytes[0] == 137
&& headerBytes[1] == 80 && headerBytes[2] == 78
&& headerBytes[3] == 71 && headerBytes[4] == 13
&& headerBytes[5] == 10 && headerBytes[6] == 26
&& headerBytes[7] == 10;
}
///
/// 判断是否为 gif 图像
///
private static bool IsGif(byte[] headerBytes)
{
// GIF 文件的前三个字节是 "GIF"
return headerBytes.Length >= 3 && headerBytes[0] == 71
&& headerBytes[1] == 73 && headerBytes[2] == 70;
}
///
/// 判断是否为 bmp 图像
///
private static bool IsBmp(byte[] headerBytes)
{
// BMP 文件的前两个字节是 "BM"
return headerBytes.Length >= 2 && headerBytes[0] == 66
&& headerBytes[1] == 77;
}
#endregion
///
/// 获取图像通道数、图像像素格式。
///
public static int GetImageChannelCount(string imagePath ,out Bitmap bitmap,out PixelFormat format)
{
try
{
Image image = Image.FromFile(imagePath);
bitmap = new Bitmap(image);
format = bitmap.PixelFormat; // 获取图像像素格式
image.Dispose();
switch (format)//据PixelFormat判断通道数
{
case PixelFormat.Format8bppIndexed: // 灰度图
return 1;
case PixelFormat.Format24bppRgb: // RGB图
return 3;
case PixelFormat.Format32bppRgb: //
case PixelFormat.Format32bppArgb: // RGBA图 RGB + Alpha
return 4;
default:
return -1;
}
}
catch (Exception ex)
{
throw new ArgumentException($"获取图像通道数->参数异常:{ex.Message}");
}
}
///
/// 读取图像,并返回读取信息:输入图像路径,返回HImage图像(8,24,32位(1,3,4通道))
///
public static void ReadImage(string path,out HImage hImage,out string info)
{
hImage = new HImage();
info = null;
GetImageChannelCount(path, out Bitmap bitmap, out PixelFormat pixelFormat);
ImageFormat imageFormat = GetImageFormat(path);
switch (pixelFormat)
{
case PixelFormat.Format8bppIndexed: //8位单通道
try
{
hImage.ReadImage(path);
}
catch (Exception ex)
{
BitmapToHImageBpp8(bitmap, out hImage);
info = $"图像原格式:{imageFormat},当前后缀:{Path.GetExtension(path)}: {ex.Message}";
}
break;
case PixelFormat.Format24bppRgb://24位3通道
try
{
hImage.ReadImage(path);
}
catch (Exception ex)
{
BitmapToHImageBpp24(bitmap, out hImage);
info = $"图像原格式:{imageFormat},当前后缀:{Path.GetExtension(path)}: {ex.Message}";
}
break;
case PixelFormat.Format32bppRgb://32位3通道
case PixelFormat.Format32bppArgb://32位4通道
try
{
hImage.ReadImage(path);
}
catch (Exception ex)
{
BitmapToHImageBpp32(bitmap, out hImage, pixelFormat);
info = $"图像原格式:{imageFormat},当前后缀:{Path.GetExtension(path)}: {ex.Message}";
}
break;
default: //8位单通道
try
{
hImage.ReadImage(path);
}
catch (Exception ex)
{
BitmapToHImageBpp8(bitmap, out hImage);
info = $"图像原格式:{imageFormat},当前后缀:{Path.GetExtension(path)}: {ex.Message}";
}
break;
}
}
///
/// Bitmap转HImage(24位3通道)
///
public static void BitmapToHImageBpp24(Bitmap bmp, out HImage image)
{
try
{
image = new HImage();
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData srcBmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
image.GenImageInterleaved(srcBmpData.Scan0, "rgb", bmp.Width, bmp.Height, 0, "byte", bmp.Width, bmp.Height, 0, 0, -1, 0);
bmp.UnlockBits(srcBmpData);
}
catch (Exception ex)
{
image = null;
}
}
///
/// Bitmap转HImage(32位4通道)
///
public static void BitmapToHImageBpp32(Bitmap bmp, out HImage image, PixelFormat pixelFormat)
{
try
{
image = new HImage();
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData srcBmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, pixelFormat);
image.GenImageInterleaved(srcBmpData.Scan0, "bgrx", bmp.Width, bmp.Height, 0, "byte", bmp.Width, bmp.Height, 0, 0, -1, 0);
bmp.UnlockBits(srcBmpData);
}
catch (Exception ex)
{
image = null;
}
}
///
/// Bitmap转HImage(8位单通道)
///
public static void BitmapToHImageBpp8(Bitmap bmp,out HImage image)
{
try
{
image = new HImage();
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData srcBmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
image.GenImage1("byte", bmp.Width, bmp.Height, srcBmpData.Scan0);
bmp.UnlockBits(srcBmpData);
}
catch (Exception ex)
{
image = null;
}
}
}
ALL参数模型类
///
/// 绘制对象(ROI)模型
///
public class DrawingObjectModel
{
///
/// 绘制对象(ROI)参数
///
public HTuple[] RoiParams { get; set; }
///
/// 绘制对象(ROI)类型
///
public RoiTypeModel RoiType { get; set; }
///
/// 绘制对象(ROI)对象
///
public HDrawingObject DrawObject { get; set; }
///
/// 绘制对象(ROI)ID
///
public HTuple DrawID { get; set; }
///
/// 绘制对象(ROI)执行操作
///
public RoiOperateModel Operate { get; set; }
public DrawingObjectModel(RoiTypeModel roiType, HDrawingObject drawObj, HTuple drawID ,HTuple[] roiParams)
{
this.RoiParams = roiParams;
this.RoiType = roiType;
this.DrawObject = drawObj;
this.DrawID = drawID;
this.Operate = RoiOperateModel.None;
}
public DrawingObjectModel(RoiOperateModel Operate, HTuple drawID)
{
this.Operate = Operate;
this.DrawID = drawID;
}
}
///
/// ROI绘制形状参数模型:获取对应形状的参数值时使用
///
public class DrawObjectParamModel
{
///
/// 圆形参数
///
public readonly static string[] CIRCLE = { "row", "column", "radius" };
///
/// 椭圆参数
///
public readonly static string[] ELLIPSE = { "row", "column", "phi", "radius1", "radius2" };
///
/// 矩形1参数
///
public readonly static string[] RECT1 = { "row1", "column1", "row2", "column2" };
///
/// 矩形2参数
///
public readonly static string[] RECT2 = { "row", "column", "phi", "length1", "length2" };
}
public class ImageModel: ModelsBase
{
///
/// 图像ID
///
public Int64 ID { get; set; }
///
/// 图像文件名(路径)
///
public string FileName { get; set; }
///
/// 图像目录
///
public string Directory { get; set; }
///
/// 原图
///
public HImage SourceImage { get; set; }
///
/// 灰度图
///
public HImage GrayImage { get; set; }
}
模型基类(空空如也,后面自己增加功能)
public abstract class ModelsBase{}
枚举
///
/// ROI操作模型
///
public enum RoiOperateModel
{
None = 0,
添加 = 1,
删除 = 2,
修改 = 3,
查询 = 4,
重画 = 5
}
枚举
///
/// ROI类型枚举模型
///
public enum RoiTypeModel
{
///
/// 矩形
///
RECTANGLE1,
///
/// 方向矩形
///
RECTANGLE2,
///
/// 圆
///
CIRCLE,
///
/// 椭圆
///
ELLIPSE,
///
/// 圆扇
///
CIRCLE_SECTOR,
///
/// 椭圆扇
///
ELLIPSE_SECTOR,
///
/// 线
///
LINE,
///
/// 轮廓
///
XLD_CONTOUR,
///
/// 文本
///
TEXT,
///
/// 没有返回,放最后面,否则转换时报错
///
NONE,
}
模板
///
/// 模板基类
///
public abstract class TemplateBase
{
private int modelCreatingMethod =0; //模板创建方式:0默认缩放模板
private int numLevels = 2; //金字塔层数
private double angleStart = -180; //起始角度
private double angleExtent = 360; //角度范围
private double angleStep = 0; //角度步长
private double scaleMin = 0.8; //最小缩放比
private double scaleMax = 1.2; //最大缩放比
private double scaleStep = 0.01; //缩放步长
private string optimization = "auto"; //模板优化
private string metric = "use_polarity"; //匹配方法
private int contrast = 30; //对比度
private int minContrast = 10; //最小对比度
///
/// 金字塔层数
///
public int NumLevels { get => numLevels; set => numLevels = value; }
///
/// 起始角度
///
public double AngleStart { get => angleStart; set => angleStart = value; }
///
/// 角度范围
///
public double AngleExtent { get => angleExtent; set => angleExtent = value; }
///
/// 角度步长
///
public double AngleStep { get => angleStep; set => angleStep = value; }
///
/// 最小缩放比
///
public double ScaleMin { get => scaleMin; set => scaleMin = value; }
///
/// 最大缩放比
///
public double ScaleMax { get => scaleMax; set => scaleMax = value; }
///
/// 缩放步长
///
public double ScaleStep { get => scaleStep; set => scaleStep = value; }
///
/// 模板优化
///
public string Optimization { get => optimization; set => optimization = value; }
///
/// 匹配方法
///
public string Metric { get => metric; set => metric = value; }
///
/// 对比度
///
public int Contrast { get => contrast; set => contrast = value; }
///
/// 最小对比度
///
public int MinContrast { get => minContrast; set => minContrast = value; }
///
/// 模板创建方式
///
public int ModelCreatingMethod { get => modelCreatingMethod; set => modelCreatingMethod = value; }
}
有点参数重复自行删除
public class CreateScaledShapeModel:TemplateBase
{
private int modelCreatingMethod = 0; //模板创建方式:0默认缩放模板
private int numLevels = 2; //金字塔层数
private double angleStart = -180; //起始角度
private double angleExtent = 360; //角度范围
private double angleStep = 0; //角度步长
private double scaleMin = 0.8; //最小缩放比
private double scaleMax = 1.2; //最大缩放比
private double scaleStep = 0.01; //缩放步长
private string optimization = "auto"; //模板优化
private string metric = "use_polarity"; //匹配方法
private int contrast = 30; //对比度
private int minContrast = 10; //最小对比度
///
/// 金字塔层数
///
public int NumLevels { get => numLevels; set => numLevels = value; }
///
/// 起始角度
///
public double AngleStart { get => angleStart; set => angleStart = value; }
///
/// 角度范围
///
public double AngleExtent { get => angleExtent; set => angleExtent = value; }
///
/// 角度步长
///
public double AngleStep { get => angleStep; set => angleStep = value; }
///
/// 最小缩放比
///
public double ScaleMin { get => scaleMin; set => scaleMin = value; }
///
/// 最大缩放比
///
public double ScaleMax { get => scaleMax; set => scaleMax = value; }
///
/// 缩放步长
///
public double ScaleStep { get => scaleStep; set => scaleStep = value; }
///
/// 模板优化
///
public string Optimization { get => optimization; set => optimization = value; }
///
/// 匹配方法
///
public string Metric { get => metric; set => metric = value; }
///
/// 对比度
///
public int Contrast { get => contrast; set => contrast = value; }
///
/// 最小对比度
///
public int MinContrast { get => minContrast; set => minContrast = value; }
///
/// 模板创建方式
///
public int ModelCreatingMethod { get => modelCreatingMethod; set => modelCreatingMethod = value; }
}
有点参数重复自行删除
///
/// 匹配缩放模板参数:建议将所有匹配模板的参数都设置
///
public class FindScaledShapeModel : TemplateBase
{
private double angleStart = -180;
private double angleExtent = 360;
private double scaleMin = 0.8; //最小缩放比
private double scaleMax = 1.2; //最大缩放比
private double minScore = 5;
private double maxOverlap = 0.5;
private double greediness = 0.9;
private int numMatches = 0;
private int numLevels = 2;
private string subPixel = "least_squares";
///
/// 起始角度
///
public double AngleStart { get => angleStart; set => angleStart = value; }
///
/// 角度范围(大于起始角度,大于1)
///
public double AngleExtent { get => angleExtent; set => angleExtent = value; }
///
/// 最小缩放范围
///
public double ScaleMin { get => scaleMin; set => scaleMin = value; }
///
/// 最大缩放范围
///
public double ScaleMax { get => scaleMax; set => scaleMax = value; }
///
/// 最小分数
///
public double MinScore { get => minScore; set => minScore = value; }
///
/// 最大重叠率
///
public double MaxOverlap { get => maxOverlap; set => maxOverlap = value; }
///
/// 贪婪度(速度尺度)
///
public double Greediness { get => greediness; set => greediness = value; }
///
/// 匹配个数
///
public int NumMatches { get => numMatches; set => numMatches = value; }
///
/// 金字塔层数
///
public int NumLevels { get => numLevels; set => numLevels = value; }
///
/// 匹配精度
///
public string SubPixel { get => subPixel; set => subPixel = value; }
}
1、代码本来像添加绘制多边形ROI的,刚开始是判断点击绘制按钮后,点击图像窗口,绘制点,右键结束。然后根据点生成区域,再生成绘制对象。最后跟添加其他绘制对象一样将该对象附加到窗体。实现了绘制,但是生成模板时出现bug,暂时没修补。
2、代码的不足之处是。基本处理绘制ROI功能外,都放置在了程序主界面,后面会分开,模块化。
3、许多功能如创建模板、匹配模板这些方法过于臃肿,后期需要拆解多把一些功能创建成方法。
4、待发现。 …