Halcon编程-基于形状特征的模板匹配

2024-09-06 12:58

本文主要是介绍Halcon编程-基于形状特征的模板匹配,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  halcon软件最高效的一个方面在于模板匹配,号称可以快速进行柔性模板匹配,能够非常方便的用于缺陷检测、目标定位。下面以一个简单的例子说明基于形状特征的模板匹配。

    

为了在右图中,定位图中的三个带旋转箭头的圆圈。注意存在,位置、旋转和尺度变化。

上halcon程序

复制代码

 1 * This example program shows how to find scaled and rotated shape models.2 dev_update_pc ('off')3 dev_update_window ('off')4 dev_update_var ('off')5 read_image (Image, 'green-dot')6 get_image_size (Image, Width, Height) 获取了图像大小7 dev_close_window ()8 dev_open_window (0, 0, Width, Height, 'black', WindowHandle)9 dev_set_color ('red')
10 dev_display (Image)
11 threshold (Image, Region, 0, 128) 对图像进行二值化
12 connection (Region, ConnectedRegions)  区域生长得到连通域
13 select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 10000, 20000) 通过面积进行筛选,得到里面的圆
14 fill_up (SelectedRegions, RegionFillUp)   对圆进行填充
15 dilation_circle (RegionFillUp, RegionDilation, 5.5)  对填充区域进行膨胀
16 reduce_domain (Image, RegionDilation, ImageReduced) ROI操作得到imagereduced
17 create_scaled_shape_model (ImageReduced, 5, rad(-45), rad(90), 'auto', 0.8, 1.0, 'auto', 'none', 'ignore_global_polarity', 40, 10, ModelID)
//基于区域创建匹配模型,得到模型的ID modelID
18 get_shape_model_contours (Model, ModelID, 1) 基于模型ID 得到模型的轮廓 model
19 area_center (RegionFillUp, Area, RowRef, ColumnRef) 获得里面圆中心位置,相对于全图来说
20 vector_angle_to_rigid (0, 0, 0, RowRef, ColumnRef, 0, HomMat2D) //vector_angle_to_rigid只需要一个点对及一个角度对即可计算刚性变换矩阵,所以可利用find_shape_model的结果
//HomMat2D 通过顶点得到其变换矩阵 21 affine_trans_contour_xld (Model, ModelTrans, HomMat2D) //对XLD轮廓(contour)进行一个任意二维仿射变换。 将model轮廓转换为相对于全图的,得到ModelTrans
22 dev_display (Image)
23 dev_display (ModelTrans) //在全局中显示模型区域 ,注意modeltrans 为xld 轮廓!!!24 read_image (ImageSearch, 'green-dots')
25 dev_display (ImageSearch)
//将之前模型ID 作为模板,
26 find_scaled_shape_model (ImageSearch, ModelID, rad(-45), rad(90), 0.8, 1.0, 0.5, 0, 0.5, 'least_squares', 5, 0.8, Row, Column, Angle, Scale, Score)
//得到图像中模板的点 位置 和 角度 尺度 和匹配值
27 for I := 0 to |Score|-1 by 1
28     hom_mat2d_identity (HomMat2DIdentity)
29     hom_mat2d_translate (HomMat2DIdentity, Row[I], Column[I], HomMat2DTranslate)
30     hom_mat2d_rotate (HomMat2DTranslate, Angle[I], Row[I], Column[I], HomMat2DRotate)
31     hom_mat2d_scale (HomMat2DRotate, Scale[I], Scale[I], Row[I], Column[I], HomMat2DScale)
2*3 刚性变换矩阵32     affine_trans_contour_xld (Model, ModelTrans, HomMat2DScale)  把model 转换为modeltrans 33     gen_region_contour_xld(ModelTrans,Region2,'filled')      填充区域
34     intensity ( Region2, ImageSearch, Mean, Deviation )      获得区域的灰度平均值和方差值
35     dev_display (ModelTrans)
36 endfor
37 clear_shape_model (ModelID)

复制代码

 

MFC程序:

 1 定义图像显示

复制代码

 1 HTuple HalHwndView1;2 3   void DispImage( const Hobject& hoImage);4 5 HalHwndView1=NULL;6 7 void CSpayTestDlg::DispImage(const Hobject& hoImage)8 {9 HTuple tmpWidth;
10 HTuple tmpHeight;
11 
12 get_image_size(hoImage,&tmpWidth,&tmpHeight);
13 Halcon::set_part(HalHwndView1, 0, 0, tmpHeight - 1, tmpWidth - 1);// 
14 Halcon::clear_window(HalHwndView1);
15 Halcon::disp_obj(hoImage, HalHwndView1);
16 
17 }
18 
19 CRect rect;
20 GetDlgItem(IDC_SHOW_WINDOW)->GetClientRect(rect);
21 HWND HwndView1=GetDlgItem(IDC_SHOW_WINDOW)->GetSafeHwnd();
22 Halcon::open_window(0,0,rect.Width(),rect.Height(),Hlong(HwndView1),"visible","",&HalHwndView1);

复制代码

2 提取模板

复制代码

Hobject  Image;
HTuple  Width, Height, WindowHandle, ModelID;
Hobject  Region, ConnectedRegions, SelectedRegions;
Hobject  RegionFillUp, RegionDilation, ImageReduced, Model;
HTuple  Angle, Scale, Score, I, HomMat2DIdentity, HomMat2DTranslate;
HTuple  HomMat2DRotate, HomMat2DScale;HTuple  Area, RowRef, ColumnRef, HomMat2D, Row, Column;Hobject  ModelTrans;
void CSpayTestDlg::OnBnClickedButtonDetection()
{// TODO: 在此添加控件通知处理程序代码Image=IplImageToHImage(m_workImg);DispImage(Image);Halcon::set_color(HalHwndView1,"red");Halcon::set_draw(HalHwndView1,"margin");get_image_size(Image, &Width, &Height);通过鼠标选择区域//HTuple  Row1,Column1,Row2,Column2,h_Area,h_Row,h_Column;//draw_rectangle1(HalHwndView1,&Row1,&Column1,&Row2,&Column2);//gen_rectangle1(&RegionDilation,Row1,Column1,Row2,Column2);//area_center(RegionDilation,&h_Area,&h_Row,&h_Column);//reduce_domain(Image,RegionDilation,&ImageReduced);threshold(Image, &Region, 0, 128);connection(Region, &ConnectedRegions);select_shape(ConnectedRegions, &SelectedRegions, "area", "and", 10000, 20000);fill_up(SelectedRegions, &RegionFillUp);dilation_circle(RegionFillUp, &RegionDilation, 5.5);reduce_domain(Image, RegionDilation, &ImageReduced);//roi 区域 ImageReducedcreate_scaled_shape_model(ImageReduced, 5, HTuple(-45).Rad(), HTuple(360).Rad(), "auto", 0.8, 1.0, "auto", "none", "ignore_global_polarity", 40, 10, &ModelID);//模型是个数据 ModelIDget_shape_model_contours(&Model, ModelID, 1);//获取模型的轮廓 Model中Halcon::write_shape_model(ModelID,"Model.shm");area_center(RegionFillUp, &Area, &RowRef, &ColumnRef);vector_angle_to_rigid(0, 0, 0, RowRef, ColumnRef, 0, &HomMat2D);affine_trans_contour_xld(Model, &ModelTrans, HomMat2D);//roi 转换到原图的过程 基于2d仿射 将 model模型 轮廓 变化到trans上Halcon::disp_obj(ModelTrans,HalHwndView1);}

复制代码

3 匹配模板

复制代码

 1     Image=IplImageToHImage(m_workImg);2     DispImage(Image);3 4     Halcon::set_color(HalHwndView1,"green");5 6     HTuple  ModelIDadd;7      Hobject  ModelTransadd;8     Halcon::read_shape_model( "Model.shm",&ModelIDadd);9     find_scaled_shape_model(Image, ModelIDadd, HTuple(-45).Rad(), HTuple(360).Rad(), 
10         0.8, 1.0, 0.5, 0, 0.5, "least_squares", 5, 0.8, &Row, &Column, &Angle, &Scale, 
11         &Score);
12     Hobject  Region2;
13     HTuple  Mean, Deviation;
14     for (I=0; I<=(Score.Num())-1; I+=1)
15     {
16         hom_mat2d_identity(&HomMat2DIdentity);
17         hom_mat2d_translate(HomMat2DIdentity, HTuple(Row[I]), HTuple(Column[I]), &HomMat2DTranslate);
18         hom_mat2d_rotate(HomMat2DTranslate, HTuple(Angle[I]), HTuple(Row[I]), HTuple(Column[I]), 
19             &HomMat2DRotate);
20         hom_mat2d_scale(HomMat2DRotate, HTuple(Scale[I]), HTuple(Scale[I]), HTuple(Row[I]), 
21             HTuple(Column[I]), &HomMat2DScale);
22         affine_trans_contour_xld(Model, &ModelTransadd, HomMat2DScale);
23         
24         gen_region_contour_xld(ModelTrans,&Region2,"filled");
25         intensity ( Region2, Image, &Mean, &Deviation );
26         
27         double mean=Mean[0].D();
28         double deviation=Deviation[0].D();
29         disp_obj(ModelTransadd,HalHwndView1);
30     }
31     clear_shape_model(ModelIDadd);

复制代码

匹配结果

 

 create_scaled_shape_model(ImageReduced, 5, HTuple(-45).Rad(), HTuple(360).Rad(), "auto", 0.8, 1.0, "auto", "none", "ignore_global_polarity", 40, 10, &ModelID);

* 01、Template,//reduce_domain后的模板图像   ImageReduced


* 02、NumLevels,//金字塔的层数,可设为“auto”或0—10的整数  5 

* 03、AngleStart,//模板旋转的起始角度     HTuple(-45).Rad()

* 04、AngleExtent,//模板旋转角度范围, >=0     HTuple(360).Rad()

* 05、AngleStep,//旋转角度的步长, >=0 and <=pi/16   auto
* 06、ScaleMin,//模板最小比例 0.8

* 07、ScaleMax,//模板最大比例   1.0
* 08、ScaleStep,//模板比例的步长  auto

* 09、Optimization,//设置模板优化和模板创建方法  none

 * 10、Metric, //匹配方法设置  ignore_global_polarity

* 11、Contrast,//设置对比度  40
* 12、MinContrast,//设置最小对比度 10

* 13、ModelID,//输出模板句柄  ModelID

1. NumLevels越大,找到匹配使用的时间就越小。另外必须保证最高层的图像具有足够的信息(至少四个点)。可以通过inspect_shape_model函数查看设置的结果。如果最高层金字塔的消息太少,算法内部会自动减少金字塔层数,如果最底层金字塔的信息太少,函数就会报错。如果设为auto,算法会自动计算金字塔的层数,我们可以通过get_shape_model_params函数查看金字塔的层数。如果金字塔的层数太大,模板不容易识别出来,这是需要将find_shape_model函数中MinScore和Greediness参数设置的低一些。如果金字塔层数太少找到模板的时间会增加。可以先使用inspect_shape_model函数的输出结果来选择一个较好的金字塔层数。 

2. 参数AngleStart、AngleExtent定义了模板可能发生旋转的范围。注意模板在find_shape_model函数中只能找到这个范围内的匹配。参数AngleStep定义了旋转角度范围内的步长。如果在find_shape_model函数中没有指定亚像素精度,这个参数指定的精度是可以实现find_shape_mode函数中的角度的。参数AngleStep的选择是基于目标的大小的,如果模板图像太小不能产生许多不同离散角度的图像,因此对于较小的模板图像AngleStep应该设置的比较大。如果AngleExtent不是AngleStep的整数倍,将会相应的修改AngleStep。 

如果选择 complete pregeneration,不同角度的模板图像将会产生并保存在内存中。用来存储模板的内存与旋转角度的数目和模板图像的的点数是成正比的。因此,如果AngleStep太小或是AngleExtent太大, 将会出现该模型不再适合(虚拟)内存的情况。在任何情况下,模型是完全适合主存储器的,因为这避免了操作系统的内存分页,使得寻找匹配模板的时间变短。由于find_shape_model函数中的角度可以使用亚像素精度,一个直径小于200像素的模板可以选择AngleStep>= 1。 

如果选择AngleStep='auto' (or 0 向后兼容),create_shape_model将会基于模板的大小自动定义一个合适的角度步长. 自动计算出来的AngleStep可以使用get_shape_model_params函数查看。 
如果没有选择complete pregeneration, 该模型会在每一层金字塔上建立在一个参考的位置。这样在find_shape_model函数运行时,该模型必须转化为不同的角度和尺度在运行时在。正因为如此,匹配该模型可能需要更多的时间。 

3. 对于特别大的模板图像,将参数Optimization设置为不同于'none'的其他数值是非常有用的。如果Optimization= 'none', 所有的模型点将要存储。在其他情况下, 按照Optimization的数值会将模型的点数减少. 如果模型点数变少了,必须在find_shape_model函数中将参数Greediness设为一个比较小的值, 比如:0.7、0.8。对于比较小的模型, 减少模型点数并不能提高搜索速度,因为这种情况下通常显着更多的潜在情况的模型必须进行检查。如果Optimization设置为'auto', create_shape_model自动确定模型的点数。Optimization的第二个值定义了模型是否进行预处理(pregenerated completely),是通过选择'pregeneration'或者'no_pregeneration'来设置的。如果不使用第二个值(例如:仅仅设置了第一个值), 默认的是系统中的设置,是通过set_system('pregenerate _shape_models',...)来设置的,对于默认值是 ('pregenerate_shape_models' = 'false'), 模型没有进行预处理. 模型的预处理设置通常会导致比较低的运行时间,因为模型不需要在运行时间时转换。然而在这种情况下,内存的要求和创建模板所需要的时间是比较高的。还应该指出,不能指望这两个模式返回完全相同的结果,因为在运行时变换一定会导致变换模型和预处理变换模型之间不同的内部数据。比如,如果模型没有 completely pregenerated,在find_shape_model函数中通常返回一个较低的scores,这可能需要将MinScore设置成一个较低的值。此外,在两个模型中插值法获得的位置可能略有不同。如果希望是最高精确度,应该使用最小二乘调整得到模型位置。 

4. 参数Contras决定着模型点的对比度。对比度是用来测量目标与背景之间和目标不同部分之间局部的灰度值差异。Contrast的选择应该确保模板中的主要特征用于模型中。Contrast也可以是两个数值,这时模板使用近似edges_image函数中滞后阈值的算法进行分割。这里第一个数值是比较低的阈值,第二个数值是比较高的阈值。Contrast也可以包含第三个,这个数值是在基于组件尺寸选择重要模型组件时所设置的阈值,比如,比指定的最小尺寸的点数还少的组件将被抑制。这个最小尺寸的阈值会在每相邻的金字塔层之间除以2。如果一个小的模型组件被抑制,但是不使用滞后阈值,然而在Contrast中必须指定三个数值,在这种情况下前两个数值设置成相同的数值。这个参数的设置可以在inspect_shape_model函数中查看效果。如果Contrast设置为'auto',create_shape_model将会自动确定三个上面描述的数值。或者仅仅自动设置对比度('auto_contrast'),滞后阈值('auto_contrast_hyst')或是最小尺寸('auto_min_size')中一个。其他没有自动设置的数值可以按照上面的格式再进行设置。可以允许各种组合,例如:如果设置 ['auto_contrast','auto_min_size'],对比度和最小尺寸自动确定;如果设置 ['auto_min_size',20,30],最小尺寸会自动设定,而滞后阈值被设为20和30。有时候可能对比度阈值自动设置的结果是不满意的,例如,由于一些具体应用的原因当某一个模型组件是被包含或是被抑制时,或是目标包含几种不同的对比度时,手动设置这些参数效果会更好。因此对比度阈值可以使用determine_shape_model_params函数自动确定,也可以在调用create_shape_model之前使用inspect_shape_mode函数检查效果。 

5. 参数Metric定义了在图像中匹配模板的条件。如果Metric= 'use_polarity',图像中的目标必须和模型具有一样的对比度。例如,如果模型是一个亮的目标在一个暗的背景上,那么仅仅那些比背景亮的目标可以找到。如果Metric= 'ignore_global_polarity',在两者对比度完全相反时也能找到目标。在上面的例子中,如果目标是比背景暗的也能将目标找到。find_shape_model函数的运行时间在这种情况下将会略微增加。如果Metric= ignore_local_polarity', 即使局部对比度改变也能找到模型。例如,当目标包含一部分中等灰度,并且其中部分比较亮部分比较暗时,这种模式是非常有用的。由于这种模式下find_shape_model函数的运行时间显著增加,最好的方法是使用create_shape_model创建几个反映目标可能的对比度变化的模型,同时使用find_shape_models去匹配他们。上面三个metrics仅仅适用于单通道图像。如果是多通道图像作为模板图像或搜索图像,仅仅第一个通道被使用。如果Metric='ignore_color_polarity', 即使颜色对比度局部变化也能找到模型。例如,当目标的部分区域颜色发生变化(e.g.从红到绿)的情况。如果不能提前知道目标在哪一个通道是可见的这种模式是非常有用的。在这种情况下find_shape_model函数的运行时间也会急剧增。'ignore_color_polarity'可以使用于具有任意通道数目的图像中。如果使用于单通道图像,他的效果和'ignore_loc al_polarity'是完全相同的。 

6. create_shape_model创建的模板通道数目和find_shape_model中的图像通道数目可以是不同的。例如,可以使用综合生成的单通道图像创建模型。另外,这些通道不需要是经过光谱细分(像RGB图像)的。这些通道还可以包括具有在不同方向照亮同一个目标所获得的图像。 

7. 模型图像Template的domain区域的重心是模板的初始位置,可以在set_shape_model_origin函数中设置不同的初始位置。 

find_scaled_shape_model(Image, ModelIDadd, HTuple(-45).Rad(), HTuple(360).Rad(), 0.8, 1.0, 0.5, 0, 0.5, "least_squares", 5, 0.8, &Row, &Column, &Angle, &Scale, &Score);

find_scaled_shape_model(Image : : image

ModelID, modeldadd

AngleStart,  -45

AngleExtent,  360

ScaleMin, 0.8

ScaleMax, 1.0

MinScore,  0.5

NumMatches,  0

MaxOverlap,  0.5

SubPixel,  least_squres

NumLevels, 5

Greediness 0.8

: Row,

Column,

Angle,

Scale,

Score)

 

//roi 区域 ImageReduced
create_scaled_shape_model(ImageReduced, 5, HTuple(-45).Rad(), HTuple(360).Rad(), "auto", 0.8, 1.0, "auto", "none", "ignore_global_polarity", 40, 10, &ModelID);//模型是个数据 ModelID
get_shape_model_contours(&Model, ModelID, 1);//获取模型的轮廓 Model中注意直接提取的MODEL轮廓原点在0,0
area_center(RegionFillUp, &Area, &RowRef, &ColumnRef);
vector_angle_to_rigid(0, 0, 0, RowRef, ColumnRef, 0, &HomMat2D);
affine_trans_contour_xld(Model, &ModelTrans, HomMat2D);//roi 转换到原图的过程 基于2d仿射 将 model模型 轮廓 变化到trans上
为了显示,需要将模板移动到相应位置,所以构造2*3矩阵,将MODEL轮廓原点转换到图像中的点!!!
[1 0 2720 1 283]同理,提取后的结果很明显,是一个2*3的矩阵,与标准模板进行运算后,就能得到它的位置。
360 291 为平移的位置
[1 0 360
 
 0 1 291]
-0.653815  角度[0.7937 0.60836 360
 -0.60836 0.7937 291]
0.937 尺度 ,最终的结果!!![0.744527 0.57548 360
 -0.57548 0.744527 291]
也就是说halcon提取的模板是一个原点的模板。通过平移 角度。缩放 到合适的位置,就是这样!

这篇关于Halcon编程-基于形状特征的模板匹配的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1142070

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

uva 1342 欧拉定理(计算几何模板)

题意: 给几个点,把这几个点用直线连起来,求这些直线把平面分成了几个。 解析: 欧拉定理: 顶点数 + 面数 - 边数= 2。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

uva 11178 计算集合模板题

题意: 求三角形行三个角三等分点射线交出的内三角形坐标。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

hdu 3065 AC自动机 匹配串编号以及出现次数

题意: 仍旧是天朝语题。 Input 第一行,一个整数N(1<=N<=1000),表示病毒特征码的个数。 接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。 在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。