【C#联合halcon实现绘制ROI功能】

前言

C#联合halcon实现绘制ROI功能:

C#联合Halcon,使用HDrawingObject、HDrawingObjectXld,绘制矩形、方向矩形、圆形、椭圆、自定义ROI。支持拖动、重设大小、选中。

运行结果

代码

代码结构

MainForm 视图

MainViewModel 视图模型

ROI ROI模型

GenArrow 生成带箭头的点

GrawArrowViewModel 绘制箭头视图模型

MainForm

csharp 复制代码
public partial class MainForm : Form
    {
        DrawArrowViewModel drawArrowViewModel;
        MainViewModel viewModel;
        public MainForm()
        {
            InitializeComponent();
            drawArrowViewModel = new DrawArrowViewModel(this);
            viewModel = new MainViewModel(this);
        }
    }

MainForm 设计器

csharp 复制代码
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

csharp 复制代码
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

csharp 复制代码
 [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

csharp 复制代码
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

csharp 复制代码
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();
        }
    }
}
相关推荐
小板凳-BGM8 小时前
C# 第二阶段 modbus
开发语言·ui·c#
黄金小码农8 小时前
c# 2024/12/25 周三
开发语言·c#
geovindu9 小时前
CSharp: Oracle Stored Procedure query table
数据库·oracle·c#·.net
yngsqq10 小时前
cad c# 二次开发 ——动态加载dll 文件制作(loada netloadx)
c#
9分钟带帽14 小时前
opencv存图速度测试
opencv·halcon
ling1s14 小时前
C#核心(18)面向对象多态vob
java·开发语言·c#
月巴月巴白勺合鸟月半15 小时前
一个C#开发的APP
c#·web
我曾经是个程序员16 小时前
C#File文件基础操作大全
开发语言·c#