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

匹配图片

WPF+Halcon 培训项目实战(7):目标匹配助手_第1张图片

7.Halcon代码导出

简单的模板匹配代码

* 读取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#代码

WPF+Halcon 培训项目实战(7):目标匹配助手_第2张图片
我之前做过C# 代码导出,详情看这个网址

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

控制台测试

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

WPF+Halcon 培训项目实战(7):目标匹配助手_第3张图片

导入halcon.dll

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

WPF+Halcon 培训项目实战(7):目标匹配助手_第4张图片
也可以直接在Nuget上面搜索halcon
WPF+Halcon 培训项目实战(7):目标匹配助手_第5张图片

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();


        }
    }
}

运行报错:

WPF+Halcon 培训项目实战(7):目标匹配助手_第6张图片
WPF+Halcon 培训项目实战(7):目标匹配助手_第7张图片

运行结果:

在这里插入图片描述

7.WPF导入Halcon

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

新建WPF程序

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

将程序修改为控制台程序
WPF+Halcon 培训项目实战(7):目标匹配助手_第8张图片

WPF仿网易云搭建笔记(7):HandyControl重构

Halcon WPF 开发学习笔记(3):WPF+Halcon初步开发

WPF 消息日志打印帮助类:HandyControl+NLog+彩色控制台打印+全局异常捕捉

WPF-UI HandyControl 控件简单实战+IconPacks矢量图导入

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

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

Nuget目录

WPF+Halcon 培训项目实战(7):目标匹配助手_第9张图片

项目目录

WPF+Halcon 培训项目实战(7):目标匹配助手_第10张图片

相关代码

app.xaml:导入HandyControl Style主题

<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>
            
            <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:全局异常捕捉

using System.Configuration;
using System.Data;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using WpfApp1.Utils;

namespace WpfApp1
{
    /// 
    /// Interaction logic for App.xaml
    /// 
    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:日志打印

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组合

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 }

        /// 
        /// 打印消息
        /// 
        /// 
        /// 
        /// 
        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;
                }
            }
        }



        /// 
        /// 询问回调
        /// 
        /// 
        /// 
        /// 
        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;
                });
            }
        }

        /// 
        /// 强制清空全部消息
        /// 
        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扩展

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# 项目生成时复制项目资源目录到生成目录
WPF+Halcon 培训项目实战(7):目标匹配助手_第11张图片

WPF+Halcon 培训项目实战(7):目标匹配助手_第12张图片

WPF+Halcon 培训项目实战(7):目标匹配助手_第13张图片

初始界面测试

MainView

<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

<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}}">
            "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

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 培训项目实战(7):目标匹配助手_第14张图片

总结

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

你可能感兴趣的:(WPF+Halcon实战项目,wpf,Halcon)