Halcon用矩阵实现图像变换(平移,旋转,缩放,镜像等)

目录


        在halcon中经常会用到图像变换的操作,然后这次作业是用矩阵来实现相关算子的功能,学到了挺多的所以就记录下来方便复习。

图像变换介绍

        基础部分可以看这篇文章,写的挺好的

深入浅出HALCON几何变换icon-default.png?t=N7T8https://www.51halcon.com/forum.php?mod=viewthread&tid=1466

 用Halcon自带的算子实现图像变换

        在学习矩阵先用算子实现一遍,毕竟这些封装好算子才是高频使用的东西,而且也可以方便

接下来自己实现时比对效果

read_image (Image, 'datacode/ecc200/ecc200_to_preprocess_001.png')
hom_mat2d_identity (HomMat2DIdentity)
*平移
hom_mat2d_translate (HomMat2DIdentity, 64, 64, HomMat2DTranslate)
affine_trans_image (Image, ImageAffineTrans, HomMat2DTranslate, 'constant', 'false')
*缩放
hom_mat2d_scale (HomMat2DIdentity, 2, 2, 0, 0, HomMat2DScale)
affine_trans_image (Image, ImageAffineTrans1, HomMat2DScale, 'constant', 'false')
*旋转
hom_mat2d_rotate (HomMat2DIdentity, 0.78, 0, 0, HomMat2DRotate)
affine_trans_image (Image, ImageAffineTrans2, HomMat2DRotate, 'constant', 'false')
*镜像
get_image_size (Image, Width, Height)
hom_mat2d_reflect (HomMat2DIdentity, 0, Width/2, 10, Width/2 , HomMat2DReflect1)
affine_trans_image (Image, ImageAffineTrans3, HomMat2DReflect1, 'constant', 'false')

        这里我提一嘴hom_mat2d_reflect的第2到第5个的参数。这其实是两个坐标点,halcon根据这两个坐标形成的线来当作对称轴。比如我这里的坐标点是(0, Width/2)和(10, Width/2),也就是图像列的中点,所以图像会左右镜像。

        顺便一提,这个图片是halcon自带的,打开read_image的算子窗口点击文件夹的图标就可以找到你的halcon的图片存放的路径,上面我已经改成相对路径所以应该可以直接运行

使用矩阵来实现相关算子的功能

一、平移

        基本的思路是将图像的每个坐标和矩阵相乘得到新的坐标,然后将旧坐标的像素值赋与新坐标

        首先创建一个矩阵

*平移
*[1,0,x,\
*0,1,y,\
*0,0,1]
create_matrix (3, 3, [1,0,64,\
                      0,1,64,\
                      0,0,1], MatrixID)

        平移矩阵形式如上,x和y分别是你在x轴和y轴上的平移量。

        然后遍历图片,将每个坐标与矩阵相乘,在这里我们先创建一个新的空图像,然后将变换后的图输入到新图像中,这样就不会覆盖原图

*使用gen_image_const 创建一个空图像
get_image_size (Image, Width, Height)
gen_image_const (Imageout, 'byte', Width, Height)
for x := 0 to Height - 1 by 1
    for y := 0 to Width - 1 by 1
        *得到原图的坐标点的像素值
        get_grayval (Image, x, y, Grayval)
        *将坐标转化为矩阵形式以用于矩阵相乘
        create_matrix (3, 1, [x,\
                              y,\
                              1], MatrixID1)
        *平移
        mult_matrix (MatrixID, MatrixID1, 'AB', MatrixMultID)
        *得到矩阵的值,Values[0]是x,Values[1]是y
        get_full_matrix (MatrixMultID, Values)
        *边界处理
        if (Values[0] >= 0 and Values[0] <Height and\
            Values[1] >= 0 and Values[1] <Width)
            *将矩阵的值赋与新图像
            set_grayval (Imageout, Values[0], Values[1], Grayval)
        endif
        
        endif
    endfor
endfor

        因为我们图像大小是400*400的,所以根据这张图像创建的新图像大小也是400*400的。当它平移后超过图像大小范围的地方要被裁剪。如果你不想裁剪图像,那么创建新图像时就要考虑到平移的长度。

        如上,左侧是原图,中间红色区域是图像的域,也就是图像的大小,右侧是平移后的图像。可以看到图像超出红色区域的部分被裁剪。

二、旋转

        旋转的思路和平移略有不同,如果直接遍历原图你会得到这样的结果

左图为正常结果图像,右图为遍历原图得到的图像

        出现这种结果是因为当你用矩阵去乘图像时很可能得不到某些点的坐标,因此这些点不会被赋值,自然就会出现空洞。

        用下面的代码测试

*由图可知在点(1,3)处有空洞,遍历原图,让程序在点(1,3)处停止,
*如果没有停止则代表该点没有出现过
mult_matrix (MatrixID2, MatrixID1, 'AB', MatrixMultID2)
get_full_matrix (MatrixMultID2, Values2)
if (Values2[0] >= 1 and Values2[0] <2 and\
Values2[1] >= 3 and Values2[1] <4)
    stop ()
endif

查过资料后才知道,为防止空洞出现一般用反解法。所谓反解法就是不遍历原图,而是遍历结果图,这样就可以保证结果图上的每一个点都有数值,防止空洞现象出现。

        如图所示,一般情况我们是遍历原图来得到结果图,就是①的思路,但原图上的坐标点并不和结果图一一对应,也就是说原图上的(2,3)未必就落在结果图的(2,3)上(一样的话你也实现不了旋转的效果╮(╯▽╰)╭)

        现在转变一下思路,既然原图上的点无法一一映射到结果图上,那么不妨用结果图一一映射到原图上去,也就是②的思路:通过遍历结果图的坐标,在原图上去寻找对应的像素值。这就是反解法。

        可能还会有人有疑惑,所以我再啰嗦一点。两张图之间的对应关系来源于你的矩阵,而矩阵运算结果未必就包括了所有的点,所以正推时出现的空洞就是结果中没有包含的点。而反推时其实也没有用到原图的所有点,比如原图是400X400的大小共160,000个像素值,结果图中比如就用了15万个点的值,那么剩下1万多个点就是矩阵运算中没有计算到的点的地方了。所以其实只用反解法的话效果未必会好,这也是为什么需要用线性插值的原因了。

        那么说完为什么用反解法后,就是怎么用反解法了。我们可以先从数学角度来看

        其中,Y是结果图的坐标矩阵,X是原图的坐标矩阵,A则是旋转矩阵,A(-1)则是A的逆矩阵。一般我们是已知X和A要求Y,但现在我们使用反解法,故此是已知Y和A要求X,简单推导后可以得到上式。所以反解法说着好听,其实就是用A的逆矩阵来乘就行了,代码如下。

*旋转
create_matrix (3, 3, [cos(rad(45)),-sin(rad(45)),0,\
                      sin(rad(45)),cos(rad(45)),0,\
                      0,0,1], MatrixID2)
*使用反解法故需得到逆矩阵
invert_matrix (MatrixID2, 'general', 0, MatrixInvID)

for x := 0 to Height - 1 by 1
    for y := 0 to Width - 1 by 1
        *得到原图的坐标点的像素值
        get_grayval (Image, x, y, Grayval)
        *将坐标转化为矩阵形式以用于矩阵相乘
        create_matrix (3, 1, [x,\
                              y,\
                              1], MatrixID1)
        
        *旋转
        mult_matrix (MatrixInvID, MatrixID1, 'AB', MatrixMultID2)
        get_full_matrix (MatrixMultID2, Values2)
        
        endif
    endfor
endfor

        这样一来我们就得到我们想要的值Values2。当然就如上面所说,反解法得到的结果未必就好,多数情况下我们还需要使用线性插值的办法来得到最佳的结果

        有关线性插值我们可以参考下面这一篇,我觉得讲的还挺好的

双线性插值原理详解及代码实现 – kkjz的文章

        根据大佬的思路创建下面两个函数,然后直接用就行了

*函数一:线性插值single_biline
*x:输入点坐标,x1,x2:已知点坐标,p1,p2:已知点像素值,p:输入点像素值

al := abs(x2 - x)
p := al*p1 + (1 - al)*p2

*函数二:双线性插值double_biline
*边界处理
get_image_size (Image, Width, Height)
if (Value_x < 0 or Value_y< 0 or\
Value_x > Height - 1 or Value_y> Width - 1)
    pout := -1
    return ()
endif
*得到x轴的相邻点
tuple_floor (Value_x, x1)
x1 := int (x1)
tuple_ceil (Value_x, x2)
x2 := int (x2)
*得到y轴的相邻点
tuple_floor (Value_y, y1)
y1 := int (y1)
tuple_ceil (Value_y, y2)
y2 := int (y2)
*得到点的像素值
get_grayval (Image, x1, y1, p1)
get_grayval (Image, x2, y1, p2)
get_grayval (Image, x1, y2, p3)
get_grayval (Image, x2, y2, p4)
*双线性插值
single_biline (Value_x, x1, x2,  p1, p2, p12)
single_biline (Value_x, x1, x2, p3, p4, p34)
single_biline (Value_y, y1, y2, p12, p34, pout)

        完整代码如下

*旋转
create_matrix (3, 3, [cos(rad(45)),-sin(rad(45)),0,\
                      sin(rad(45)),cos(rad(45)),0,\
                      0,0,1], MatrixID2)
*使用反解法故需得到逆矩阵
invert_matrix (MatrixID2, 'general', 0, MatrixInvID)
get_image_size (Image, Width, Height)
gen_image_const (Imageout2, 'byte', Width, Height)
for x := 0 to Height - 1 by 1
    for y := 0 to Width - 1 by 1
        create_matrix (3, 1, [x,\
                              y,\
                              1], MatrixID1)
        *旋转
        mult_matrix (MatrixInvID, MatrixID1, 'AB', MatrixMultID2)
        get_full_matrix (MatrixMultID2, Values2)
        
        double_biline (Image, Values2[0], Values2[1], pout)
        if (pout == (-1))
            continue
        endif
        set_grayval (Imageout2, x, y, pout)
endfor

        在这里我的边界处理和大佬不同,我是直接将越界的值直接continue掉。因为我这里原图和结果图的域是相同的,所以结果图中有些地方不存在值,直接continue掉了。

三、缩放

        缩放与旋转思路一样,只是矩阵不同而已,所以我就直接贴代码了

*缩放,x和y分别是x轴和y轴的缩放倍率
*[x,0,0,\
*0,y,0,\
*0,0,1]
create_matrix (3, 3, [2,0,0,\
                      0,2,0,\
                      0,0,1], MatrixID3)
invert_matrix (MatrixID3, 'general', 0, MatrixInvID1)
get_image_size (Image, Width, Height)
gen_image_const (Imageout3, 'byte', Width, Height)
for x := 0 to Height - 1 by 1
    for y := 0 to Width - 1 by 1
        create_matrix (3, 1, [x,\
                              y,\
                              1], MatrixID1)
        *缩放
        mult_matrix (MatrixInvID1, MatrixID1, 'AB', MatrixMultID3)
        get_full_matrix (MatrixMultID3, Values3)
        
        get_grayval (Image,Values3[0], Values3[1], Grayval1)
        set_grayval (Imageout3, x, y, Grayval1)
        double_biline (Image, Values3[0], Values3[1], pout2)
        if (pout2 == (-1))
            continue
        endif
        set_grayval (Imageout3,x,y, pout2)
        
    endfor
endfor

四、镜像

        镜像与平移同理,不过镜像矩阵我是直接抄的hom_mat2d_reflect的矩阵,感兴趣可以自行研究

*镜像
get_image_size (Image, Width, Height)
create_matrix (3, 3, [1,0,0,\
                      0,-1,Width,\
                      0,0,1], MatrixID4)

get_image_size (Image, Width, Height)
gen_image_const (Imageout4, 'byte', Width, Height)
for x := 0 to Height - 1 by 1
    for y := 0 to Width - 1 by 1
        get_grayval (Image, x, y, Grayval)
        create_matrix (3, 1, [x,\
                              y,\
                              1], MatrixID1)
        *镜像
        mult_matrix (MatrixID4, MatrixID1, 'AB', MatrixMultID4)
        get_full_matrix (MatrixMultID4, Values4)
        if (Values4[0] >= 0 and Values4[0] <Height and\
            Values4[1] >= 0 and Values4[1] <Width)
            set_grayval (Imageout4, Values4[0], Values4[1], Grayval)
        endif
        
    endfor
endfor

完整代码

read_image (Image, 'datacode/ecc200/ecc200_to_preprocess_001.png')
* read_image (Image, 'claudia.png')
* rgb1_to_gray (Image, Image)
hom_mat2d_identity (HomMat2DIdentity)
get_domain (Image, Domain)
*平移
hom_mat2d_translate (HomMat2DIdentity, 64, 64, HomMat2DTranslate)
affine_trans_image (Image, ImageAffineTrans, HomMat2DTranslate, 'constant', 'false')
*缩放
hom_mat2d_scale (HomMat2DIdentity, 2, 2, 0, 0, HomMat2DScale)
affine_trans_image (Image, ImageAffineTrans1, HomMat2DScale, 'constant', 'false')
*旋转
hom_mat2d_rotate (HomMat2DIdentity, 0.78, 0, 0, HomMat2DRotate)
affine_trans_image (Image, ImageAffineTrans2, HomMat2DRotate, 'constant', 'false')
*镜像
get_image_size (Image, Width, Height)
hom_mat2d_reflect (HomMat2DIdentity, 0, Width/2, 10, Width/2 , HomMat2DReflect1)
affine_trans_image (Image, ImageAffineTrans3, HomMat2DReflect1, 'constant', 'false')

*tuple实现变换
dev_update_off ()
*平移
create_matrix (3, 3, [1,0,64,\
                      0,1,64,\
                      0,0,1], MatrixID)
*旋转
create_matrix (3, 3, [cos(rad(45)),-sin(rad(45)),0,\
                      sin(rad(45)),cos(rad(45)),0,\
                      0,0,1], MatrixID2)
*使用反解法故需得到逆矩阵
invert_matrix (MatrixID2, 'general', 0, MatrixInvID)

*缩放
create_matrix (3, 3, [2,0,0,\
                      0,2,0,\
                      0,0,1], MatrixID3)
invert_matrix (MatrixID3, 'general', 0, MatrixInvID1)

*镜像
get_image_size (Image, Width, Height)
create_matrix (3, 3, [1,0,0,\
                      0,-1,Width,\
                      0,0,1], MatrixID4)

get_image_size (Image, Width, Height)
gen_image_const (Imageout, 'byte', Width, Height)
gen_image_const (Imageout2, 'byte', Width, Height)
gen_image_const (Imageout3, 'byte', Width, Height)
gen_image_const (Imageout4, 'byte', Width, Height)
for x := 0 to Height - 1 by 1
    for y := 0 to Width - 1 by 1
        get_grayval (Image, x, y, Grayval)
        create_matrix (3, 1, [x,\
                              y,\
                              1], MatrixID1)
        *平移
        mult_matrix (MatrixID, MatrixID1, 'AB', MatrixMultID)
        get_full_matrix (MatrixMultID, Values)
        if (Values[0] >= 0 and Values[0] <Height and\
            Values[1] >= 0 and Values[1] <Width)
            set_grayval (Imageout, Values[0], Values[1], Grayval)
        endif

        *旋转
        mult_matrix (MatrixInvID, MatrixID1, 'AB', MatrixMultID2)
        get_full_matrix (MatrixMultID2, Values2)
        
        double_biline (Image, Values2[0], Values2[1], pout)
        if (pout != (-1))
            set_grayval (Imageout2, x, y, pout)
        endif
        
        *缩放
        mult_matrix (MatrixInvID1, MatrixID1, 'AB', MatrixMultID3)
        get_full_matrix (MatrixMultID3, Values3)

        double_biline (Image, Values3[0], Values3[1], pout2)
        if (pout2 != (-1))
            set_grayval (Imageout3,x,y, pout2)
        endif
        
        
        *镜像
        mult_matrix (MatrixID4, MatrixID1, 'AB', MatrixMultID4)
        get_full_matrix (MatrixMultID4, Values4)
        if (Values4[0] >= 0 and Values4[0] <Height and\
            Values4[1] >= 0 and Values4[1] <Width)
            set_grayval (Imageout4, Values4[0], Values4[1], Grayval)
        endif
        
                

    endfor
endfor






相关函数代码

*函数一:线性插值single_biline
*x:输入点坐标,x1,x2:已知点坐标,p1,p2:已知点像素值,p:输入点像素值

al := abs(x2 - x)
p := al*p1 + (1 - al)*p2

*函数二:双线性插值double_biline
*边界处理
get_image_size (Image, Width, Height)
if (Value_x < 0 or Value_y< 0 or\
Value_x > Height - 1 or Value_y> Width - 1)
    pout := -1
    return ()
endif
*得到x轴的相邻点
tuple_floor (Value_x, x1)
x1 := int (x1)
tuple_ceil (Value_x, x2)
x2 := int (x2)
*得到y轴的相邻点
tuple_floor (Value_y, y1)
y1 := int (y1)
tuple_ceil (Value_y, y2)
y2 := int (y2)
*得到点的像素值
get_grayval (Image, x1, y1, p1)
get_grayval (Image, x2, y1, p2)
get_grayval (Image, x1, y2, p3)
get_grayval (Image, x2, y2, p4)
*双线性插值
single_biline (Value_x, x1, x2,  p1, p2, p12)
single_biline (Value_x, x1, x2, p3, p4, p34)
single_biline (Value_y, y1, y2, p12, p34, pout)

版权声明:本文为博主作者:meidin原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/meidin/article/details/137030228

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2024年4月1日
下一篇 2024年4月1日

相关推荐