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 區域 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中 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,
//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 272
0 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提取的模板是一個原點的模板。通過平移 角度。縮放 到合適的位置,就是這樣!