前言
C#联合halcon实现绘制ROI功能:
C#联合Halcon,使用HDrawingObject、HDrawingObjectXld,绘制矩形、方向矩形、圆形、椭圆、自定义ROI。支持拖动、重设大小、选中。
运行结果
代码
代码结构
MainForm 视图
MainViewModel 视图模型
ROI ROI模型
GenArrow 生成带箭头的点
GrawArrowViewModel 绘制箭头视图模型
MainForm
public partial class MainForm : Form
{
DrawArrowViewModel drawArrowViewModel;
MainViewModel viewModel;
public MainForm()
{
InitializeComponent();
drawArrowViewModel = new DrawArrowViewModel(this);
viewModel = new MainViewModel(this);
}
}
MainForm 设计器
partial class MainForm
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.panel_Top = new System.Windows.Forms.Panel();
this.checkBox_ShowROI = new System.Windows.Forms.CheckBox();
this.Btn_LoadImage = new System.Windows.Forms.Button();
this.panel_Bottom = new System.Windows.Forms.Panel();
this.panel_Left = new System.Windows.Forms.Panel();
this.Lbx_ROIList = new System.Windows.Forms.ListBox();
this.Cbx_ROIType = new System.Windows.Forms.ComboBox();
this.Btn_CreateROI = new System.Windows.Forms.Button();
this.Btn_DrawArrow = new System.Windows.Forms.Button();
this.panel_ImageWin = new System.Windows.Forms.Panel();
this.HSImageWindow = new HalconDotNet.HSmartWindowControl();
this.panel_ImageInfo = new System.Windows.Forms.Panel();
this.Tbx_ImageInfo = new System.Windows.Forms.TextBox();
this.panel_Top.SuspendLayout();
this.panel_Left.SuspendLayout();
this.panel_ImageWin.SuspendLayout();
this.panel_ImageInfo.SuspendLayout();
this.SuspendLayout();
//
// panel_Top
//
this.panel_Top.Controls.Add(this.checkBox_ShowROI);
this.panel_Top.Controls.Add(this.Btn_LoadImage);
this.panel_Top.Dock = System.Windows.Forms.DockStyle.Top;
this.panel_Top.Location = new System.Drawing.Point(0, 0);
this.panel_Top.Name = "panel_Top";
this.panel_Top.Size = new System.Drawing.Size(1449, 100);
this.panel_Top.TabIndex = 0;
//
// checkBox_ShowROI
//
this.checkBox_ShowROI.AutoSize = true;
this.checkBox_ShowROI.Font = new System.Drawing.Font("宋体", 12F);
this.checkBox_ShowROI.Location = new System.Drawing.Point(281, 44);
this.checkBox_ShowROI.Name = "checkBox_ShowROI";
this.checkBox_ShowROI.Size = new System.Drawing.Size(120, 28);
this.checkBox_ShowROI.TabIndex = 2;
this.checkBox_ShowROI.Text = "显示ROI";
this.checkBox_ShowROI.UseVisualStyleBackColor = true;
//
// Btn_LoadImage
//
this.Btn_LoadImage.Location = new System.Drawing.Point(53, 31);
this.Btn_LoadImage.Name = "Btn_LoadImage";
this.Btn_LoadImage.Size = new System.Drawing.Size(136, 46);
this.Btn_LoadImage.TabIndex = 1;
this.Btn_LoadImage.Text = "加载图像";
this.Btn_LoadImage.UseVisualStyleBackColor = true;
//
// panel_Bottom
//
this.panel_Bottom.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel_Bottom.Location = new System.Drawing.Point(0, 804);
this.panel_Bottom.Name = "panel_Bottom";
this.panel_Bottom.Size = new System.Drawing.Size(1449, 113);
this.panel_Bottom.TabIndex = 1;
//
// panel_Left
//
this.panel_Left.Controls.Add(this.Lbx_ROIList);
this.panel_Left.Controls.Add(this.Cbx_ROIType);
this.panel_Left.Controls.Add(this.Btn_CreateROI);
this.panel_Left.Controls.Add(this.Btn_DrawArrow);
this.panel_Left.Dock = System.Windows.Forms.DockStyle.Left;
this.panel_Left.Location = new System.Drawing.Point(0, 100);
this.panel_Left.Name = "panel_Left";
this.panel_Left.Size = new System.Drawing.Size(411, 704);
this.panel_Left.TabIndex = 1;
//
// Lbx_ROIList
//
this.Lbx_ROIList.Font = new System.Drawing.Font("宋体", 14F);
this.Lbx_ROIList.FormattingEnabled = true;
this.Lbx_ROIList.ItemHeight = 28;
this.Lbx_ROIList.Location = new System.Drawing.Point(13, 221);
this.Lbx_ROIList.Name = "Lbx_ROIList";
this.Lbx_ROIList.ScrollAlwaysVisible = true;
this.Lbx_ROIList.Size = new System.Drawing.Size(358, 340);
this.Lbx_ROIList.TabIndex = 3;
//
// Cbx_ROIType
//
this.Cbx_ROIType.Font = new System.Drawing.Font("宋体", 16F);
this.Cbx_ROIType.FormattingEnabled = true;
this.Cbx_ROIType.Location = new System.Drawing.Point(13, 157);
this.Cbx_ROIType.Name = "Cbx_ROIType";
this.Cbx_ROIType.Size = new System.Drawing.Size(358, 41);
this.Cbx_ROIType.TabIndex = 2;
//
// Btn_CreateROI
//
this.Btn_CreateROI.Location = new System.Drawing.Point(12, 78);
this.Btn_CreateROI.Name = "Btn_CreateROI";
this.Btn_CreateROI.Size = new System.Drawing.Size(359, 57);
this.Btn_CreateROI.TabIndex = 1;
this.Btn_CreateROI.Text = "创建ROI";
this.Btn_CreateROI.UseVisualStyleBackColor = true;
//
// Btn_DrawArrow
//
this.Btn_DrawArrow.Location = new System.Drawing.Point(12, 6);
this.Btn_DrawArrow.Name = "Btn_DrawArrow";
this.Btn_DrawArrow.Size = new System.Drawing.Size(359, 57);
this.Btn_DrawArrow.TabIndex = 0;
this.Btn_DrawArrow.Text = "绘制箭头";
this.Btn_DrawArrow.UseVisualStyleBackColor = true;
//
// panel_ImageWin
//
this.panel_ImageWin.Controls.Add(this.HSImageWindow);
this.panel_ImageWin.Controls.Add(this.panel_ImageInfo);
this.panel_ImageWin.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel_ImageWin.Location = new System.Drawing.Point(411, 100);
this.panel_ImageWin.Name = "panel_ImageWin";
this.panel_ImageWin.Size = new System.Drawing.Size(1038, 704);
this.panel_ImageWin.TabIndex = 2;
//
// HSImageWindow
//
this.HSImageWindow.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.HSImageWindow.AutoValidate = System.Windows.Forms.AutoValidate.EnableAllowFocusChange;
this.HSImageWindow.Dock = System.Windows.Forms.DockStyle.Fill;
this.HSImageWindow.HDoubleClickToFitContent = true;
this.HSImageWindow.HDrawingObjectsModifier = HalconDotNet.HSmartWindowControl.DrawingObjectsModifier.None;
this.HSImageWindow.HImagePart = new System.Drawing.Rectangle(0, 0, 640, 480);
this.HSImageWindow.HKeepAspectRatio = true;
this.HSImageWindow.HMoveContent = true;
this.HSImageWindow.HZoomContent = HalconDotNet.HSmartWindowControl.ZoomContent.WheelForwardZoomsIn;
this.HSImageWindow.Location = new System.Drawing.Point(0, 0);
this.HSImageWindow.Margin = new System.Windows.Forms.Padding(0);
this.HSImageWindow.Name = "HSImageWindow";
this.HSImageWindow.Size = new System.Drawing.Size(1038, 666);
this.HSImageWindow.TabIndex = 1;
this.HSImageWindow.WindowSize = new System.Drawing.Size(1038, 666);
//
// panel_ImageInfo
//
this.panel_ImageInfo.BackColor = System.Drawing.SystemColors.ControlDarkDark;
this.panel_ImageInfo.Controls.Add(this.Tbx_ImageInfo);
this.panel_ImageInfo.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel_ImageInfo.Location = new System.Drawing.Point(0, 666);
this.panel_ImageInfo.Name = "panel_ImageInfo";
this.panel_ImageInfo.Size = new System.Drawing.Size(1038, 38);
this.panel_ImageInfo.TabIndex = 0;
//
// Tbx_ImageInfo
//
this.Tbx_ImageInfo.BackColor = System.Drawing.SystemColors.ButtonShadow;
this.Tbx_ImageInfo.Dock = System.Windows.Forms.DockStyle.Fill;
this.Tbx_ImageInfo.Font = new System.Drawing.Font("宋体", 14F);
this.Tbx_ImageInfo.Location = new System.Drawing.Point(0, 0);
this.Tbx_ImageInfo.Name = "Tbx_ImageInfo";
this.Tbx_ImageInfo.Size = new System.Drawing.Size(1038, 39);
this.Tbx_ImageInfo.TabIndex = 0;
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1449, 917);
this.Controls.Add(this.panel_ImageWin);
this.Controls.Add(this.panel_Left);
this.Controls.Add(this.panel_Bottom);
this.Controls.Add(this.panel_Top);
this.DoubleBuffered = true;
this.Name = "MainForm";
this.Text = "MainForm";
this.panel_Top.ResumeLayout(false);
this.panel_Top.PerformLayout();
this.panel_Left.ResumeLayout(false);
this.panel_ImageWin.ResumeLayout(false);
this.panel_ImageInfo.ResumeLayout(false);
this.panel_ImageInfo.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panel_Top;
private System.Windows.Forms.Panel panel_Bottom;
private System.Windows.Forms.Panel panel_Left;
private System.Windows.Forms.Panel panel_ImageWin;
private System.Windows.Forms.Panel panel_ImageInfo;
public HalconDotNet.HSmartWindowControl HSImageWindow;
public System.Windows.Forms.Button Btn_DrawArrow;
public System.Windows.Forms.Button Btn_LoadImage;
public System.Windows.Forms.TextBox Tbx_ImageInfo;
public System.Windows.Forms.Button Btn_CreateROI;
public System.Windows.Forms.ComboBox Cbx_ROIType;
public System.Windows.Forms.ListBox Lbx_ROIList;
public System.Windows.Forms.CheckBox checkBox_ShowROI;
}
MainViewModel
public class MainViewModel
{
#region 集合
//ROI类型字典
private Dictionary<string, ROIType> _roiTypeDictionary = new Dictionary<string, ROIType>();
//ROI类型字典
public Dictionary<string, ROIType> RoiTypeDictionary { get => _roiTypeDictionary; }
//ROI类型字典键
public List<string> ROITypeNameList { get => RoiTypeDictionary.Keys.ToList(); }
//ROI字典
private Dictionary<ROI, DrawingObject> _roiDictionary = new Dictionary<ROI, DrawingObject>();
//ROI字典
public Dictionary<ROI, DrawingObject> ROIDictionary { get => _roiDictionary; }
//ROI字典 - 值
public List<DrawingObject> ROIValues { get => ROIDictionary.Values.ToList(); }
#endregion
#region 字段
private MainForm mainForm; //操作窗体
private string currentRoiType = string.Empty; //当前ROI类型
private int ImageChannel = 0; //图像通道
private ContextMenuStrip RoiRightMenu; //右键菜单
private HWindow windowID = null; //窗口ID
private HTuple CurrentROIHandle = null; //当前选中的ROI
private ROI copyROI = null; //当前ROI对象
public event EventHandler<ROIOperator> NotifyROIOperation; //ROI操作事件
#endregion
//构造函数
public MainViewModel(MainForm mainForm)
{
this.mainForm = mainForm;
this.windowID = this.mainForm.HSImageWindow.HalconWindow;
//事件绑定
this.mainForm.HSImageWindow.HMouseDown += HSImageWindow_HMouseDown;
this.mainForm.Btn_CreateROI.Click += Btn_CreateROI_Click;
this.mainForm.Cbx_ROIType.SelectedIndexChanged += Cbx_ROIType_SelectedIndexChanged;
this.mainForm.Lbx_ROIList.SelectedIndexChanged += Lbx_ROIList_SelectedIndexChanged;
this.mainForm.checkBox_ShowROI.CheckedChanged += CheckBox_ShowROI_CheckedChanged;
Initialize();
//数据绑定,ROI类型集合
this.mainForm.Cbx_ROIType.DataSource = ROITypeNameList;
this.mainForm.Cbx_ROIType.SelectedIndex = 0;
}
//ROI-列表索引变更
private void Lbx_ROIList_SelectedIndexChanged(object sender, EventArgs e)
{
if (sender is ListBox item)
{
string handler = item.SelectedItem?.ToString();
var tempDrawObj = ROIDictionary.FirstOrDefault(roi =>roi.Key.Name.ToString().Equals(handler)).Value;
if (tempDrawObj != null)
{
Debug.WriteLine($"{CurrentROIHandle} => {tempDrawObj.Obj.ID}");
HOperatorSet.DetachDrawingObjectFromWindow(windowID, tempDrawObj.Obj);
foreach ( var draw in ROIValues)
{
if (draw.Obj.ID != tempDrawObj.Obj.ID)
HOperatorSet.AttachDrawingObjectToWindow(windowID, draw.Obj);
}
foreach (var draw in ROIValues)
{
if (draw.Obj.ID != tempDrawObj.Obj.ID)
HOperatorSet.DetachDrawingObjectFromWindow(windowID, draw.Obj);
}
HOperatorSet.AttachDrawingObjectToWindow(windowID, tempDrawObj.Obj);
CurrentROIHandle = tempDrawObj.Obj.ID;
}
}
}
//显示ROI
private void CheckBox_ShowROI_CheckedChanged(object sender, EventArgs e)
{
if (this.mainForm.checkBox_ShowROI.Checked)
{
foreach (var item in ROIDictionary.Values)
{
HOperatorSet.AttachDrawingObjectToWindow(windowID, item.Obj);
}
}
else
{
foreach (var item in ROIDictionary.Values)
{
HOperatorSet.DetachDrawingObjectFromWindow(windowID, item.Obj);
}
}
}
//初始化方法:初始化ROI类型
public void Initialize()
{
//初始化ROI列表
RoiTypeDictionary.Add("矩形1", ROIType.RECTANGLE1);
RoiTypeDictionary.Add("矩形2", ROIType.RECTANGLE2);
RoiTypeDictionary.Add("圆形", ROIType.CIRCLE);
RoiTypeDictionary.Add("椭圆", ROIType.ELLIPSE);
RoiTypeDictionary.Add("半圆", ROIType.CIRCLE_SECTOR);
RoiTypeDictionary.Add("半椭圆", ROIType.ELLIPSE_SECTOR);
RoiTypeDictionary.Add("线条", ROIType.LINE);
RoiTypeDictionary.Add("轮廓", ROIType.XLD_CONTOUR);
RoiTypeDictionary.Add("文本", ROIType.TEXT);
//初始化ROI右键菜单
RoiRightMenu = new ContextMenuStrip();
ToolStripMenuItem menuItem1 = new ToolStripMenuItem("复制");
ToolStripMenuItem menuItem2 = new ToolStripMenuItem("粘贴");
ToolStripMenuItem menuItem3 = new ToolStripMenuItem("删除");
menuItem1.Click += ROIOperation_CopyEvent;
menuItem2.Click += ROIOperation_PasterEvent;
menuItem3.Click += ROIOperation_RemoveEvent;
RoiRightMenu.Items.Add(menuItem1);
RoiRightMenu.Items.Add(menuItem2);
RoiRightMenu.Items.Add(menuItem3);
}
//析构函数
~MainViewModel()
{
CurrentROIHandle?.Dispose();
copyROI = null;
}
//ROI右键操作-复制
private void ROIOperation_CopyEvent(object sender, EventArgs e)
{
ROI roiObj = ROIDictionary.FirstOrDefault(roi => roi.Value.Obj.ID.Equals(CurrentROIHandle)).Key;
if (roiObj!=null)
{
copyROI = roiObj.DeepCopy<ROI>(roiObj); ;
Debug.WriteLine($"复制ROI{roiObj.Handle},新ROI{copyROI.Handle}");
}
}
//ROI右键操作-删除
private void ROIOperation_RemoveEvent(object sender, EventArgs e)
{
Debug.WriteLine("移除ROI");
ROI roi = ROIDictionary.FirstOrDefault(roiObj => roiObj.Value.Obj.ID.Equals(CurrentROIHandle)).Key;
if (roi != null)
{
OnNotifyROIOperation(new ROIOperator(roi.Name, ROIOperation.Remove), null);
HDrawingObject drawObj = ROIDictionary[roi].Obj;
ROIDictionary.Remove(roi);
HOperatorSet.DetachDrawingObjectFromWindow(windowID, drawObj);
Debug.WriteLine($"移除ROI{roi.Handle}");
}
}
//ROI右键操作-粘贴
private void ROIOperation_PasterEvent(object sender, EventArgs e)
{
Debug.WriteLine("粘贴ROI");
if (copyROI != null)
{
ROI roi = new ROI();
roi.ID = ROIDictionary.Count;
roi.Name = $"{currentRoiType}{ROIDictionary.Count}";
roi.Type = copyROI.Type;
roi.ShapeParamNames = copyROI.ShapeParamNames;
roi.ShapeParamValues = copyROI.ShapeParamValues;
for (int i = 0; i < copyROI.ShapeParamValues.Length; i++)
roi.ShapeParamValues[i] = roi.ShapeParamValues[i] + 20;
DrawingObject drawObj = null;
CreateROIDrawingObject(roi, out drawObj );
drawObj.Obj.SetDrawingObjectParams("color", "blue");
if (drawObj!=null)
{
ROIDictionary.Add(roi, drawObj);
roi.Handle = drawObj.Obj.ID;
copyROI = roi;
OnNotifyROIOperation(new ROIOperator(roi.Name, ROIOperation.Add), null);
HOperatorSet.AttachDrawingObjectToWindow(windowID, drawObj.Obj);
Debug.WriteLine($"粘贴ROI{copyROI.Handle}");
}
}
}
//鼠标按下弹出右键菜单
private void HSImageWindow_HMouseDown(object sender, HMouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// 检查鼠标点击位置是否在绘图对象内
foreach(DrawingObject drawingObject in ROIValues)
{
GetDrawingObjectShapeParameter(drawingObject,out HObject shape ,out HTuple results);
}
}
if (e.Button == MouseButtons.Right)
if (CurrentROIHandle != null)
if (sender is HSmartWindowControl control)
RoiRightMenu.Show(Control.MousePosition.X, Control.MousePosition.Y);
}
//ROI类型列表索引变更
private void Cbx_ROIType_SelectedIndexChanged(object sender, EventArgs e)
{
currentRoiType = this.mainForm.Cbx_ROIType.SelectedItem.ToString();
}
//创建ROI按钮按下
private void Btn_CreateROI_Click(object sender, EventArgs e)
{
DrawROI(currentRoiType);
}
//通知ROI变更
private void OnNotifyROIOperation(object sender, EventArgs e)
{
if (sender is ROIOperator roiOperator)
{
switch(roiOperator.Operation)
{
//this.mainForm.Lbx_ROIList.Items.Add(sender);
case ROIOperation.Add:
this.mainForm.Lbx_ROIList.Items.Add(roiOperator.Name);
break;
case ROIOperation.Remove:
this.mainForm.Lbx_ROIList.Items.Remove(roiOperator.Name);
break;
case ROIOperation.Update:
break;
case ROIOperation.Search:
break;
}
}
}
//绘制ROI
public void DrawROI(string typeName)
{
ROI roi = new ROI();
roi.ID = ROIDictionary.Count;
roi.Name = $"{typeName}_{roi.ID}";
roi.Type = RoiTypeDictionary[typeName];//获取ROI类型
DrawingObject drawObj = null;
SetROIParam(roi, roi.Type);
CreateROIDrawingObject(roi, out drawObj);
if (drawObj != null)
{
HOperatorSet.AttachDrawingObjectToWindow(windowID, drawObj.Obj);
roi.Handle = drawObj.Obj.ID;
ROIDictionary.Add(roi, drawObj);
CurrentROIHandle = drawObj.Obj.ID;
OnNotifyROIOperation(new ROIOperator(roi.Name,ROIOperation.Add),null);
}
}
/// <summary>
/// 获取绘制对象参数: input: 自定义DrawingObject对象,
/// output: 形状对象,
/// output: 输出结果参数
/// </summary>
public void GetDrawingObjectShapeParameter(DrawingObject drawObj,out HObject shape ,out HTuple resultParam)
{
HTuple paramValue = new HTuple();
HObject roiTypeObj = new HObject();
shape = roiTypeObj;
resultParam = paramValue;
switch (drawObj.Type)
{
case ROIType.RECTANGLE1: //矩形1
paramValue = drawObj.Obj.GetDrawingObjectParams(drawObj.ShapeParamNames);
HOperatorSet.GenRectangle1(out roiTypeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2], paramValue.DArr[3]);
shape = roiTypeObj;
resultParam = paramValue;
break;
case ROIType.RECTANGLE2: //方向矩形
paramValue = drawObj.Obj.GetDrawingObjectParams(drawObj.ShapeParamNames);
HOperatorSet.GenRectangle2(out roiTypeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2], paramValue.DArr[3], paramValue.DArr[4]);
shape = roiTypeObj;
resultParam = paramValue;
break;
case ROIType.CIRCLE: //圆形
paramValue = drawObj.Obj.GetDrawingObjectParams(drawObj.ShapeParamNames);
HOperatorSet.GenCircle(out roiTypeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2]);
shape = roiTypeObj;
resultParam = paramValue;
break;
case ROIType.ELLIPSE: //椭圆
paramValue = drawObj.Obj.GetDrawingObjectParams(drawObj.ShapeParamNames);
HOperatorSet.GenEllipse(out roiTypeObj, paramValue.DArr[0], paramValue.DArr[1], paramValue.DArr[2], paramValue.DArr[3], paramValue.DArr[4]);
shape = roiTypeObj;
resultParam = paramValue;
break;
case ROIType.XLD_CONTOUR: //xld轮廓
paramValue = drawObj.Obj.GetDrawingObjectParams(drawObj.ShapeParamNames);
shape = roiTypeObj;
resultParam = paramValue;
break;
default: //其他
shape = null;
resultParam = null;
break;
}
}
/// <summary>
/// 设置ROI参数
/// </summary>
public void SetROIParam(ROI roi, ROIType type)
{
switch (type)
{
case ROIType.RECTANGLE1: //矩形1
roi.ShapeParamNames = ShapeParam.RECT1;
roi.ShapeParamValues = ShapeParamValue.RECT1;
break;
case ROIType.RECTANGLE2: //矩形2
roi.ShapeParamNames = ShapeParam.RECT2;
roi.ShapeParamValues = ShapeParamValue.RECT2;
break;
case ROIType.CIRCLE: //圆形
roi.ShapeParamNames = ShapeParam.CIRCLE;
roi.ShapeParamValues = ShapeParamValue.CIRCLE;
break;
case ROIType.ELLIPSE: //椭圆
roi.ShapeParamNames = ShapeParam.ELLIPSE;
roi.ShapeParamValues = ShapeParamValue.ELLIPSE;
break;
case ROIType.LINE: //线型
break;
case ROIType.XLD_CONTOUR: //轮廓
roi.ShapeParamNames = ShapeParam.XLD_COUTOUR;
roi.ShapeParamValues = ShapeParamValue.XLD_COUTOUR;
break;
case ROIType.TEXT:
break;
}
}
/// <summary>
/// 创建ROI对象:根据ROI类型和ROI参数,在指定窗体中生成ROI
/// </summary>
public void CreateROIDrawingObject(ROI roi,out DrawingObject drawObj)
{
drawObj = new DrawingObject();
if (ROIDictionary.Count < 10)
{
drawObj.Type = roi.Type;
drawObj.ShapeParamNames = roi.ShapeParamNames;
drawObj.ShapeParamValues = roi.ShapeParamValues;
if (roi.Type == ROIType.XLD_CONTOUR)
{
HOperatorSet.CreateDrawingObjectXld(drawObj.ShapeParamValues[0], drawObj.ShapeParamValues[1], out HTuple drawID);
drawObj.Obj = new HDrawingObject(drawID.H);
}
else
{
drawObj.Obj = CreateROIObject(drawObj.Type, drawObj.ShapeParamValues);
}
//line_width
drawObj.Obj.SetDrawingObjectParams("color", "blue");
///绑定事件:ROI:拖拽、调整大小、解除附加、选中
drawObj.Obj.OnDrag(OnDragDrawObjectCallback);
drawObj.Obj.OnResize(OnResizeDrawObjectCallback);
drawObj.Obj.OnDetach(OnDetachDrawObjectCallback);
drawObj.Obj.OnSelect(OnSelectDrawObjectCallback);
drawObj.Obj.OnAttach(OnAttachDrawObjectCallback);
}
else
{
drawObj = null;
}
}
#region 绘制对象的事件绑定 :ROI信息变更回调(拖拽、调整大小、选择...)
/// <summary>
/// 附加ROI时触发
/// </summary>
private void OnAttachDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
Debug.WriteLine($"附加ROI:ID = [{drawid}]");
CurrentROIHandle = drawid.ID;
}
/// <summary>
/// 移除ROI时触发
/// </summary>
private void OnDetachDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
CurrentROIHandle = null ;
Debug.WriteLine($"移除ROI:ID = [{drawid.ID}]");
}
/// <summary>
/// 选中ROI时触发
/// </summary>
private void OnSelectDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
var drawObj = ROIDictionary.FirstOrDefault(obj => obj.Value.Obj.ID == drawid.ID).Value;
if (drawObj != null)
{
Debug.WriteLine($"选中ROI:ID = [{drawid.ID}]");
}
CurrentROIHandle = drawid.ID;
}
/// <summary>
/// 拖拽ROI时触发
/// </summary>
private void OnDragDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
Debug.WriteLine($"拖拽ROI:ID = [{drawid.ID}]");
CurrentROIHandle = drawid.ID;
}
/// <summary>
/// 调整ROI大小时触发
/// </summary>
private void OnResizeDrawObjectCallback(HDrawingObject drawid, HWindow window, string type)
{
Debug.WriteLine($"调整ROI大小:ID = [{drawid.ID}]");
CurrentROIHandle = drawid.ID;
}
#endregion
#region 类型转换、对象创建
//创建ROI对象
public HDrawingObject CreateROIObject(ROIType type, HTuple[] param)
{
return HDrawingObject.CreateDrawingObject(ROITypeConvert(type), param);
}
//ROI类型转换
private HDrawingObject.HDrawingObjectType ROITypeConvert(ROIType type)
{
return (HDrawingObject.HDrawingObjectType)type;
}
#endregion
}
ROI
[Serializable]
public class ROI
{
public int ID { get; set; } //ID
public string Name { get; set; } //名称
public ROIType Type { get; set; } //形状类型
public HTuple Handle { get; set; } //绘制对象
public HTuple ShapeParamNames { get; set; } //创建参数名:row1 ,column1,row2 ,column2...
public HTuple[] ShapeParamValues { get; set; } //创建参数值:100,100,200,200...
public HObject Contour { get; set; } //结果轮廓
public T DeepCopy<T>(T obj)
{
object retval = null;
try
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
//序列化成流
bf.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
//反序列化成对象
retval = bf.Deserialize(ms);
}
}
catch (Exception ex) { MessageBox.Show(new Form { TopMost = true }, ex.ToString()); }
return (T)retval;
}
}
public enum ROIType
{
RECTANGLE1,
RECTANGLE2,
CIRCLE,
ELLIPSE,
CIRCLE_SECTOR,
ELLIPSE_SECTOR,
LINE,
XLD_CONTOUR,
TEXT
}
/// <summary>
/// ROI绘制形状参数模型:获取对应形状的参数值时使用
/// </summary>
public class ShapeParam
{
/// <summary>
/// 圆形参数
/// </summary>
public readonly static HTuple CIRCLE = new string[] { "row", "column", "radius" };
/// <summary>
/// 椭圆参数
/// </summary>
public readonly static HTuple ELLIPSE = new string[] { "row", "column", "phi", "radius1", "radius2" };
/// <summary>
/// 矩形1参数
/// </summary>
public readonly static HTuple RECT1 = new string[] { "row1", "column1", "row2", "column2" };
/// <summary>
/// 矩形2参数
/// </summary>
public readonly static HTuple RECT2 = new string[] { "row", "column", "phi", "length1", "length2" };
/// <summary>
///
/// </summary>
public readonly static HTuple XLD_COUTOUR = new string[] { "row", "column" };
}
public class ShapeParamValue
{
public readonly static HTuple[] CIRCLE = new HTuple[] { 100, 100, 100 };
public readonly static HTuple[] ELLIPSE = new HTuple[] { 100, 100, 50, 50, 50 };
public readonly static HTuple[] RECT1 = new HTuple[] { 100, 100, 200, 200 };
public readonly static HTuple[] RECT2 = new HTuple[] { 200, 200, 0, 100, 100 };
public readonly static HTuple[] XLD_COUTOUR = new HTuple[] { (new HTuple(200, 250)), (new HTuple(300, 350)) };
}
/// <summary>
/// 绘制对象
/// </summary>
public class DrawingObject
{
public HDrawingObject Obj { get; set; }
public HTuple ShapeParamNames { get; set; }
public HTuple[] ShapeParamValues { get; set; }
public ROIType Type { get; set; }
}
public class ROIOperator
{
public ROIOperator(string name, ROIOperation operation)
{
this.Name = name;
this.Operation = operation;
}
public HTuple ID { get; set; }
public string Name { get; set; }
public ROIOperation Operation { get; set; }
}
public enum ROIOperation
{
Add,
Remove,
Update,
Search
}
GenArrow
public class GenArrow
{
/// <summary>
/// 生成箭头轮廓
/// </summary>
public static void Gen_arrow_contour_xld(out HObject ho_Arrow,
HTuple hv_Row1, HTuple hv_Column1,
HTuple hv_Row2, HTuple hv_Column2,
HTuple hv_HeadLength, HTuple hv_HeadWidth)
{
HObject ho_TempArrow = null;
HTuple hv_Length = new HTuple();
HTuple hv_ZeroLengthIndices = new HTuple();
HTuple hv_DR = new HTuple();
HTuple hv_DC = new HTuple();
HTuple hv_HalfHeadWidth = new HTuple();
HTuple hv_RowP1 = new HTuple();
HTuple hv_ColP1 = new HTuple();
HTuple hv_RowP2 = new HTuple();
HTuple hv_ColP2 = new HTuple();
HTuple hv_Index = new HTuple();
HOperatorSet.GenEmptyObj(out ho_Arrow);
HOperatorSet.GenEmptyObj(out ho_TempArrow);
ho_Arrow.Dispose();
HOperatorSet.GenEmptyObj(out ho_Arrow);
//计算箭头长度
hv_Length.Dispose();
HOperatorSet.DistancePp(hv_Row1, hv_Column1, hv_Row2, hv_Column2, out hv_Length);
//
//Mark arrows with identical start and end point
//(set Length to -1 to avoid division-by-zero exception)
hv_ZeroLengthIndices.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_ZeroLengthIndices = hv_Length.TupleFind(
0);
}
if ((int)(new HTuple(hv_ZeroLengthIndices.TupleNotEqual(-1))) != 0)
{
if (hv_Length == null)
hv_Length = new HTuple();
hv_Length[hv_ZeroLengthIndices] = -1;
}
//
//Calculate auxiliary variables.
hv_DR.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_DR = (1.0 * (hv_Row2 - hv_Row1)) / hv_Length;
}
hv_DC.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_DC = (1.0 * (hv_Column2 - hv_Column1)) / hv_Length;
}
hv_HalfHeadWidth.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_HalfHeadWidth = hv_HeadWidth / 2.0;
}
//
//Calculate end points of the arrow head.
hv_RowP1.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_RowP1 = (hv_Row1 + ((hv_Length - hv_HeadLength) * hv_DR)) + (hv_HalfHeadWidth * hv_DC);
}
hv_ColP1.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_ColP1 = (hv_Column1 + ((hv_Length - hv_HeadLength) * hv_DC)) - (hv_HalfHeadWidth * hv_DR);
}
hv_RowP2.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_RowP2 = (hv_Row1 + ((hv_Length - hv_HeadLength) * hv_DR)) - (hv_HalfHeadWidth * hv_DC);
}
hv_ColP2.Dispose();
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
hv_ColP2 = (hv_Column1 + ((hv_Length - hv_HeadLength) * hv_DC)) + (hv_HalfHeadWidth * hv_DR);
}
//Finally create output XLD contour for each input point pair
for (hv_Index = 0; (int)hv_Index <= (int)((new HTuple(hv_Length.TupleLength())) - 1); hv_Index = (int)hv_Index + 1)
{
if ((int)(new HTuple(((hv_Length.TupleSelect(hv_Index))).TupleEqual(-1))) != 0)
{
//Create_ single points for arrows with identical start and end point
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
ho_TempArrow.Dispose();
HOperatorSet.GenContourPolygonXld(out ho_TempArrow,
hv_Row1.TupleSelect(hv_Index),
hv_Column1.TupleSelect(hv_Index));
}
}
//Create arrow contour
else
{
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
ho_TempArrow.Dispose();
HOperatorSet.GenContourPolygonXld(out ho_TempArrow, ((((((((((hv_Row1.TupleSelect(
hv_Index))).TupleConcat(hv_Row2.TupleSelect(hv_Index)))).TupleConcat(
hv_RowP1.TupleSelect(hv_Index)))).TupleConcat(hv_Row2.TupleSelect(hv_Index)))).TupleConcat(
hv_RowP2.TupleSelect(hv_Index)))).TupleConcat(hv_Row2.TupleSelect(hv_Index)),
((((((((((hv_Column1.TupleSelect(hv_Index))).TupleConcat(hv_Column2.TupleSelect(
hv_Index)))).TupleConcat(hv_ColP1.TupleSelect(hv_Index)))).TupleConcat(
hv_Column2.TupleSelect(hv_Index)))).TupleConcat(hv_ColP2.TupleSelect(
hv_Index)))).TupleConcat(hv_Column2.TupleSelect(hv_Index)));
}
}
{
HObject ExpTmpOutVar_0;
HOperatorSet.ConcatObj(ho_Arrow, ho_TempArrow, out ExpTmpOutVar_0);
ho_Arrow.Dispose();
ho_Arrow = ExpTmpOutVar_0;
}
}
ho_TempArrow.Dispose();
hv_Length.Dispose();
hv_ZeroLengthIndices.Dispose();
hv_DR.Dispose();
hv_DC.Dispose();
hv_HalfHeadWidth.Dispose();
hv_RowP1.Dispose();
hv_ColP1.Dispose();
hv_RowP2.Dispose();
hv_ColP2.Dispose();
hv_Index.Dispose();
}
}
DrawArrowViewModel
public class DrawArrowViewModel
{
#region 字段
private MainForm mainForm; //操作的窗体
private HSmartWindowControl HSImageWindow; //操作的图像窗口
private HWindow window; //图像窗口ID
private HObject hImage = null; //图像
private HTuple width = new HTuple(); //宽度
private HTuple height = new HTuple(); //高度
private Thread drawArrowThread = null; //绘制线程
private int pointX = -1; //点X
private int pointY = -1; //点Y
private string context = string.Empty; //内容
public event EventHandler<string> ContextChanged; //内容变更事件
#endregion
/// <summary>
/// 构造函数
/// </summary>
public DrawArrowViewModel(MainForm form)
{
this.mainForm = form;
HSImageWindow = form.HSImageWindow;
window = HSImageWindow.HalconWindow;
mainForm.Btn_DrawArrow.Click += Btn_DrawArrow_Click;
mainForm.Btn_LoadImage.Click += Btn_LoadImage_Click;
mainForm.MouseWheel += CustomMouseWheel;
mainForm.HSImageWindow.HMouseMove += HSImageWindow_HMouseMove;
drawArrowThread = new Thread(DrawArrowThreadMethod);
}
//析构函数、释放资源
~ DrawArrowViewModel()
{
drawArrowThread = null ;
window?.Dispose();
hImage?.Dispose();
width?.Dispose();
height?.Dispose();
HSImageWindow?.Dispose();
}
/// <summary>
/// 鼠标滚轮缩放图像
/// </summary>
private void CustomMouseWheel(object sender, MouseEventArgs e)
{
System.Drawing.Point pt = mainForm.Location;
int leftBorder = HSImageWindow.Location.X;
int rightBorder = HSImageWindow.Location.X + HSImageWindow.Size.Width;
int topBorder = HSImageWindow.Location.Y;
int bottomBorder = HSImageWindow.Location.Y + HSImageWindow.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);
HSImageWindow.HSmartWindowControl_MouseWheel(sender, newe);
}
}
/// <summary>
/// 绘制箭头线程方法
/// </summary>
public void DrawArrowThreadMethod()
{
HObject ho_Arrow = new HObject();
HTuple hv_Row = new HTuple();
HTuple hv_Column = new HTuple();
try
{
HOperatorSet.SetColor(window, "blue");
HOperatorSet.DrawPoint(window, out hv_Row, out hv_Column);
HOperatorSet.SetColor(window, (new HTuple("red")).TupleConcat("green"));
GenArrow.Gen_arrow_contour_xld(out ho_Arrow,
hv_Row.TupleConcat(hv_Row), hv_Column.TupleConcat(hv_Column),
((hv_Row + 200)).TupleConcat(hv_Row),
hv_Column.TupleConcat(hv_Column + 200), 5, 5);
HOperatorSet.DispObj(ho_Arrow, window);
}
catch (Exception ex)
{
Debug.Write(ex.ToString());
}
finally
{
ho_Arrow.Dispose();
hv_Row.Dispose();
hv_Column.Dispose();
}
}
/// <summary>
/// 鼠标移动显示坐标及所在点的灰度值。
/// </summary>
private void HSImageWindow_HMouseMove(object sender, HMouseEventArgs e)
{
if (hImage != null)
{
HOperatorSet.CountChannels(hImage, out HTuple hChannels);
HTuple grayValue = new HTuple();
pointX = Convert.ToInt32(e.X);
pointY = Convert.ToInt32(e.Y);
if (pointX >= 0 && pointY >= 0
&& pointX < width && pointY < height)
{
HOperatorSet.GetGrayval(hImage, pointY, pointX, out grayValue);
if (hChannels != null && hChannels == 1)
{
context = $"[X = {pointX},Y = {pointY},Gray = {grayValue.D.ToString("F0")}]";
Debug.WriteLine(context);
mainForm.Tbx_ImageInfo.Text = context;
}
else if (hChannels != null && hChannels == 3)
{
context = $"[X = {pointX},Y = {pointY},Gray = {grayValue.D.ToString("F0")}]";
Debug.WriteLine(context);
mainForm.Tbx_ImageInfo.Text = context;
}
}
}
else
{
context = $"[X = {(int)e.X},Y = {(int)e.Y}]";
Debug.WriteLine(context);
mainForm.Tbx_ImageInfo.Text = context;
}
}
/// <summary>
/// 加载图像的方法
/// </summary>
private void Btn_LoadImage_Click(object sender, EventArgs e)
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.InitialDirectory = "F:\\Image";
if (openFile.ShowDialog() == DialogResult.OK)
{
HOperatorSet.ReadImage(out hImage, openFile.FileName);
HOperatorSet.DispObj(hImage, window);
HOperatorSet.GetImageSize(hImage, out width, out height);
}
}
/// <summary>
/// 绘制箭头
/// </summary>
private void Btn_DrawArrow_Click(object sender, EventArgs e)
{
if (!drawArrowThread.IsAlive)
{
drawArrowThread = new Thread(DrawArrowThreadMethod);
drawArrowThread?.Start();
}
else
{
drawArrowThread?.Start();
}
}
}