halcon的仿射变换总结
一、halcon 的坐标系
像素是离散的,为了解决它们,我们有一个仅使用整数值的坐标系,即像素坐标系。但是为了获得超出像素网格的更高精度,我们需要浮点坐标,这导致亚像素精确坐标系。在 HALCON 中,我们有三种不同的亚像素坐标系实现:
像素中心坐标 (Pixel Centered Coordinates),HALCON 标准子像素坐标系
边中心坐标 (Edge Centered Coordinates)
1)像素精确坐标系
像素坐标系将图像视为离散元素的网格,即像素,我们将原点放在左上角像素的中间(0,0);
图像大小: h e i g h t × w i d t h = m × n
行坐标值从0到m − 1 ,列坐标值从0到n − 1。如下图(1)所示:
(1)
2)亚像素精确坐标系(边缘中心坐标):以像素为中心
该坐标系的原点也位于左上角图像像素的中心(0,0),图像的左上角坐标(-0.5,-0.5),如下图(2)所示;
(2)
两个坐标系的对比如下图所示:
二、通过创建初始矩阵实现仿射变换
通过初始化一个初始矩阵
hom_mat2d_identity (HomMat2DIdentity)
定义一个对角为 1 的 单位矩阵,它和任何矩阵相乘等于原矩阵本身。
配合下面hom_mat2d_translate(平移)、hom_mat2d_rotate(旋转)、hom_mat2d_scale(缩放)这三个算子可以实现图像、边缘、区域的仿射变化。
hom_mat2d_translate( : : HomMat2D, Tx, Ty : HomMat2DTranslate)
hom_mat2d_rotate( : : HomMat2D, Phi, Px, Py : HomMat2DRotate)
hom_mat2d_scale( : : HomMat2D, Sx, Sy, Px, Py : HomMat2DScale)
可应用的对象
affine_trans_pixel *应用于像素点
affine_trans_point_2d *应用于二维点
affine_trans_image *应用于图像
affine_trans_region *应用于区域
affine_trans_contour_xld *应用于XLD轮廓
2.1 平移矩阵
hom_mat2d_translate (HomMat2DIdentity, Tx, Ty, HomMat2DTranslate)
功能:把平移添加到防射变换矩阵
HomMat2D(输入参数):仿射变换矩阵
Tx(输入参数):沿Row方向平移的距离
Ty(输入参数):沿Column方向平移的距离
HomMat2DTranslate(输出参数):输出变换矩阵
如图所示,将一个区域row方向平移100,column向平移100的结果为:
*向Row方向平移120
trans_x:=120
*向Column方向平移0
trans_y:=0
hom_mat2d_identity (HomMat2DIdentity)
*Tx和Ty分别代表Row方向和Column方向的平移量
hom_mat2d_translate(HomMat2DIdentity,trans_x,trans_y,HomMat2DIdentity)
affine_trans_region(ROI_0, RegionAffineTrans, HomMat2DIdentity, 'nearest_neighbor')
2.2 旋转矩阵
hom_mat2d_rotate (HomMat2DIdentity, rad(), row, column, HomMat2DRotate)
rad():输入旋转的度数,为正代表逆时针
旋转,为负表示为顺时针旋转
row:围绕点旋转的行坐标
column:围绕点旋转的列坐标
1)红色区域沿着顶点坐标旋转80度
*坐标是起点,终点
min_row:=200
max_row:=220
min_column:=100
max_column:=200
gen_rectangle1 (ROI_0, min_row, min_column, max_row, max_column)
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_rotate (HomMat2DIdentity, rad(80), min_row, min_column, HomMat2DRotate)
affine_trans_region(ROI_0, RegionAffineTrans, HomMat2DRotate, 'nearest_neighbor')
2.3缩放矩阵
hom_mat2d_scale( : : HomMat2D, Sx, Sy, Px, Py : HomMat2DScale)
Sx: row方向的的缩放比例
Sy:column方向的的缩放比例
Px:变换的不动点(row坐标)
Py:变换的不动点(column坐标)
1) hom_mat2d_scale (HomMat2DIdentity, 0.5, 1, 0, 0, HomMat2DScale)
row方向的的缩放0.5,column方向的不缩放。下图中黄色的是红色区域转变后的结果,转后后坐标的关系变为row坐标减半,column的坐标不发生变化
一些测试代码:
dev_open_window(0, 0, 512, 512, 'black', WindowHandle)
*坐标是起点,终点
min_row:=200
max_row:=220
min_column:=100
max_column:=200
txt_string1:=min_row+' '+min_column
txt_string2:=max_row+' '+max_column
gen_rectangle1 (ROI_0, min_row, min_column, max_row, max_column)
*向Row方向平移50
* trans_x:=-10
*向Column方向平移100
* trans_y:=120
hom_mat2d_identity (HomMat2DIdentity)
*Tx和Ty分别代表Row方向和Column方向的平移量
* hom_mat2d_translate(HomMat2DIdentity,trans_x,trans_y,HomMat2DIdentity)
area_center(ROI_0, Area, Row, Column)
hom_mat2d_scale (HomMat2DIdentity, 0.5, 1, 200, 100, HomMat2DScale)
affine_trans_region(ROI_0, RegionAffineTrans, HomMat2DScale, 'nearest_neighbor')
get_region_points(RegionAffineTrans, Rows, Columns)
trans_min_row:=min(Rows)
trans_max_row:=max(Rows)
trans_min_column:=min(Columns)
trans_max_column:=max(Columns)
txt_string:=trans_min_row+' '+trans_min_column
txt_string3:=trans_max_row+' '+trans_max_column
dev_set_color('yellow')
dev_display(RegionAffineTrans)
dev_disp_text(txt_string1, 'window', min_row-10, min_column, 'black', [], [])
dev_disp_text(txt_string2, 'window', max_row, max_column, 'black', [], [])
dev_disp_text(txt_string, 'window', trans_min_row-10, trans_min_column, 'black', [], [])
dev_disp_text(txt_string3, 'window', trans_max_row, trans_max_column, 'black', [], [])
2.4例程
测试图像为下图所示:
1)平移
根据显示的坐标结果,并不是很准确的加上了平移的数值,row向向下平移30.column向右平移 150.
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_translate (HomMat2DIdentity,30, 150, HomMat2DTranslate)
affine_trans_region (Region, RegionAffineTrans, HomMat2DTranslate, 'nearest_neighbor')
read_image (Image, '1.PNG')
threshold (Image, Region, 0, 200)
opening_circle (Region, Region, 1.5)
connection (Region, ConnectedRegions)
select_shape_std (ConnectedRegions, SelectedRegions, 'max_area', 70)
*得到变换的中心点
area_center (SelectedRegions, Area, Row, Column)
dev_open_window_fit_image(Image, 0, 0, -1, -1, WindowHandle)
dev_set_draw ('margin')
*hom_mat2d_translate中的两个参数的意思是:Tx和Ty分别代表Row方向和Column方向的平移量
dev_display (Image)
disp_cross (WindowHandle, Row, Column, 10, 40)
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_translate (HomMat2DIdentity,30, 150, HomMat2DTranslate)
affine_trans_region (Region, RegionAffineTrans, HomMat2DTranslate, 'nearest_neighbor')
area_center (RegionAffineTrans, Area, Row_trans, Column_trans)
2)旋转
区域的旋转:
read_image (Image, '1.PNG')
threshold (Image, Region, 0, 200)
opening_circle (Region, Region, 1.5)
connection (Region, ConnectedRegions)
select_shape_std (ConnectedRegions, SelectedRegions, 'max_area', 70)
*得到变换的中心点
area_center (SelectedRegions, Area, Row, Column)
dev_open_window_fit_image(Image, 0, 0, -1, -1, WindowHandle)
dev_set_draw ('margin')
dev_display (Image)
dev_set_color('yellow')
disp_cross (WindowHandle, Row, Column, 10, 40)
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_rotate (HomMat2DIdentity, rad(20), Row, Column, HomMat2DRotate)
affine_trans_region (Region, RegionAffineTrans, HomMat2DRotate, 'nearest_neighbor')
dev_set_color('red')
dev_display (RegionAffineTrans)
3)缩放
区域的缩放:
read_image (Image, '1.PNG')
get_image_size(Image, Width, Height)
threshold (Image, Region, 0, 200)
opening_circle (Region, Region, 1.5)
connection (Region, ConnectedRegions)
select_shape_std (ConnectedRegions, SelectedRegions, 'max_area', 70)
*得到变换的中心点
area_center (SelectedRegions, Area, Row, Column)
* disp_cross (WindowHandle, Row, Column, 10, 40)
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_scale (HomMat2DIdentity, 2.0, 1.05, Row, Column, HomMat2DScale)
affine_trans_region (Region, RegionAffineTrans, HomMat2DScale, 'nearest_neighbor')
* affine_trans_image(Image, ImageAffineTrans, HomMat2DScale, 'constant', 'false')
* get_image_size(ImageAffineTrans, Width_imagetrans, Height_imagetrans)
dev_open_window_fit_image(Image, 0, 0, -1, -1, WindowHandle)
dev_set_draw ('margin')
dev_set_color('red')
dev_display (Image)
dev_display (RegionAffineTrans)
图像的缩放:注意图像缩放后图像的宽高不变换
三、不创建初始化矩阵的方法
刚性仿射变换:在图像坐标系内只有平移和旋转的位置转换,不涉及斜切和缩放,而所有的定位项目的应用场景也都是刚性仿射变换。
有时候,并不需要创建初始化矩阵也可以执行仿射变换,例如vector_angle_to_rigid算子就是如此。它只需要输入“源”的定位点坐标、倾斜角度,以及“目标”的定位点坐标、倾斜角度,它就能生成一个仿射变换矩阵用于配准。
vector_angle_to_rigid( : : Row1, Column1, Angle1, Row2, Column2, Angle2 : HomMat2D),刚性仿射变换算子,rigid中文意思是刚性。
该算子意思是:先将图像旋转,旋转角度为(Angle2 - Angle1) (逆时针为正),旋转中心坐标是(Row1, Column1)。再将原图的点(Row1, Column1)一一对应移到点 (Row2, Column2)上,移动的row和column方向的位移分别是( Row2 - Row1)、( Column2 - Column1),如果Row1 = Row2, Column1 = Column2,那么就完整等价于旋转变换。该算子同时具备了旋转平移变换的功能。
参数:
Row1 (输入参数) ——原始点的行坐标
Column1 (输入参数) ——原始点的列坐标
Angle1 (输入参数) ——原始点的角度
Row2 (输入参数) ——转换点的行坐标
Column2 (输入参数) ——转换点的列坐标
Angle2 (输入参数) ——转换点的角度
HomMat2D (输出参数) ——输出转换矩阵
3.1 完全当旋转矩阵使用
执行下面的代码,可以发现,图像会一直以定点(100,200)为旋转的圆心,进行图像的旋转
read_image (Image, '1.PNG')
Row := 100
Column := 200
dev_open_window(0, 0, 512, 512, 'black', WindowHandle)
dev_display (Image)
for Index := 1 to 150 by 1
vector_angle_to_rigid (Row, Column, 0, Row, Column, rad(10), HomMat2D)
disp_cross (WindowHandle, 100, 200, 10, 40)
affine_trans_image (Image, ImageAffinTrans, HomMat2D, 'nearest_neighbor', 'false')
copy_image (ImageAffinTrans, Image)
endfor
3.2 加入平移变换
可以将vector_angle_to_rigid理解为同时执行旋转变换和平移变换。最难弄明白的是旋转中心是什么?下面的程序可以说明如果先旋转后平移,那么旋转中心是(Row1, Column1),而不是 (Row2, Column2)。
先旋转 后平移
read_image (Image, 'C:/Users/kongxiangnan/Desktop/1.PNG')
Row1 := 100
Column1 := 100
Row2 := 100
Column2 := 200
dev_open_window(0, 0, 512, 512, 'black', WindowHandle)
dev_display (Image)
*用vector_angle_to_rigid实现旋转和平移
vector_angle_to_rigid (Row1, Column1, 0, Row2, Column2, rad(10), HomMat2D)
affine_trans_image (Image, ImageAffinTrans, HomMat2D, 'nearest_neighbor', 'false')
*分两步依次执行旋转、平移
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_rotate (HomMat2DIdentity, rad(10) - 0, Row1, Column1, HomMat2DRotate)
hom_mat2d_translate (HomMat2DRotate,Row2 - Row1, Column2 - Column1, HomMat2DTranslate)
*观察图像ImageAffinTrans和ImageAffinTrans_2能够完全重合
affine_trans_image (Image, ImageAffinTrans_2, HomMat2DTranslate, 'nearest_neighbor', 'false')
3.3 例程
&emsp ;将图像顺时针旋转90度,使用仿射变化,将未旋转图像上的区域进行仿射变化印在旋转后的图像上。使用hom_mat2d_identity实现的代码如下:
read_image (Image, '1.PNG')
get_image_size(Image, Width, Height)
binary_threshold(Image, Region, 'max_separability', 'dark', UsedThreshold)
connection (Region, ConnectedRegions)
select_shape_std (ConnectedRegions, SelectedRegions, 'max_area', 70)
area_center(Image, Area, Row, Column)
*将图像旋转-90度
rotate_image (Image, ImageRotate, -90, 'constant')
get_image_size(ImageRotate, Width_rotate, Height_rotate)
area_center(ImageRotate, Area1, Row1, Column1)
*将上图中的区域通过仿射变换和旋转90度后的图像的区域重合
*分两步依次执行旋转、平移
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_rotate (HomMat2DIdentity, rad(-90) - 0, Row, Column, HomMat2DRotate)
hom_mat2d_translate(HomMat2DRotate, Row1-Row, Column1-Column, HomMat2DTranslate)
affine_trans_region(SelectedRegions, RegionAffineTrans, HomMat2DTranslate, 'nearest_neighbor')
dev_open_window_fit_image(ImageRotate, 0, 0, -1, -1, WindowHandle)
dev_display(ImageRotate)
dev_set_draw('margin')
dev_display(RegionAffineTrans)
*计算误差
binary_threshold(ImageRotate, Region, 'max_separability', 'dark', UsedThreshold)
connection (Region, ConnectedRegions)
select_shape_std (ConnectedRegions, SelectedRegions, 'max_area', 70)
area_center(SelectedRegions, Area, Row, Column)
area_center(RegionAffineTrans, Area, Row_region, Column_region)
diff_row:=abs(Row-Row_region)
diff_Column:=abs(Column-Column_region)
旋转90度后,图像已经发生变化,如下图所示:
检查旋转的结果大致上,误差在列向量上差一个像素点
使用 vector_angle_to_rigid方法实现:
vector_angle_to_rigid (Row, Column, 0, Row1, Column1, rad(-90), HomMat2D)
affine_trans_region(SelectedRegions, RegionAffineTrans, HomMat2D, 'nearest_neighbor')
`测试该方法下的平移误差,精度和上面的方法是一样的结果。
注意:用rotate_image 算子旋转图像时,如果旋转角度不是0°、90°、180°、270°等角度,那么图像其实只做了旋转变换,而没有进行平移变换。
例如当旋转-45度,测试图像的大小是否有变化:
旋转60度的图像
旋转90度的图像:
四、边缘中心坐标系的仿射变换
有时候使用刚性转换,坐标系只能到达普通的图像坐标系,此时的精度只能到半个像素点,可以使用hom_mat2d_translate_local仿射变换进行坐标的精细纠正,使得仿射变换后的结果和原始区域高精度重合在一起。
dev_close_window ()
dev_open_window (0, 0, 100, 100, 'black', WindowHandle)
gen_circle (Circle, 50, 50, 50)
*region to image
region_to_bin (Circle, BinImage, 255, 0, 512, 512)
area_center (Circle, Area, Row, Column)
***********
*旋转矩阵
vector_angle_to_rigid (Row, Column, 0, Row, Column, rad(90), HomMat2D)
*旋转得到未经坐标变换的区域,图1
affine_trans_region (Circle, RegionAffineTrans, HomMat2D, 'constant')
***********
*坐标变换
hom_mat2d_translate (HomMat2D, 0.5, 0.5, HomMat2DTranslate)
hom_mat2d_translate_local (HomMat2DTranslate, -0.5, -0.5, HomMat2DTranslate1)
*旋转得到经坐标变换的区域,图2
affine_trans_region (Circle, RegionAffineTrans1, HomMat2DTranslate1, 'constant')
图1 未经坐标转换
旋转后区域位置会偏移;
图2 经过坐标转换
旋转后的区域是重合的;
hom_mat2d_translate(HomMat2D, 0.5, 0.5, HomMat2DTmp)
hom_mat2d_translate_local(HomMat2DTmp, -0.5, -0.5, HomMat2DAdapted)
上述操作符将对象的坐标从HALCON标准坐标系(原点位于左上角像素的中心)转换为边缘中心坐标系(原点位于左上角像素的左上角)
五、图像旋转90度的三种操作方法
read_image (Image, 'C:/Users/kongxiangnan/Desktop/1.PNG')
get_image_size(Image, Width1, Height1)
*绕着图像中心点旋转90度
*这是基于标准图像坐标系
area_center(Image,Area,Row,Column)
vector_angle_to_rigid(Row,Column,0,Row,Column,rad(90),HomMat2D)
*以下两个运算交换下顺序也可以,因为矩阵乘法满足结合律
hom_mat2d_translate(HomMat2D, 0.5, 0.5, HomMat2DTmp)
*矩阵右乘,基于本地坐标系
hom_mat2d_translate_local(HomMat2DTmp, -0.5, -0.5, HomMat2DAdapted)
*这是基于像素坐标系
affine_trans_image(Image, ImageAffinTrans, HomMat2DAdapted, 'constant', 'false')
get_image_size(ImageAffinTrans, Width2, Height2)
area_center(ImageAffinTrans,Area,Row1,Column1)
stop()
*绕着图像中心点旋转90度(另一种实现方式)
get_image_size(Image,Width,Height)
Row := Height/2
Column := Width/2
vector_angle_to_rigid(Row,Column,0,Row,Column,rad(90),HomMat2D)
affine_trans_image(Image, ImageAffinTrans, HomMat2DAdapted, 'constant', 'false')
*绕着图像中心点旋转90度(第三种实现方式)
area_center(Image,Area, Row,Column)
vector_angle_to_rigid(Row+0.5,Column+0.5,0,Row+0.5,Column+0.5,rad(90),HomMat2D)
affine_trans_image(Image,ImageAffinTrans2,HomMat2D,'constant', 'false')
六、投影变换(解决图像的形变问题)
*该算子利用两个点坐标,计算变换矩阵(点1_X,点1_Y,点1_W, 点2_X,点2_Y,点2_W,HomMat2D表示一个结果矩阵)
hom_vector_to_proj_hom_mat2d ([Px],[Py],[Pw],[Qx],[Qy],[Qw], 'normalized_dlt', HomMat2D)
*投影变换,应用于图像
projective_trans_image (ImageReduced, TransImage, HomMat2D, 'bilinear', 'false', 'false')
例程:
原始图像
矫正后图像:
dev_close_window ()
read_image (Image, 'C:/Users/Public/Documents/MVTec/HALCON-17.12-Progress/examples/images/caliper/caliper_calib_12.png')
rgb1_to_gray (Image, GrayImage)
get_image_size (GrayImage, Width, Height)
dev_open_window (0, 0, Width/2, Height/2, 'black', WindowHandle)
dev_display (GrayImage)
XCoordCorners:=[]
YCoordCorners:=[]
*阈值处理,提取灰度在15-90之间的区域(灰度直方图)
threshold (GrayImage, Regions, 15, 90)
*将提取的区域,不相连的部分分割成若干个
connection (Regions, ConnectedRegions)
*面积筛选,选择面积在613-2871之间的区域(特征直方图)
select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 613.86, 2871.29)
*裁剪区域
reduce_domain (GrayImage, SelectedRegions, ImageReduced)
*创建边缘轮廓
gen_contour_region_xld (SelectedRegions, Contours, 'border')
*将边缘轮廓分割为边(ps: 正方形4条,6边形6条)
segment_contours_xld (Contours, ContoursSplit, 'lines_circles', 5, 4, 2)
count_obj (ContoursSplit, Number)
for Index := 1 to Number by 1
select_obj (ContoursSplit, ObjectSelected, Index)
*拟合每条边
fit_line_contour_xld (ObjectSelected, 'tukey', -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist)
*存储当前的边顶点的X,Y坐标
tuple_concat (XCoordCorners, RowBegin, XCoordCorners)
tuple_concat (YCoordCorners, ColBegin, YCoordCorners)
endfor
Xoff:=100
Yoff:=100*Height/Width
*该算子利用两个点坐标,计算变换矩阵(Point1_X,Point1_Y,Point1_W, Point2_X,Point2_Y,Point2_W)
hom_vector_to_proj_hom_mat2d (XCoordCorners, YCoordCorners,[1,1,1,1], [Yoff,Yoff,Height-Yoff,Height-Yoff], [Xoff,Width-Xoff,Width-Xoff,Xoff], [1,1,1,1], 'normalized_dlt', HomMat2D)
*投影变换
projective_trans_image (ImageReduced, TransImage, HomMat2D, 'bilinear', 'false', 'false')
dev_display (TransImage)