⼀⽂弄懂halcon例程:rim.hdev
⼀⽂弄懂halcon例程:rim.hdev
打怪的路上总是⽆聊的,但是也不能不打啊,我⾃⼰现在也在每天打怪升级呢。昨天就因为⼀个问题,我到视觉群问⾥⾯的⼤⽜,结果,他不帮我解答,他不告诉我怎么解决就算了呗,却总是说我基础不⾏,要从头学,要去他那报班。我说我事情有个轻重缓急啊,现在项⽬上⼀个⼩问题耽误进度了,我抛下不管,跑去报班从头学,回头再来解决这个问题?他说项⽬⾛外包,要不然我以后还会遇到问题。。。说得好像去他那⼉报班了以后就不会遇到问题了似的。。。当时真把我整崩溃了,好在我克制了情绪,保持了理智,从他的⾔语中发现线索,最后解决了这个bug。必须得回头到那个群去嘚瑟⼀下啊,结果他还理直⽓壮的教育了我⼀番,真不知道该他尴尬,还是我尴尬。。。。这段故事的具体聊天记录我竟然截屏了,在下⼀节节结尾我会贴出来,供各位看官⼀乐。
现在先进⼊本节的学习。
⾸先,halcon上打开这个例程,ctrl+E,搜索rim即可。这个例⼦的⽬的是找到下图中的⼏个圆,并标⼀下圆的直径(单位是像素),和识别那上⾯⼏个字符,原图如下(3-6-1)。
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-1
⼤家先⾃⼰单步执⾏到第30⾏,因为前⾯这些准备⼯作和简单的预处理,都是⽼⽣常谈的了。唯⼀可以说的是第25⾏,lect_shape (DarkRegions, Circles, [‘circularity’,’area’], ‘and’, [0.85,50], [1.0,99999]),按照特征来筛选region嘛,这⼀次它同时⽤了两个特征:⾯积和圆度,意思就是圆度在0.85-1之间,⾯积在50-99999之间的region。And这个时候就起作⽤了,就是必须得同时满⾜这两个条件的region才会被筛选出来,如果是or,就是或的关系啦!那什么叫圆度(circularity)呢?就是圆的程度嘛!(不过数学上也是有公式的,详见本节TIPS 1)。
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-2
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-3
到了第30⾏,其实这⼏个圆已经⼤致的提取出来了,只是边缘部分实在是达不到⼯业的要求,如上图(3-6-3),所以还是需要⼀些处理的,如果是你,你怎么处理?此处要认真思考⼀分钟。。没有思路了吧,我们来看看halcon的思路,如下图(3-6-4):
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-4
官场礼仪
dilation_circle()和erosion_circle()这两个算⼦,印象中我在某节的TIPS⾥⾯有讲过这⼀类的算⼦,这类算⼦这种结合起来⽤,往往对提取边缘有奇效!这⼉就是⽤了这个思路:先膨胀⼀点点,再腐蚀⼀点点,然后做差(difference()),得到的就是每个圆的边缘部分的区域了。这个地⽅是个⼩技巧点,经常会⽤到的,最好记住!
接下来⼜有⼀个⼩细节了,就是合并,因为有4个圆嘛,所以difference出来的ROI区域其实是4个了,你得合并下,下⼀步才好reduce。代码如下(3-6-5):
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-5
接下来果然就是reduce_domain(),看下上⾯绿⾊的注释:reduce这个ROI(domain)到被提取的包含边缘的region。哈哈,蹩脚的翻译。。还是看图吧(3-6-6)。
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
天什么地什么
图 3-6-6
这样就很好理解了,reduce之后,整张图⽚就剩下这⼏个圆孔边缘的⼀⼩部分。这样提取边缘就没有什么⼲扰了。Halcon程序员就是机智。
接下来这个算⼦,我得隆重介绍⼀下:
edges_sub_pix (RimReduced, Edges, ‘canny’, 4, 20, 40):就是求取图⽚的亚像素边缘(sub-pixel edges),什么叫亚像素呢?(详见本节TIPS 2)。不得不承认,这个算⼦是很厉害的,⾥⾯有好多算法,⼩弟我也不是很懂,所以这个算⼦我⽤的时候,参数选择多是凭经验和慢慢试。第⼀个参数是你要求边缘的图⽚;第⼆个参数是求出来的边缘,第三个参数是求边缘的算法,有好⼏种可以选择;第四个参数叫筛选因⼦吧,越⼩图像会越被平滑,⼀些⼩细节就会被忽略掉,简⽽⾔之,你要筛选⼤的明显的边缘的时候,就把它设⼩⼀点吧!第五个第六个参数,有道的解释是:迟滞阈值的下限和上限。。。这是什么⿁,我也不晓得。但是这两个参数的意思是这样的,当某个点相对于它周围的点振幅超过第六个参数(high)的时候,直接认为该点是边缘!当某个点的振幅⼩于第五个参数(low)的时候,直接认为该点不是边缘!剩下的振幅在中间的这些呢?就判断,跟上⾯已经确定的边缘相连接的,就也判断为边缘,不连接的就直接拒绝,不当边缘。接着讲下⼀个算⼦:
lect_contours_xld (Edges, RelEdges, ‘length’, 30, 999999, 0, 0):复制这段代码的时候,情不⾃禁
看了下上⾯的英⽂注释,发现解释的真好,⽐上⾯那个翻译好多了。只选长度⼤于30个像素的边缘,这个算⼦跟lect_shape()可以说是异曲同⼯了,所以也很好理解。不过它出来的是xld,那什么是xld呢?eXtended Line Descriptions:表⽰的是亚像素级别的轮廓(详见TIPS 3)。为什么这⼉要⽤这个算⼦⽽不⽤lect_shape()呢?好奇的⼩朋友可以⾃⼰试⼀下。因为我们上⾯⼀步算⼦提取的是亚像素精度的边缘嘛,所以出来的就是xld了,那就只能⽤xld的算⼦来筛选啦。⽤了亚像素,halcon⾃⼰都很⾃信,直接给提取出来的轮廓叫了Reledges,我英⽂不好,反正我就把rel理解成real,哼!
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-7
不得不承认,的确提取的还是可以的,如上图(3-6-7),蛮准。接下来是要求它的直径了,如果是⼀个region,我最喜欢的就是
smallest_circle()来求最⼩外接圆,直接求出这个圆的直径,但是那样明显会不准,结果偏⼤。所以本例也没有⽤我那么挫的⽅法。要求直径,还是得把这些亚像素的边缘,拟合成⼀个规则图形,才能求直径,所以下⼀步⼜是⼀个(甚⾄⼀类)新算⼦:
fit_ellip_contour_xld (RelEdges, ‘ftukey’, -1, 2, 0, 200, 3, 2, Row, Column, Phi, Ra, Rb, StartPhi, E
ndPhi, PointOrder):fit 在我初中时候我⼀直感觉就是”使合⾝”的意思。这⼉也将就着这样理解吧:使这个亚像素的轮廓(contour_xld)合⾝到⼀个椭圆(ellip)上,或者说拟合,当然也可能只是椭圆的⼀条弧。成了⼀个规则图形,这个椭圆的参数就可以给我们了。这个算⼦第⼀个参数是要拟合的xld边缘,第⼆个参数是拟合的算法,貌似有很多种拟合成椭圆的算法,但是初学者实在没必要会这些,直接默认的算法即可,后⾯的参数我也喜欢直接默认,到出不来效果再F1 去看看要调整哪个参数。这算不算⼀个⼩技巧?后⾯的Row ,Column就是这个椭圆的中⼼了,Ra,Rb是椭圆的长短轴,Phi是长轴的⾓度,不是每个椭圆的长轴都是⽔平的嘛!这个很好理解。StartPhi和EndPhi是弧线的起始终⽌⾓度了。最后的PointOrder,你⾃⼰去帮助⽂档看吧!都第五节了,不能总是靠我说(其实这个算⼦前⾯⼏个我没说的参数也是可以通过⾃⼰看帮助了解的,这⼀个算⼦的那⼏个参数刚好不难理解,可以试着⾃学⼀下)。
女娲传奇得到这些参数了,下⾯当然要显⽰出来嘚瑟下,顺着代码往下看,出现了红⾊的代码:
display_ellips (Rim, Row, Column, Phi, Ra, Rb, WindowID):看字⾯意思就是显⽰椭圆了,你按F1进帮助发现也没有多少解释了,为什么呢?因为这是⼀个函数,类似于C#⾥⾯的⼀个⽅法,对的,halcon⾥⾯也可以⾃⼰写⽅法,然后调⽤的哦!右键这个函数,然后选择”在新的标签页中显⽰函数”,就可以看到这个函数的函数体了。如下图:
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-8
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-9
我们先来看看本例是怎么显⽰的(本节在TIPS 4⾥⾯再告诉⼤伙⼉怎么⾃⼰写⼀个函数),如上图(3-6-9),放眼望去也就两个新算⼦,
gen_ellip_contour_xld(Ellip,Row,Column,Phi,Ra,Rb,gen_tuple_const(|Row|,0), gen_tuple_const(|Row|,6.28318),
gen_tuple_const(|Row|,’positive’), 1.5):创建⼀个符合椭圆曲线的XLD轮廓。就是根据上⼀步求出来的椭圆那么多参数,再把这个椭圆画出来呗。⼤家不要被后⾯这么多参数吓到了,其实⼏乎所有的参数都是上⼀个算⼦fit_ellip_contour_xld ()算出来的。第⼀个就是你要画的XLD轮廓的名字了;第⼆个第三个就是椭圆的中⼼坐标;第四个就是这个椭圆长轴的⾓度;第五个第六个就是长短轴的长度了;第七个第⼋个就是这个椭圆的开始⾓度和结束⾓度了;第九个参数就是PointOrder了;第⼗个参数是相邻轮廓点之间的最⼤距离。纵观这10个参数,第⼀个是命名,第⼗个是设置下相邻轮廓点的距离,⼩于该距离的应该就合并成有⼀个弧线了。中间的⼋个参数都是上⼀个fit…算⼦给出来的,当然P
ointOrder参数两个设为⼀样的就可以了。那么这⾥为什么会出现这么多”gen_tuple_const(|Row|,0)”呢?其实⽬的只有⼀个,保持参数个数⼀样的。这个算⼦的意思是⽣成⼀个成员全为常数的数组,数组成员的个数是第⼀个参数,成员的常数值为第⼆个参数,在这⾥就是⽣成有⼀个跟Row数组个数⼀样多的值全为0的数组。因为你的第⼆个到第五个参数都是数组,每个数组有多个成员,后⾯⾓度的个数也得配套才对,这个⾓度就是画圆的起始和终⽌⾓度了嘛。画整个椭圆就是0到2π(6.28318)了。这个懂了这个后⾯的也就都好懂了。
接下来这个算⼦:
gen_arrow_contour_xld (Arrow2, Row + Ra * sin(Phi), Column – Ra * cos(Phi), Row – Ra * sin(Phi), Column + Ra * cos(Phi), 5, 5):就是画⼀个箭头了。这个严格意义上应该也不叫算⼦,属于halcon⾃⼰封装的所有例⼦⾥⾯通⽤的⼀个函数了。你右键依然可以在新的标签页中显⽰出它是怎么画的具体halcon代码。但是我是不求甚解的。直接调⽤就可以了。那么画⼀个箭头需要⼏个参数呢?看看halcon的设定:第⼀个参数就是这个箭头的名字了;第⼆个第三个参数是箭头开始点的坐标;第四个第五个参数是箭头的结束坐标;第六个第七个参数是箭头的长度跟宽度。我们再看看本例⾥⾯的参数设定,不如我换个讲解⽅式,你们跟我⼀起来看,我说个⼤致的,它⼀共画了四次箭头,说明每个圆画了四个,但是起始点竟然都不是圆⼼,你单步执⾏下就知道。其实把起始点都换成圆⼼也是可以的(本⼈亲测哦,你也不妨⼀试)。
好啦,这个函数⾥⾯剩下的就没什么好说的了,你们⾃⼰单步执⾏下就都懂啦。我们跳出来,回到主函数继续:
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-10
还是⽼规矩,先看halcon⾃⼰的绿⾊注释,如上图(3-6-10):接下来的⽬的是找到图⽚中的字符,提取⼩的灰度值较低的region。我记得以前我说过,字符的提取识别,有很多种⽅法。halcon也喜欢显摆不同的⽅法,⽐如本例:
gauss_filter (Rim, RimGauss, 11):⽤离散⾼斯函数平滑图⽚,第三个参数就是平滑过滤器的⼤⼩了,⼀般都是奇数,其实跟平滑系数⼀个意思我觉得,越⼤越平滑呗。
dyn_threshold (Rim, RimGauss, SmallAndDarkerRegion, 5, ‘dark’):使⽤局部阈值分割图像。这两个算⼦合起来,也算是⼀个⼩连招了,⽐较容易筛选出来⼀些较为尖锐的部分,也就是灰度值变化稍微⼤⼀点的地⽅。第⼀个参数就是正常图像了;第⼆个是平滑过后的图像;第三个是输出的region;第四个是阈值,灰度值变化超过阈值的被筛选出来;第五个是选择暗的地⽅还是亮的地⽅,还是亮或者暗的地⽅都要。具体的数学公式我就不列出来了,你们F1进去看吧。
submitted经过这两步之后,再稍微求下连通域,lect下shape,就可以筛选出来字符了。厉不厉害?好吧,我⽆聊了。。
接下来的步骤,它也卖了个关⼦,先没全部选中,然后⼀个⼩技巧,求了下闭运算⼜来个交集,找到了整个字符区域,这些都没有什么新意了。你们可以⾃学。我想讲的是下⾯这⼏⾏代码:
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-11
就是上图(3-6-11)中红框框内的⼏⾏代码了。这⼏⾏代码涉及到了线性代数的只是了,就算你不记得什么叫线性代数了也没太⼤关系,⼤不了不知道具体原理吧,先学会⽤呗。⼤致来说:⼀个图像要平移,旋转,缩放都可以通过图像的坐标左乘矩阵来实现。所以要实现⼀个图像的平移,旋转,缩放我们要先求出这个矩阵。求这个矩阵之前你得先⽣成⼀个,也可以叫初始化⼀个吧,或者实例化⼀个,就是红框内的第⼀步了,第⼆步就是为这个矩阵增加⼀个旋转矩阵,第三步就是⽤这个矩阵来转region,第四步就是⽤这个矩阵来转图像。下⾯我们挨个讲下每个算⼦的参数:
房产转让协议hom_mat2d_identity (HomMat2DIdentity):⽣成⼀个齐次⼆维矩阵,hom就是齐次,mat就是矩阵,2d就是2维。
hom_mat2d_rotate (HomMat2DIdentity, rad(180) – PhiChar, RowChar, ColumnChar, HomMat2DRotate):这就是在这个矩阵⾥⾯添加旋转矩阵,第⼀个参数就是初始化的矩阵,第⼆个就是要旋转的⾓度,第三个就是要围绕哪个点来旋转,第四个参数就是转完后的矩阵了。围绕哪个点来转,转多少度呢?本例中这些参数都来⾃上⼀步smallest_rectangle2()别跟我说你不记得这句算⼦的含义了!
affine_trans_region (Characters, CharRotated, HomMat2DRotate, ‘constant’):这个就是⽤上⾯的矩阵来旋转region了,第⼀个参数是要旋转的region,第⼆个是旋转后的region,第三个是旋转矩阵,第四个是是否⽤插值,有两个选
择,’constant’和’nearest_neighbor’ ,第⼀个应该是⽤常数,第⼆个应该是⽤最邻近的灰度值,(这是我的猜测,不知道对不对的)。据说⽤第⼆个会增加代码运⾏时间但是使边缘更平滑。我都不在意那些细节的。。。
affine_trans_image (Rim, RimRotated, HomMat2DRotate, ‘weighted’, ‘fal’):这个就是⽤矩阵来旋转图⽚了,前三个参数还是那意思,第四个是插值⽅法,具体的前⾯章节有介绍过,也就⼀个”weighted”没有说过,F1⾥⾯刚好有介绍,这个任务果断留给你。。第五个参数是是否需要适应图⽚⼤⼩,选fal吧,这样真实点,没什么好适应的。单步执⾏下,你会发现图⽚旋转到了字符是⽔平
的⾓度了。接下来⼀堆褐⾊的代码就是显⽰了。我也就不再赘述。如果你有兴趣,可以接着⽤OCR识别,把这⼏个字符识别出来。我之前有教过的哦!在此就不重复了,但是本节知识点还是挺多了,还是得帮你们总结⼀下,毕竟谁都懒。。。
1)edges_sub_pix (RimReduced, Edges, ‘canny’, 4, 20, 40):
班级活动有哪些2)lect_contours_xld (Edges, RelEdges, ‘length’, 30, 999999, 0, 0):
3)fit_ellip_contour_xld (RelEdges, ‘ftukey’, -1, 2, 0, 200, 3, 2, Row, Column, Phi, Ra, Rb, StartPhi, EndPhi, PointOrder):煎鱼不粘锅的妙招
4)gen_ellip_contour_xld(Ellip,Row,Column,Phi,Ra,Rb, gen_tuple_const(|Row|,0), gen_tuple_const(|Row|,6.28318),
gen_tuple_const(|Row|,’positive’), 1.5):
云天雾地
5)gen_arrow_contour_xld (Arrow2, Row + Ra * sin(Phi), Column – Ra * cos(Phi), Row – Ra * sin(Phi), Column + Ra *
cos(Phi), 5, 5):
6)gauss_filter (Rim, RimGauss, 11):
7)dyn_threshold (Rim, RimGauss, SmallAndDarkerRegion, 5, ‘dark’):
8)hom_mat2d_identity (HomMat2DIdentity):
9)hom_mat2d_rotate (HomMat2DIdentity, rad(180) – PhiChar, RowChar, ColumnChar, HomMat2DRotate):
10)affine_trans_region (Characters, CharRotated, HomMat2DRotate, ‘constant’):
11)affine_trans_image (Rim, RimRotated, HomMat2DRotate, ‘weighted’, ‘fal’):
不管怎么说,这⼀节的任务还是很重的,知识点有点多,建议⼤家过段时间回头在看⼀遍,或许有新的领悟。本节不但学了这么多新算⼦,还学了⼀些⼩套路⽐如开闭运算的合作弄出边缘区域,平滑和局部阈值的连招等。另外还有⾃⼰写⼀个⼩函数的⽅法。都是知识点啊!都得学会啊同学们!
本节TIPS:
1) 圆度的计算公式,halcon⾥⾯也有的,在帮助⾥⾯,直接搜索circularity就有了,如下图(3-6-12):
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-12
什么意思呢?F是这个region的⾯积,max是这个region⾥⾯从中⼼到边缘最远的距离(如果Region是个圆,max就是半径了),那么C’就等于F/(max maxπ),我的理解,分母就是圆的⾯积公式了,如果你是圆,你的圆度就是1,如果不是应该⼤多数情况下都⼩于1吧。为了把圆度的值域约束在[0,1]之间,halcon加了下⾯⼀⾏,C=min(1,C’)。这个C才是region的圆度。⾄于有没有⼀个图形它通过第⼀种算法会⼤于1呢?我没有考究过。。。毕竟不是真学霸呀哈哈!
2) 亚像素应该是⽐像素精度更⾼⼀级的吧,我们都知道,图像⾥⾯的⼀条线,其实是n个像素拼在⼀起的嘛,但是像素其实是有⼤⼩的,所以它的精度只能精确到他的像素级别,但是我们做算法的时候,其实可以把每个像素再切割⼀下,然后来求,这样原来⼀个像素只能表⽰⼀个值,现在⼀个像素被切割成了2个或3个,就可以表⽰2个或者3个数值了,是不是精度相对就上去了?更为科学的严谨的解释请百度。。。。
3) XLD算是halcon⾥⾯的另⼀种类型了。Halcon类型简单来说分为两种,⼀种是HTuple,⼀种是HOjbect。⼀般的数值,数组,字符,句柄等都是HTuple了,⼀般的图⽚,region,XLD就都是HObject了。XLD就是上⾯都说的,亚像素精度的轮廓啊线条啊之类的,他可以让你的图像处理的精
度更⾼⼀个级别。操作⽅法跟region其实很⼤⼀部分都是⼀样的了。但是它不是region,很多时候你要学会这两个之间的转换的算⼦,今天第⼀次接触就先不说了。等以后我详细列举⼀下,并说⼀下之间的规律。
4) 还记得我开头的时候说过,halcon⾥⾯的很多功能我会⼀点⼉⼀点⼉告诉你们的吗?如果开头就说肯定难记住,不如这样⼀点⼀点说,我觉得会⽐较好⼀点,就像全班的同学,⼀天认识⼏个,过⼏天⼜认识⼏个,慢慢就都认识了,⼀下⼦全罗列出来反⽽没精⼒去认识了还容易混淆。这⼉我们就来说说halcon的⾃⼰创建函数:
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图3-6-13
点开上图(3-6-13)的”创建新函数”选项,在弹出窗⼝中,给⾃⼰的新函数命⼀个名,然后点击参数栏,如下图(3-6-14):前两个是图标参数,⼀个输⼊⼀个输出,后两个是控制参数,⼀个输⼊⼀个输出。这个我开头就讲过的了,你的函数⾥⾯需要哪些参数,在这⼉命名好就可以了,如果你某种类型的参数有两个或多个,点击加号按钮,下⾯对⾃动⽣成多⼀个。添加完后点击应⽤,程序窗⼝就会⾃⼰进⼊你的这个函数的⾥⾯,你在⾥⾯编写你要实现的功能就可以了。这个栏⾥还有⼀般⽂档,可以给⽂档加⼀些注释啊之类的,你们就⾃⼰看看吧。
机器视觉之halcon⼊门(11)-⼀⽂弄懂halcon例程:rim.hdev
图 3-6-14
写完之后,如果还想修改函数,可以”函数”->”编辑函数”,然后最下⾯看到你函数名,点进去就可以编辑了。有没有想动⼿跃跃欲试的感觉,哈哈不要克制,just do it!