一、介绍
1、项目背景
老板: 公司招了个牛马实习生,月薪3000,干软件开发,还得懂计算机算法。
第一天跑去吐槽:"你写的这软件也太难用了,界面卡成PPT,功能一用就崩,你是专门来写Bug的吗?"
实习生: 头也不抬:"别问,3000块自然有3000块的写法。"
老板: "那能不能做一个视觉工具?现场人员点一点就能用那种。"
实习生: "可以,别问原理,问就是封装。"
老板: "什么原理?"
实习生: "说了别问。"

于是,这个基于 WPF + HALCON 的小工具就诞生了。
功能很简单:找线、找圆、读码、模板匹配,就这四板斧。
目标很朴素------让不会写代码的人,也能把视觉检测跑起来。

2、为什么做这个工具
写业务痛点:
- 现场人员不会写代码
- 每次换产品都要重新调参数
- 算法工程师不可能天天站产线旁边
- 所以需要一个"能点、能拖、能保存、能运行"的视觉配置工具
以前调一个圆,要改代码、编译、运行、截图、发群里问一句"这样行不行"。
现在不一样了,现在可以直接拖 ROI。
效率提高了,头发保住了一点点。

3、项目整体结构

可以放一张项目结构图,介绍几个工程:
PiCoreAlgo:核心算法库,封装 HALCON 算法PiCoreAlgoUi/UI:WPF 可视化配置界面PLog:日志模块PNotificationCenter:通知中心,用于 UI 模块通信TestAlg:测试调用工程Dll/halcondotnet.dll:HALCON .NET 依赖
这个界面主要干三件事:
-
打开图片
-
配置工具
-
看结果
老板转头看了看软件功能: "等等,你这日志模块和消息通知怎么写的?我看跑起来还算稳定。"
实习生: "那个啊,PLog 日志模块和 PNotificationCenter 之前章节有讲过。"
老板: "什么章节?"
实习生: "老板,年轻人你渴望力量吗?"
老板: "......什么意思?"

实习生: 从桌子底下抽出一摞书,"可以看看以下的书📕,基本功。
C#运行日志
https://blog.csdn.net/qq_54122623/article/details/150564238?spm=1011.2124.3001.6209
C# 观察者模式
https://blog.csdn.net/qq_54122623/article/details/160167275?spm=1011.2124.3001.6209
二、UI界面
1、Ui界面介绍
讲 UI,而不是代码。
可以介绍:
- 左侧/中间:图像显示区
- 右侧/上方:工具配置区
- 支持打开图片
- 支持缩放、拖动
- 支持添加 ROI
- 支持运行后显示结果

2、Ui样式
老板兴致上来了: "那界面能不能再好看点?换个皮肤什么的。"
实习生: "好说,我默认写了三种样式,分别是 MyStyles.xaml、MyStylesSharp.xaml、MyStylesHome.xaml。你想换风格,把 Source="MyStylesHome.xaml" 替换一下就行。"
老板: "这么高级?有什么讲究?"
实习生: "讲究大了。MyStyles 是正常风格,MyStylesSharp 是锐利商务风,MyStylesHome 是居家暖色调------其实就是我把三个按钮颜色换了一下。"
老板: "......就这?"
实习生: "老板,3000块的工资,我能封装出三套皮肤已经是买一送二了。您要是不满意,xaml 文件是开放的,您可以根据个人喜好自己写样式。"
老板: "我自己写?"
实习生: "对,简陋点也无所谓,反正您写出来的大概率比我的还简陋。"

接下来就是

三、找线建模工具
你告诉软件大概在哪找,它负责把边缘抠出来。
老板: "你这视觉软件,找线功能怎么一堆参数?现场工人哪看得懂。"
实习生: "老板别慌,我给你用人话翻译一遍:
-
StartX / StartY------告诉软件从哪开始找。 -
EndX / EndY------告诉软件找到哪为止。两点连条线,就这条线上找边缘,别的地方不管。 -
CaliperNum------放多少个卡尺。相当于派多少个人沿线站岗,人多了查得细,人少了容易漏。 -
LineHeight------往两边找多远。设小了边缘漏了,设大了把隔壁工件的边也抓来了。 -
LineWidth------每个卡尺的宽度,胖卡尺还是瘦卡尺的区别。 -
Threshold------边缘够不够硬。数值越高要求越严,跟找对象一样,眼光太高一个都找不到。"
老板: "那这个 Transition 呢?"
实习生: "方向。黑到白还是白到黑,搞反了边缘就跟你的人生一样,全是错的。"
老板: "......那 Select 呢?"
实习生: "找到一堆边缘选哪个。第一个、最后一个、还是全都要。成年人建议选第一个,别贪。"
老板: "IgnoreEdge 呢?"
实习生: "剔除捣乱的边缘点,防止一颗老鼠屎坏了一锅汤。跟公司开掉摸鱼的一个道理。"
老板: "Sigma 呢?"
实习生: "图太糊了就加点,相当于美颜磨皮,但别加太猛,不然边缘也一起糊没了。"
老板: "那要是调了半天还是找不到呢?"
实习生: "五句话排查法,记好了:
-
线没放到边缘附近------你让人家在起点找,边缘在终点,这不扯淡嘛。
-
Threshold太高------眼光太高,边缘不配。 -
Transition方向反了------南辕北辙。 -
CaliperNum太少------就派三个人搜一座山,能找到才怪。 -
图像太糊------边缘像被生活磨平了一样,换相机吧,这个3000块真不包。"

老板: "......那这套参数你调好了吗?"
实习生: "默认就能跑。能找、能测、能出结果,3000块的标准已经非常体面了。要自动调参也行------"
老板: "加钱对吧?"
实习生: "老板悟了。"

四、找圆建模工具
老板: "上次找线说完了,这找圆功能又一堆参数,你再给我翻译翻译。"
实习生: "好嘞。找圆的思路很简单------就像一群质检员围着圆站一圈,每个人低头看自己脚下有没有边缘。最后大家把看到的点交上来,系统说:行,我给你们拟合一个圆。"
老板: "那参数呢?"

实习生: "挨个说:
-
CenterX / CenterY------圆心大概在哪。不用很准,大概就行,别指到工件外面去。 -
Radius------大概半径。也就是你告诉软件:圆差不多这么大,别跑太远去找。 -
CaliperNum------安排多少个质检员。人太少,圆拟合不稳;人太多,老板会问你为什么运行慢。3000块的建议16到24个,体面。 -
CaliperLength------每个质检员往内外看多远。太短了边缘在视野外,太长了把隔壁的边也抓来凑热闹。 -
CaliperWidth------卡尺宽度。质检员的眼睛睁多大。 -
Threshold------边缘要多硬才算数。跟招人一样,门槛太高一个合格的都没有。 -
Transition------黑到白还是白到黑。方向反了,质检员全体向后转,找到一个寂寞。 -
Direction------从外往里找,还是从里往外找。你告诉质检员先看哪头。 -
IgnoreEdge------忽略捣乱的边缘点。有人眼神不好报了假点,直接踢出去,防止一颗老鼠屎坏了拟合的圆。 -
Sigma------平滑。图太糊了就加点,跟美颜一个道理,但别太过,不然圆也磨成椭圆了。"
老板: "那要是找不出来呢?"
实习生: "老规矩,五句话排查:
-
圆ROI没套准------你圈了个寂寞,圆根本不在里面。
-
Threshold太高------边缘强度不够,人家不配。 -
Transition方向反了------质检员全员面壁。 -
CaliperLength太短------边缘刚好在视野外面,差一毫米就是两个世界。 -
图像太糊------边缘像被生活磨平了一样,换相机,这个3000块真不包。"
老板: "总结一下?"
实习生: "找圆失败,一般不是算法摆烂,而是参数没哄好。哄好了,它比狗都听话。"
老板: "那你默认参数哄好了吗?"
实习生: "哄好了。能找、能测、能出结果,3000块的标准,这圆拟合得已经非常体面了。"

五、模板匹配建模工具
老板: "上次找线找圆说完了,这模板匹配又是什么?参数更多了。"
实习生: "模板匹配很好理解------你先给系统看一眼标准样品,然后告诉它:'记住这个东西,下次在图里把它找出来。'"
老板: "就这么简单?"
实习生: "就这么简单。参数我给你翻一遍:
-
ModelIDPath------模板存哪。你得告诉系统把记住的东西放哪个文件夹,路径写错了它就跟失忆了一样,谁都不认识。 -
TrainROIType------训练区域类型。你是圈个矩形还是画个圆让系统认,别选太大,你只是让它记住产品特征,不是让它记住整张图的青春。 -
TrainRow / TrainCol------训练区域中心位置。你告诉系统:盯着这儿看,特征在这。 -
TrainPhi------训练区域角度。产品歪着放的,就告诉它歪了多少度。 -
TrainLength1 / TrainLength2------训练区域宽高。框多大,框大了背景干扰多,框小了特征没圈全。 -
FullImageFlag------是否全图搜索。选是,系统满图找,但慢;选否,只在指定区域找,快。 -
FindROIType------查找区域类型。跟训练一个意思,告诉系统去哪找。 -
FindRow / FindCol------查找区域中心。目标大概在哪,告诉它。 -
FindLength1 / FindLength2------查找区域宽高。范围给够,别目标挪两步就出圈了。 -
MinScore------最低匹配分数。满分100,低于这个分系统就当没看见。设太高,稍微偏一点就装死;设太低,什么阿猫阿狗都认。"
老板: "那后面这几个呢?"
实习生: "ModelingRow / ModelingColumn / ModelingAngle------建模时的位置和角度。就是系统记住模板时产品站哪、朝哪,后续坐标跟随全靠这个基准。"
老板: "那如果目标有点旋转、偏移、缩放,还能认吗?"
实习生: "一般能认。系统没那么矫情,稍微歪点、远点、大点小点,它都能凑合。但如果图片糊得像监控截图,或者你模板区域选得太随意,那就别怪它说------'这谁啊?不认识。'"
老板: "那匹配失败了怎么排查?"
实习生: "还是老规矩:
-
训练ROI没选到明显特征------你让人家记了个寂寞。
-
查找ROI没覆盖目标位置------目标在框外面,系统看都看不见。
-
MinScore设置太高------满分100你设99,系统心想:臣妾做不到啊。 -
模板区域太大------背景干扰太多,系统不知道该看哪。
-
图片亮度变化太大------昨天拍的晴天,今天拍的阴天,系统直接脸盲。
-
模型文件路径不对------模板压根没保存成功,系统脑子里是空的。"
老板: "这个第六点怎么加粗了?"
实习生: "因为这是最容易犯的错,仅次于忘记保存Word文档。"
老板: "那你这默认参数都设好了?"
实习生: "设好了。能存、能找、能匹配,3000块的标准,已经认得挺体面了。记住一句话------"
老板: "什么?"
实习生: "你只是让系统记住产品特征,不是让它记住整张图的青春。"

六、读码建模工具
老板: "找线、找圆、模板匹配都说完了,你这还有个读码功能,又是干嘛的?"
实习生: "读码就是识别二维码、DataMatrix这些。产品追溯码、料盘码、工件ID,统统交给它。"
老板: "操作复杂吗?"

实习生: "不复杂,几步走:打开图片,选读码工具,起个名比如code_1,选码类型,然后决定要不要框ROI------要的话拖个矩形把码圈住,设好识别数量和超时时间,点运行,完事。"
老板: "ROI又是什么?"
实习生: "Region of Interest。这个工具有一个朴素原则:能框住就别全图找。"
老板: "全图找不行吗?"
实习生: "不是不行,但就像你在公司群里翻三年前的通知。理论上能找到,实际上你已经开始怀疑人生。所以现场用一定开ROI,只在框里找,速度快还稳。"
老板: "参数呢?"
实习生: "少,就这几个:
-
CodeType------码类型。二维码还是DataMatrix,别选错。选错了就像拿饭卡刷地铁,系统不是不努力,是你俩根本不在一个频道。 -
IsUseRoi------是否启用ROI。建议勾上,理由刚才说了,全图找约等于大海捞针。 -
FindRow / FindCol------ROI中心位置。告诉它大概在哪。 -
FindLength1 / FindLength2------ROI宽高。框大点没事,但一定要把码完整框住,框一半它就不认识了。 -
FindPhi------ROI角度。码歪了就把框也歪一下。 -
ExpectedCount------期望识别几个码。一个就填1,多个就填对应数量。 -
TimeOut------最长识别时间。超过这个时间还没读到就放弃,别让它死磕。 -
DefaultParameters------识别策略。系统预设的读码模式,默认就行,别手贱乱改。"
老板: "那读不出来怎么回事?"
实习生: "常见六大原因:
-
ROI没框住完整码------码在框外面,系统看都看不见。
-
码太小------小到系统怀疑这是不是个码。
-
图片反光------一道白光正好打在码上,系统直接瞎了。
-
码类型选错了------拿饭卡刷地铁,刷到天荒地老也刷不开。
-
TimeOut太短------系统刚准备发力,你喊停了。 -
码本身印刷质量差------糊了、缺了、脏了,神仙难救。"
老板: "默认能跑吗?"
实习生: "能。打开图,框个ROI,点运行,3000块的标准,已经读得非常体面了。记住一句话:能框住就别全图找,全图找那是跟自己过不去。"

六、总结
老板: "讲了半天,你这几个工具到底能干啥,给我总结一下。"

实习生: "行,一句话总结:FindLineControl负责找线,FindCircleControl负责找圆,FindDataCodeControl负责读码,PatternToolControl负责模板匹配。"
老板: "就这?"
实习生: "就这。说白了,它们解决的不是'算法有多高级'的问题,而是'现场人员能不能点一点就把检测跑起来'的问题。能打开图片,能拖ROI,能调参数,能保存配置,能看检测结果------对于一个月薪3000的牛马实习生来说,已经不能要求更多了。"
老板: "那实际项目里怎么用?总不能天天打开这个配置界面点来点去吧?"
实习生: "老板问到点子上了。实际项目一般不会让用户天天点配置界面。正确的姿势是:先用这个工具把参数调好、保存好,然后在项目代码里调这些配置好的工具。"
老板: "比如?"
实习生: "比如在PiCoreAlgo的Project文件夹下面建一个.cs文件,写个项目逻辑类,里面写个ReadQcCode方法专门读二维码。流程大概就是:上位机传图进来,项目类加载配置好的读码工具,调FindDataCode去识别,拿到字符串和结果,返回给上位机。这个配置工具更像是'调参后台',真正上线跑的时候是项目代码在干活。"

老板: "那你这代码结构怎么分的?"
实习生: "三层:
-
PiCoreAlgo------后端算法层,负责集成HALCON,封装找线找圆读码模板匹配这些算法。 -
UI------前端配置层,就是你现在看到的这个界面,加ROI、调参数、保存配置、看结果。 -
TestAlg------测试调用层,模拟上位机调用算法,看看真实项目里怎么把这些工具跑起来。"
老板: "HALCON呢?授权贵不贵?"
实习生: "目前用的学习版。大家都懂。能学习、能测试、能跑Demo,先把流程打通。后续如果继续更新,考虑把底层从HALCON换成OpenCV。"
老板: "OpenCV?"
实习生: "纯免费,部署也轻松。到时候老板再问'这个软件授权多少钱',我就可以自信一点说:老板,这次不要钱。"
老板: "免费的好啊!"

实习生: "免费的通常也意味着更多事情要自己干。这个坑后面再慢慢填。"
老板: "所以下一篇讲什么?"
实习生: "讲这些配置好的工具怎么在真实项目里被调用。怎么在Project目录下创建项目类,怎么写ReadQcCode这样的业务方法,怎么把结果返回给上位机,怎么让视觉工具从'能点点点'变成真正能在产线上跑的检测流程。"
老板: "最后总结一下你这工具?"
实习生: "这个工具不是什么高大上的视觉平台,它只是一个朴素的工业视觉小工具。但它能让现场人员少问几句'这个参数怎么调',也能让开发人员少加几次班。"
老板: "那我可以少招几个人了?"
实习生: "......老板,3000块的牛马只有一个,您看着办。"
老板: "对于3000元的写法来说呢?"
实习生: "这已经是非常有性价比的一集了。 但是可能还是会有BUG"

Gitee
https://gitee.com/Li_zhengyuan/wpf-halcon.git
