学习笔记 3 — 图像仿射变换详解【含实例和代码解析】

一、基础的图像变化

之前做过了Harris特征匹配和SIFT特征匹配的测试例子,如果要实现拼接,会涉及到一些基础的图像处理,简单的2D图像变换主要包括以下几种:

1. 平移变换
主要是水平方向和垂直方向地移动变换,2个自由度。

2. 刚体变换
刚体变换包含旋转和平移变换, 3个自由度,点与点之间的距离不变。就好像你扔了一把三角尺出去,不仅在位置上发生变化,也进行了一定角度的旋转。

3. 相似变换
相似原理大家高中都学过,就是形状不变,加了缩放尺度, 四个自由度,点与点之间的距离比不变。

4. 仿射变化
仿射变换和相似变换近似,不同之处在于相似变换具有单一旋转因子和单一缩放因子,仿射变换具有两个旋转因子和两个缩放因子,因此具有6个自由度. 不具有保角性和保持距离比的性质,但是原图平行线变换后仍然是平行线.。

5. 投影变换
投影变换叫作单应性变换。投影变换是齐次坐标下非奇异的线性变换。然而在非齐次坐标系下却是非线性的,这说明齐次坐标的发明是很有价值的。投影变换比仿射变换多2个自由度,具有8个自由度。

下面主要介绍的是二维图像的仿射变化。

二、图像仿射变换原理

刚开始老师介绍图像变换类型的时候并不太明白仿射是什么含义,后来通过例子明白其实仿射变换和透视变换更直观的叫法可以叫做“平面变换”和“空间变换”或者“二维坐标变换”和“三维坐标变换”。

定义:仿射变换,又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。

仿射变换能够保持图像的“平直性”,包括旋转,缩放,平移,错切操作。一般而言,仿射变换矩阵为2*3的矩阵,第三列的元素起着平移的作用,前面两列的数字对角线上是缩放,其余为旋转或者错切的作用。变换矩阵关系如下:在这里插入图片描述
其中A为变换后坐标矩阵,C为原始坐标矩阵,B是仿射变换矩阵,有6个未知量,假设目标图形以(x , y )为轴心顺时针旋转θ弧度到目标图像,则变换矩阵对应的变量为:
在这里插入图片描述
所以前两列的4个未知量a,b,d,e是起到旋转的作用,第三列的2个未知量c,f起到了平移的作用。仿射变换的方程组有6个未知数,所以要求解就需要找到3组映射点,三个点刚好确定一个平面。

如果不考虑特征匹配,我们只考虑图像的固定点映射,那么大概有以下三个步骤:

1. 根据图像的变化特点,选择合适的变化结构。
2. 根据DLT等方法计算变换结构。
3. 采用正向/逆向映射,利用插值方式实现图像映射变换。

三、实际应用与分析

下面我们来看一下图像映射的效果图。图片取景于集美大学的中山纪念馆已经嘉庚湖,以及一张广告牌,一张表情包。
例子一:把表情包贴到建筑物上。

建筑物(中山纪念馆):
Alt
表情包:
在这里插入图片描述
映射效果图:在这里插入图片描述
例子二:将集美大学嘉庚湖映射到广告牌上。

嘉庚湖:在这里插入图片描述
广告牌:在这里插入图片描述
映射效果图:在这里插入图片描述
可以看出来,当映射的图片只有位移上的变化时,图片可以完美映射到理想的位置,可以如果要让图像发展旋转的话,就会出现上图的情况,右下角的部分并没有完全贴合,这是因为变换矩阵中存在着映射残差,使得变化矩阵并不能达到最理想的状态。

首先我们来看一下图像映射源代码
main:

from scipy import ndimage
from pylab import *
from PIL import Image
from numpy import *


import warp
im1 = array(Image.open('kid.png').convert('L'))
im2 = array(Image.open('house1.png').convert('L'))
tp = array([[252, 420, 420, 252], [270, 270, 461, 461], [1, 1, 1, 1]])
im3 = warp.image_in_image(im1, im2, tp)

figure()
gray()
imshow(im3)
axis('equal')
axis('off')
show()

简要分析:
im1,im2分别读入的是仿射图和背景图,目的是将图像im1仿射到im2上。
tp是一个矩阵,包含了仿射图要在背景图里仿射的框架坐标,四个点的坐标顺序分别是左上、左下、右下、右上。第一个矩阵是四个点的纵坐标,第二个矩阵是四个点的横坐标,第三个矩阵是为求解变换矩阵准备的。
warp.image_in_image的输入是两种图片已经映射的框架坐标点矩阵。

image_in_image函数:

def image_in_image(im1,im2,tp):    
    # points to warp from
    m,n = im1.shape[:2]
    fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
    
    # compute affine transform and apply
    H = homography.Haffine_from_points(tp,fp)
    im1_t = ndimage.affine_transform(im1,H[:2,:2],
                    (H[0,2],H[1,2]),im2.shape[:2])
    alpha = (im1_t > 0)
    
    return (1-alpha)*im2 + alpha*im1_t

简要分析:
m、n分别是仿射图的大小。
homography.Haffine_from_points()是一个变换矩阵函数,有两个输入tp,fp;其中tp是仿射后的框架坐标点矩阵,fp是仿射图的原图框架矩阵。它的作用是在变化前的框架和变化后的框架之间,求出一个变换矩阵。输出矩阵H就是我们原理中的矩阵B。
ndimage.affine_transform()的四个输入中,im1是仿射图像,H[2,2]是旋转矩阵(包含旋转角θ), (H[0,2],H[1,2])是位移矩阵(包含了x,y),im2.shape[:2]是背景图im2的大小,输出是一张图像,大小和im2相同。证明如下图:在这里插入图片描述
除了大小一样,输出的im1_t图像中只有映射的框架部分有图像,其他部分都定义为其他颜色。在这里插入图片描述
alpha通道:
阿尔法通道是一个8位的灰度通道,该通道用256级灰度来记录图像中的透明度信息,定义透明、不透明和半透明区域,其中白表示不透明,黑表示透明,灰表示半透明。范围是 0~255,即 255 是不透明,0是全透明。我们主要要控制显示与不显示,所以在二进制里1表示不透明,0表示透明。

alpha = (im1_t > 0)
return (1-alpha)im2 + alphaim1_t
第一句可以理解为将映射框架部分的点alpha值设置为1,就是不透明,而其他部分不需要则设置为0。
第二句就是拼接两张图的最后关键步骤,将图im1_1的仿射部分和原图im2的非仿射部分相加,就得到最后的合成图。

四、改进:三角形仿射变换

在前面的操作里,可以看到如果使用完全图像进行仿射扭曲,我们不可能使用同一个仿射变化将全部的四个角点变换到它们的目标位置,所以我们可以想到,对于三个点,我们是可以将一幅图像进行扭曲,并且使这三对点对可以完美匹配。于是,我们采用三角形仿射变化,看一下实际效果:在这里插入图片描述
明显可以看出,利用了三角形仿射变换后,图像完美贴合到预期框架上,三角形仿射扭曲比完全图像扭曲的图像效果更好,这里就不解析具体的原理,与完全图像扭曲的原理大同小异。

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值