determine_rotation_almost_rotationally_symmetric_objects
确定_旋转_几乎_旋转_对称_对象
*此示例显示如何结合使用基于形状的匹配和极坐标变换来稳健地确定几乎旋转对称的对象的方向。
*旋转对称导致问题,即无法通过简单的基于形状的匹配可靠地确定对象的方向。
*相反,基于形状的匹配对于任何旋转都会返回高分,因为重要边缘(非旋转对称)的影响太小,并且由于噪声,透视图,照明,相机变形等。
*
*我们通过两步法解决了这个问题:
* 1.大致确定对象位置
*(但方向任意):
*在此示例中,这是基于形状的匹配完成的,因为它是最通用的方法,同时说明了匹配几乎旋转对称的对象时的困难。
* 2.确定方向:
*这是通过将对象转换为极坐标并匹配定义对象方向的非对称部分来完成的。
*请注意,此步骤至关重要,可以高精度确定旋转对称中心。
*在此示例中,这是通过非常准确地为步骤1选择模型区域来完成的。
*
*初始化参数Initialize parameters
*
*互动式Interactive:
*如果将此参数设置为1(true),将要求用户用鼠标交互选择步骤2(极坐标中的匹配)的模型边缘。
*请注意,结果的质量很大程度上取决于用户定义的模型区域。
Interactive := 0
* Tolerance: Half height of the search region in the
* polar transformed image for step 2
*公差:步骤2的极坐标变换图像中搜索区域的一半高度
Tolerance := 2.5
* MinScoreMain: MinScore for shape-based matching in step 1
MinScoreMain := 0.7
* MinScorePolar: MinScore for shape-based matching in step 2
MinScorePolar := 0.4
* ContrastMain: Contrast for shape-based matching in step 1
ContrastMain := 30
* ContrastPolar: Contrast for shape-based matching in step 2
ContrastPolar := 20
* Matching parameters for both steps
Greediness := 0.5
MinContrast := 10
*
* **********************************************************
* Generate model region for shape-based matching in step 1
* **********************************************************
*
* Read model image
dev_update_off ()
read_image (Image, 'plastic_parts/cistern_valve_diaphragms_01')
*
* Estimate center of rotation symmetry.估计旋转中心的对称性。
* It is important, that the center of the model
* is equal to the rotation center of the object.*重要的是,模型的中心必须等于对象的旋转中心。
threshold (Image, Region, 128, 255)//使用阈值分割图像,得到灰度值在最小到最大之间的那些像素区域
boundary (Region, RegionBorder, 'inner')//参数为inner时 ,提取区域的内边界,边界的宽是一个像素
dilation_circle (RegionBorder, RegionDilation, 3.5)//膨胀操作,将变换得到的RegionBorder区域扩大3.5边缘,并倒圆角
reduce_domain (Image, RegionDilation, ImageReduced)//Image是输入图像;RegionDilation是输入区域;ImageReduced输出对象,为相交的图像区域,缩小图像的亚像素轮廓
edges_sub_pix (ImageReduced, Edges, 'canny', 1, 20, 40)//edges_sub_pix亚像素边缘提取,canny算子边缘检测,alpha=1参数指定值越小越平滑,低阈值low=20,高阈值high=40
fit_circle_contour_xld (Edges, 'geohuber', -1, 0, 0, 3, 2, RowFittedCircle, ColumnFittedCircle, Radius, StartPhi, EndPhi, PointOrder)//用于圆拟合XLD轮廓,输出圆心Row、Column、Radius
*
* Generate model region and create model 生成模型区域并创建模型
RegionRadius := Radius + 10
Clip := RegionRadius + 50
gen_circle (ModelRegion, RowFittedCircle, ColumnFittedCircle, RegionRadius)//生成一个或多个由中心和半径描述的圆
reduce_domain (Image, ModelRegion, ModelImage)//输出图像的相交区域
create_shape_model (ModelImage, 'auto', 0, rad(360), 'auto', 'auto', 'use_polarity', ContrastMain, MinContrast, ModelID)//生成模型ModleID
* Correct the origin of the shape model, such that
* the model center is equal to the center of the
* fitted circle which is more accurate than the
* center of the circle region.*校正形状模型的原点,以使模型中心等于拟合圆的中心,比圆区域的中心更精确。
area_center (ModelRegion, AreaModelRegion, RowModelRegion, ColumnModelRegion)//获取区域的中心行列坐标,区域面积
set_shape_model_origin (ModelID, RowFittedCircle - RowModelRegion, ColumnFittedCircle - ColumnModelRegion)//设置模板的原始坐标(不调用该算子默认为0,0;)
find_shape_model (Image, ModelID, -0.39, 0.78, 0.5, 1, 0.5, 'least_squares', 0, 0.9, R, C, A, S)//查找图像Image与模板ModelID匹配的图像部分
* **********************************************************
*
* Init main window and display intro screen 初始化主窗口并显示简介屏幕
*
dev_update_off ()
dev_close_window ()
dev_open_window_fit_image (Image, 0, 0, -1, 500, WindowHandle)
get_window_extents (WindowHandle, WindowRow, WindowColumn, WindowWidth, WindowHeight)
get_image_size (Image, ImageWidth, ImageHeight)
Aspect := real(ImageWidth) / ImageHeight//相当于double,占用8个字节。
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
*
* Display main model and intro text*显示主要模型和介绍文字
gen_cross_contour_xld (ModelCenter, RowFittedCircle, ColumnFittedCircle, 12, 0.0)//生成十字形
get_shape_model_contours (ModelContours, ModelID, 1)//找到模板的轮廓
hom_mat2d_identity (HomMat2DIdentity)//创建一个初始化矩阵
hom_mat2d_translate (HomMat2DIdentity, RowFittedCircle, ColumnFittedCircle, HomMat2DTranslate)//生成平移仿射变换矩阵
affine_trans_contour_xld (ModelContours, ModelAffineTrans, HomMat2DTranslate)//执行仿射变换
display_model (Image, ModelRegion, ModelCenter, ModelAffineTrans)//显示放射变换后图像
display_intro_text (WindowHandle)//显示介绍文字
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
*
* Select asymmetric edges for step 2 *为步骤2选择非对称边
*
AsymModelReady := 0
while (not AsymModelReady)
* Zoom into object for better visualization *放大对象以获得更好的可视化
dev_set_part (RowFittedCircle - Clip, ColumnFittedCircle - Clip * Aspect, RowFittedCircle + Clip, ColumnFittedCircle + Clip * Aspect)
* Display main model*显示主要模型
display_model (Image, ModelRegion, ModelCenter, ModelAffineTrans)
*
* Generate model region for step 2 *生成步骤2的模型区域
* (Interactively or automatically) *(交互式或自动)
dev_set_color ('blue')//设置显示颜色
if (Interactive)
* Let the user choose the asymmetric edges interactively *让用户交互选择不对称边
show_instructions (Image, ModelContours, WindowWidth, RowFittedCircle, ColumnFittedCircle, Clip, WindowHandlePolar)
draw_refinement_region (AsymEdgesRegion, WindowHandle)
display_model (Image, ModelRegion, ModelCenter, ModelAffineTrans)
display_marked_edges (Image, AsymEdgesRegion, ContrastPolar)
confirm_model (WindowHandle, RowFittedCircle, ColumnFittedCircle, Clip, AsymModelReady)
dev_set_window (WindowHandlePolar)
dev_close_window ()
else
* Use pre-defined model
gen_rectangle1 (AsymEdgesRegion, 315, 694, 343, 711)
AsymModelReady := 1
*
* Display selected region for step 2
*
display_model (Image, ModelRegion, ModelCenter, ModelAffineTrans)
display_marked_edges (Image, AsymEdgesRegion, ContrastPolar)
display_info_text (WindowHandle)
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
endif
endwhile
*
*
* **********************************************************
* Generate shape model in polar transformed image 在极坐标变换的图像中生成形状模型
* **********************************************************
*
* Calculate parameters for polar transform
*
distance_pr (AsymEdgesRegion, RowFittedCircle, ColumnFittedCircle, RadiusInner, RadiusOuter)//获得点到区域最大最小距离
area_center (AsymEdgesRegion, Area, RowAsymEdgesRegion, ColumnAsymEdgesRegion)
angle_lx (RowFittedCircle, ColumnFittedCircle, RowAsymEdgesRegion, ColumnAsymEdgesRegion, RefAngle)//获得直线和X轴的夹角
PolarWidth := round(RadiusOuter * rad(360))
PolarHeight := round(RadiusOuter - RadiusInner)
* Estimate width of model region after polar transform
polar_trans_region (AsymEdgesRegion, PolarTransAsymEdgesRegion, RowFittedCircle, ColumnFittedCircle, RefAngle + rad(180), RefAngle + rad(540), RadiusInner, RadiusOuter, PolarWidth, PolarHeight, 'bilinear')//对区域极坐标转换
smallest_rectangle1 (PolarTransAsymEdgesRegion, Row1, Column1, Row2, Column2)//最小外接矩形
RegionWidth := Column2 - Column1 + 1
* Extend the polar transform by the width of the transformed model region
* to make sure, that the asymmetric part is completely visible even
* if it is near the border of the transformed image.
AngleOverlap := RegionWidth * rad(360) / PolarWidth
PolarStartAngle := RefAngle - AngleOverlap / 2
PolarEndAngle := RefAngle + rad(360) + AngleOverlap / 2
PolarAngleRangeExtended := PolarEndAngle - PolarStartAngle
PolarWidthExtended := round(RadiusOuter * PolarAngleRangeExtended)
*
* Perform polar transform for model image and model region
polar_trans_image_ext (Image, PolarTransModelImage, RowFittedCircle, ColumnFittedCircle, PolarStartAngle, PolarEndAngle, RadiusInner, RadiusOuter, PolarWidthExtended, PolarHeight, 'bilinear')//对图像极坐标转换
polar_trans_region (AsymEdgesRegion, PolarTransModelRegion, RowFittedCircle, ColumnFittedCircle, PolarStartAngle, PolarStartAngle + AngleOverlap, RadiusInner, RadiusOuter, RegionWidth, PolarHeight, 'bilinear')//对区域极坐标转换
*
* Create shape model for the asymmetric part in polar transformed image
shape_trans (PolarTransModelRegion, RegionTrans, 'convex')//对区域形状标转换
reduce_domain (PolarTransModelImage, RegionTrans, PolarModelImage)
create_shape_model (PolarModelImage, 1, 0, 0, 'auto', 'auto', 'use_polarity', ContrastPolar, MinContrast, ModelIDPolar)
get_shape_model_contours (PolarModelContours, ModelIDPolar, 1)//获得模板轮廓
* Determine reference position
find_shape_model (PolarModelImage, ModelIDPolar, 0, 0, MinScorePolar, 1, 0.0, 'least_squares', 0, Greediness, RowRefPolar, ColumnRefPolar, AngleRefPolar, ScoreRefPolar)//查找模板
*
* **********************************************************
* Main loop
* **********************************************************
*
* Init display
dev_set_part (0, 0, ImageHeight - 1, ImageWidth - 1)
dev_set_line_width (1)
dev_open_window (0, WindowWidth + 12, 1, 1, 'black', WindowHandlePolar)
set_display_font (WindowHandlePolar, 14, 'mono', 'true', 'false')
*
NumImages := 16
for Index := 1 to NumImages by 1
read_image (Image, 'plastic_parts//cistern_valve_diaphragms_' + Index$'02')
* ****************************************
* Step 1 : First find main model roughly
* ****************************************
find_shape_model (Image, ModelID, 0, rad(360), MinScoreMain, 0, 0.0, 'least_squares', 0, Greediness, Row, Column, Angle, Score)
NumMatches := |Row|
if (NumMatches > 0)
* **************************************************
* Step 2: Refine search in polar transformed image
* **************************************************
* Init result variables
gen_empty_obj (PolarResultImages)
gen_empty_obj (RefinedContours)
FinalAngle := []
RowsPolar := []
ColumnsPolar := []
AnglesPolar := []
RefinementFailed := []
for J := 0 to NumMatches - 1 by 1
* **************************************************
* Refinement in polar transformed image
* ***************************************************
* Use more than 360° to make sure, that objects at the border are
* also completely visible.
polar_trans_image_ext (Image, PolarTransImage, Row[J], Column[J], PolarStartAngle, PolarEndAngle, RadiusInner, RadiusOuter, PolarWidthExtended, PolarHeight, 'bilinear')
* Only search along the reference row
rectangle1_domain (PolarTransImage, SearchImageReduced, RowRefPolar - Tolerance, 0, RowRefPolar + Tolerance, PolarWidthExtended - 1)
find_shape_model (SearchImageReduced, ModelIDPolar, 0, 0, MinScorePolar, 1, 0.0, 'least_squares', 0, Greediness, RowPolar, ColumnPolar, AnglePolar, ScorePolar)
* Evaluate results
if (|ColumnPolar| > 0)
* Calculate rotation angle from matching position
* in polar transformed image.
* (The Column represents the angle)
*
FinalAngle[J] := (ColumnPolar - ColumnRefPolar) / (PolarWidthExtended - 1) * PolarAngleRangeExtended
*
* Prepare visualization of polar transform
RowsPolar[J] := RowPolar + J * PolarHeight
ColumnsPolar[J] := ColumnPolar
AnglesPolar[J] := AnglePolar
* Transform polar matching result back to original image
vector_angle_to_rigid (0, 0, 0, RowPolar, ColumnPolar, AnglePolar, HomMat2D)
affine_trans_contour_xld (PolarModelContours, ContoursAffineTrans, HomMat2D)
polar_trans_contour_xld_inv (ContoursAffineTrans, XYTransContour, Row[J], Column[J], PolarStartAngle, PolarEndAngle, RadiusInner, RadiusOuter, PolarWidthExtended, PolarHeight, ImageWidth, ImageHeight)
concat_obj (RefinedContours, XYTransContour, RefinedContours)
*
else
* No match in polar image, keep first result
FinalAngle[J] := Angle[J]
*
RowsPolar[J] := RowRefPolar + J * PolarHeight
ColumnsPolar[J] := -999
AnglesPolar[J] := 0
RefinementFailed := [RefinementFailed,J + 1]
endif
* Build a stack of polar transformed images for visualization
concat_obj (PolarResultImages, PolarTransImage, PolarResultImages)
endfor
Title := 'Image ' + Index + '/' + NumImages
display_results (Image, PolarResultImages, RefinedContours, Row, Column, Angle, RowsPolar, ColumnsPolar, AnglesPolar, FinalAngle, ColumnRefPolar, RowRefPolar, PolarWidthExtended, PolarHeight, PolarAngleRangeExtended, RefinementFailed, ModelID, ModelIDPolar, Title, WindowHandle, WindowHandlePolar)
else
disp_message (WindowHandle, 'No match', 'window', 12, 12, 'black', 'true')
endif
if (Index < NumImages)
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
endif
endfor
参考博客:
Halcon图像变量 - Z大山 - 博客园 https://www.cnblogs.com/zhengzc/p/11340371.html
Halcon二维仿射变换实例探究 - xh6300 - 博客园 https://www.cnblogs.com/xh6300/p/7442164.html