WPF+Halcon 培训项目实战(7):目标匹配助手

文章目录

前言

为了更好地去学习WPF+Halcon,我决定去报个班学一下。原因无非是想换个工作。相关的教学视频来源于下方的Up主的提供的教程。这里只做笔记分享,想要源码或者教学视频可以和他联系一下。

相关链接

微软系列技术教程 WPF 年度公益课程
Halcon开发 CSDN博客专栏
个人学习的Gitee 项目地址仓库

项目专栏

WPF+Halcon实战项目

运行环境

  • .net core 8.0
  • visual studio 2022
  • halcon HDevelop 20.11
  • windows 11

匹配图片

7.Halcon代码导出

简单的模板匹配代码

c 复制代码
* 读取Resource的文件
read_image (Image, '../resource/1.png')
dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle)
dev_display (Image)

* 截取ROI
draw_rectangle1 (WindowHandle, Row1, Column1, Row2, Column2)
gen_rectangle1 (Rectangle, Row1, Column1, Row2, Column2)
reduce_domain (Image, Rectangle, ImageReduced)

* 生成匹配模板
create_shape_model (ImageReduced, 'auto', -0.39, 0.79, 'auto', 'auto', 'use_polarity', 'auto', 'auto', ModelID)
* 导出匹配模板
write_shape_model (ModelID, 'output.sha')

find_shape_model (ImageReduced, ModelID, -0.39, 0.79, 0.5, 1, 0.5, 'least_squares', 0, 0.9, Row, Column, Angle, Score)

导出C#代码

我之前做过C# 代码导出,详情看这个网址

Halcon WPF 开发学习笔记(2):Halcon导出c#脚本和WPF初步开发

控制台测试

这里使用.net core 8.0新建项目

导入halcon.dll

可以通过安装目录的dll导入程序

也可以直接在Nuget上面搜索halcon

csharp 复制代码
using HalconDotNet;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            HObject img;
            HOperatorSet.GenEmptyObj(out img);
            //读取图片,这里填你的图片的位置
            HOperatorSet.ReadImage(out img, "Resources\\1.png");

            //读取形状匹配模板,路径选择你的文件路径
            HTuple modelId = new HTuple();
            HOperatorSet.ReadShapeModel("Resources\\output.shm", out modelId);

            //匹配Image中的结果
            HTuple hv_WindowHandle = new HTuple(), hv_Row1 = new HTuple();
            HTuple hv_Column1 = new HTuple(), hv_Row2 = new HTuple();
            HTuple hv_Column2 = new HTuple(), hv_ModelID = new HTuple();
            HTuple hv_Row = new HTuple(), hv_Column = new HTuple();
            HTuple hv_Angle = new HTuple(), hv_Score = new HTuple();
            //hv_Row.Dispose(); hv_Column.Dispose(); hv_Angle.Dispose(); hv_Score.Dispose();
            HOperatorSet.FindShapeModel(img, modelId, -0.39, 0.79, 0.5, 1,
    0.5, "least_squares", 0, 0.9, out hv_Row, out hv_Column, out hv_Angle, out hv_Score);

            //输出匹配结果
            Console.WriteLine($"分数:{hv_Score},Row坐标:{hv_Row},Col坐标:{hv_Column},角度:{hv_Angle}");

            Console.WriteLine("程序运行完毕!");
            Console.ReadKey();


        }
    }
}

运行报错:


运行结果:

7.WPF导入Halcon

如果你上个代码已经跑成功了,说明你已经能成功导入Halcon代码并进行运行,接下来我们就要开始学习WPF如何导入Halcon

新建WPF程序

我不想使用Prism框架,我想用HandyControl替换Material Design UI框架。所以我前端时间尝试了一下代码的书写。

将程序修改为控制台程序

WPF仿网易云搭建笔记(7):HandyControl重构
Halcon WPF 开发学习笔记(3):WPF+Halcon初步开发
WPF 消息日志打印帮助类:HandyControl+NLog+彩色控制台打印+全局异常捕捉
WPF-UI HandyControl 控件简单实战+IconPacks矢量图导入

由于WPF比较复杂,这里我就不展开说明了。具体代码可以看我的Gitee仓库

个人学习的Gitee 项目地址仓库

Nuget目录

项目目录

相关代码

app.xaml:导入HandyControl Style主题

xml 复制代码
<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <!--导入HandyControl Style主题-->
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

App.xaml.cs:全局异常捕捉

csharp 复制代码
using System.Configuration;
using System.Data;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using WpfApp1.Utils;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {

        public App()
        {
            //首先注册开始和退出事件
            this.Startup += new StartupEventHandler(App_Startup);
            this.Exit += new ExitEventHandler(App_Exit);
        }

        void App_Startup(object sender, StartupEventArgs e)
        {
            //UI线程未捕获异常处理事件
            this.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(App_DispatcherUnhandledException);
            //Task线程内未捕获异常处理事件
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
            //非UI线程未捕获异常处理事件
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        }

        void App_Exit(object sender, ExitEventArgs e)
        {
            //程序退出时需要处理的业务
        }

        void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            try
            {
                e.Handled = true; //把 Handled 属性设为true,表示此异常已处理,程序可以继续运行,不会强制退出      
                MsgHelper.Error("UI线程异常:" + e.Exception.Message);
                //MessageBox.Show("UI线程异常:" + e.Exception.Message);
            }
            catch (Exception ex)
            {
                //此时程序出现严重异常,将强制结束退出
                //MessageBox.Show("UI线程发生致命错误!");
                MsgHelper.FatalGlobal("UI线程发生致命错误!"+ex.ToString());

            }

        }

        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            StringBuilder sbEx = new StringBuilder();
            if (e.IsTerminating)
            {
                sbEx.Append("非UI线程发生致命错误");
            }
            sbEx.Append("非UI线程异常:");
            if (e.ExceptionObject is Exception)
            {
                sbEx.Append(((Exception)e.ExceptionObject).Message);
            }
            else
            {
                sbEx.Append(e.ExceptionObject);
            }
            //MessageBox.Show(sbEx.ToString());
            MsgHelper.Error(sbEx.ToString());

        }

        void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            //task线程内未处理捕获
            MsgHelper.Error("Task线程异常:" + e.Exception.Message);

            //MessageBox.Show("Task线程异常:" + e.Exception.Message);
            e.SetObserved();//设置该异常已察觉(这样处理后就不会引起程序崩溃)
        }
    }

}

NlogHelper:日志打印

csharp 复制代码
using NLog.Config;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp1.Utils
{
    public static class NLogHelper
    {
        private static Logger logger;

        static NLogHelper()
        {
            LogManager.Configuration = new XmlLoggingConfiguration(string.Format("{0}/NLog.config", AppDomain.CurrentDomain.BaseDirectory.ToString()));
            logger = NLog.LogManager.GetCurrentClassLogger();
        }

        public static void Debug(string msg)
        {
            Console.WriteLine(msg);
            logger.Debug(msg);
        }

        public static void Info(string msg)
        {
            ConsoleWirte(msg,ConsoleColor.Green);
            logger.Info(msg);

        }

        public static void Error(string msg)
        {
            ConsoleWirte(msg, ConsoleColor.Red);

            logger.Error(msg);
        }

        public static void Warning(string msg)
        {
            ConsoleWirte(msg, ConsoleColor.Yellow);
            logger.Warn(msg);
        }

        public static void ConsoleWirte(string msg,ConsoleColor color)
        {
            Console.ForegroundColor = color;
            Console.WriteLine(msg);
            Console.ResetColor();
        }
    }
}

MsgHelper:HandyControl+NLog组合

csharp 复制代码
using HandyControl.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp1.Utils
{
    public static class MsgHelper
    {
        public enum MsgType { Info, Warn, Error, Success, Fatal }

        /// <summary>
        /// 打印消息
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="type"></param>
        /// <param name="isGlobal"></param>
        public static void ShowMsg(string msg, MsgType type, bool isGlobal = false)
        {
            if (isGlobal)
            {
                switch (type)
                {
                    case MsgType.Info:
                        NLogHelper.Info(msg);
                        Growl.InfoGlobal(msg);
                        break;
                    case MsgType.Warn:
                        NLogHelper.Warning(msg);
                        Growl.WarningGlobal(msg);
                        break;
                    case MsgType.Error:
                        NLogHelper.Error(msg);
                        Growl.ErrorGlobal(msg);
                        break;
                    case MsgType.Success:
                        NLogHelper.Info(msg);
                        Growl.SuccessGlobal(msg);
                        break;
                    case MsgType.Fatal:
                        NLogHelper.Error(msg);
                        Growl.FatalGlobal(msg);
                        break;
                }
            }
            else
            {
                switch (type)
                {
                    case MsgType.Info:
                        NLogHelper.Info(msg);
                        Growl.Info(msg);
                        break;
                    case MsgType.Warn:
                        NLogHelper.Warning(msg);
                        Growl.Warning(msg);
                        break;
                    case MsgType.Error:
                        NLogHelper.Error(msg);
                        Growl.Error(msg);
                        break;
                    case MsgType.Success:
                        NLogHelper.Info(msg);
                        Growl.Success(msg);
                        break;
                    case MsgType.Fatal:
                        NLogHelper.Error(msg);
                        Growl.Fatal(msg);
                        break;
                }
            }
        }



        /// <summary>
        /// 询问回调
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="callback"></param>
        /// <param name="isGlobal"></param>
        public static void Ask(string msg, Action<bool> callback, bool isGlobal = false)
        {
            NLogHelper.Info(msg);
            if (isGlobal)
            {
                Growl.AskGlobal(msg, isConfrimed =>
                {
                    callback(isConfrimed);
                    return true;
                });
            }
            else
            {
                Growl.Ask(msg, isConfrimed =>
                {
                    callback(isConfrimed);
                    return true;
                });
            }
        }

        /// <summary>
        /// 强制清空全部消息
        /// </summary>
        public static void CleanAll()
        {
            Growl.Clear();
            Growl.ClearGlobal();
        }

        public static void Info(string msg)
        {
            NLogHelper.Info(msg);
            Growl.Info(msg);
        }

        public static void Warning(string msg)
        {
            NLogHelper.Warning(msg);
            Growl.Warning(msg);

        }

        public static void Error(string msg) {
            NLogHelper.Error(msg);
            Growl.Error(msg);
        }

        public static void Fatal(string msg) {
            NLogHelper.Error(msg);
            Growl.Fatal(msg);
        }

        public static void Success(string msg) {
            NLogHelper.Info(msg);
            Growl.Success(msg);
        }

        public static void InfoGlobal(string msg)
        {
            NLogHelper.Info(msg);
            Growl.InfoGlobal(msg);
        }

        public static void WarningGlobal(string msg)
        {
            NLogHelper.Warning(msg);
            Growl.WarningGlobal(msg);

        }

        public static void ErrorGlobal(string msg)
        {
            NLogHelper.Error(msg);
            Growl.ErrorGlobal(msg);
        }

        public static void FatalGlobal(string msg)
        {
            NLogHelper.Error(msg);
            Growl.FatalGlobal(msg);
        }

        public static void SuccessGlobal(string msg)
        {
            NLogHelper.Info(msg);
            Growl.SuccessGlobal(msg);
        }

    }
}

IconPacksExtesion:IconPacks Material Design 矢量Icon扩展

csharp 复制代码
using MahApps.Metro.IconPacks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
using System.Windows.Media;

namespace WpfApp1.ViewModels
{
    public class IconPacksExtesion : PackIconGeometryExtension<PackIconMaterialKind>
    {
        protected override IDictionary<PackIconMaterialKind, string> DataIndex => PackIconMaterialDataFactory.DataIndex.Value;

        public IconPacksExtesion() { }

        public IconPacksExtesion(PackIconMaterialKind kind) : base(kind) { }
    }

    public abstract class PackIconGeometryExtension<TKind> : MarkupExtension where TKind : Enum
    {
        public TKind Kind { get; set; }

        protected abstract IDictionary<TKind, string> DataIndex { get; }

        protected PackIconGeometryExtension() { }

        protected PackIconGeometryExtension(TKind kind) => Kind = kind;

        public override object ProvideValue(IServiceProvider serviceProvider) => Geometry.Parse(DataIndex[Kind]);
    }
}

注意:这里已经默认你很了解WPF程序项目。已经搭建好了WPF框架。而且本项目不会使用Prism,而是使用原生的WPF MVVM开发

程序生成时将整个Resources文件复制到Debug文件夹中

Visual Studio C# 项目生成时复制项目资源目录到生成目录

初始界面测试

MainView

xml 复制代码
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:Views="clr-namespace:WpfApp1.Views"
        xmlns:ViewModels="clr-namespace:WpfApp1.ViewModels"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.DataContext>
        <ViewModels:MainViewModel />
    </Window.DataContext>
    <Grid>
        <hc:TabControl Style="{StaticResource TabControlInLine}">
      
            <hc:TabItem Header="Halcon测试界面">
                <Views:HalconView />
            </hc:TabItem>
           <!--存放你其它的页面-->
        </hc:TabControl>
    </Grid>
</Window>

HalconView.xaml

xml 复制代码
<UserControl x:Class="WpfApp1.Views.HalconView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:WpfApp1.Views"
             mc:Ignorable="d"
             xmlns:ViewModels="clr-namespace:WpfApp1.ViewModels"
             xmlns:hc="https://handyorg.github.io/handycontrol"
             xmlns:halcon="clr-namespace:HalconDotNet;assembly=halcondotnet"
             d:DesignHeight="450"
             d:DesignWidth="800">
    <UserControl.DataContext>
        <ViewModels:HalconViewModel />
    </UserControl.DataContext>
    <UserControl.Resources>
        <Style TargetType="Button"
               x:Key="MyButton"
               BasedOn="{StaticResource {x:Type Button}}">
            <Setter Property="Margin"
                    Value="5" />
        </Style>
    </UserControl.Resources>
    <DockPanel>
        <StackPanel Orientation="Horizontal"
                    DockPanel.Dock="Top">
            <StackPanel.Resources>
                <Style TargetType="Button"
                       BasedOn="{StaticResource MyButton}" />
            </StackPanel.Resources>
            <Button Content="读取图片"
                    hc:IconElement.Geometry="{ViewModels:IconPacksExtesion Kind=ImagePlus}" Command="{Binding ReadImgBtn}" />
            <Button Content="定位结果"
                    hc:IconElement.Geometry="{ViewModels:IconPacksExtesion Kind=ImageMarkerOutline}" Command="{Binding LocateBtn}"/>
            <Button Content="生成矩形"
                    hc:IconElement.Geometry="{ViewModels:IconPacksExtesion Kind=ShapeRectanglePlus}" Command="{Binding InitRectangleBtn}"/>
            <Button Content="生成图片"
                    hc:IconElement.Geometry="{ViewModels:IconPacksExtesion Kind=StickerPlus}" Command="{Binding InitImgBtn}"/>
        </StackPanel>
        <halcon:HSmartWindowControlWPF />
    </DockPanel>
</UserControl>

HalconViewModel.cs

csharp 复制代码
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp1.Utils;

namespace WpfApp1.ViewModels
{
    public class HalconViewModel : ObservableObject
    {

        public RelayCommand ReadImgBtn { get; set; }

        public RelayCommand LocateBtn {  get; set; }

        public RelayCommand InitRectangleBtn { get; set; }

        public RelayCommand InitImgBtn { get; set; }
        public HalconViewModel() {
            ReadImgBtn = new RelayCommand(() =>
            {
                MsgHelper.Info("读取图片");
            });

            LocateBtn = new RelayCommand(() => {
                MsgHelper.Info("显示定位结果");
            });

            InitRectangleBtn = new RelayCommand(() => {
                MsgHelper.Info("生成矩形");
            });

            InitImgBtn = new RelayCommand(() => {
                MsgHelper.Info("生成图片");
            });
        
        }

    }
}

测试运行结果

总结

下一章我们将重点转移到WPF 的Halcon组件的使用中去。

相关推荐
zxb11c38 分钟前
wpf 事件转命令的方式
wpf
冷眼Σ(-᷅_-᷄๑)1 小时前
WPF异步UI交互功能的实现方法
c#·wpf
shepherd枸杞泡茶4 小时前
WPF触发器
wpf
公子小六7 小时前
在WPF程序中实现PropertyGrid功能
windows·microsoft·c#·.net·wpf
当下就是最好1 天前
WPF应用程序的生命周期-笔记
wpf
九鼎科技-Leo2 天前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
麻花20132 天前
C#之WPF的C1FlexGrid空间的行加载事件和列事件变更处理动态加载的枚举值
开发语言·c#·wpf
lcintj2 天前
【WPF】Prism学习(九)
学习·wpf·prism
界面开发小八哥2 天前
界面控件DevExpress WPF中文教程:网格视图数据布局的列和卡片字段
wpf·界面控件·devexpress·ui开发·用户界面
△曉風殘月〆2 天前
如何在WPF中嵌入其它程序
wpf