一、单应性变换
单应性变换是将一个平面内的点映射到另一个平面内的二维投射变换,在这里,平面是指图像或者三维中的平面表面。单应性变换具有很强的实用性,比如图像批准、图像纠正和纹理扭曲,以及创建全景图像。本质上,单应性变换H,按照下面的方程映射二维中的点,齐坐标含义如下:
对于图像平面内的点,齐坐标是一个非常有用的表达方式,点的齐坐标依赖于其尺度定义的,所以,x=[x,y,w]=[ax,ay,aw]=[x/w,y/w,1]都表示同一个二维点。因此,单应性矩阵H也仅一化点,这样,点具有唯一的图像坐标x和y,这个额外的坐标使得我们可以简单地使用一个矩阵来表示变换。
矩阵HH会将一幅图像上的一个点的坐标a=(x,y,1)映射成另一幅图像上的点的坐标b=(x1,y1,1),也就是说,我们已知a和b,它们是在同一平面上。 则有下面的公式:
b=Ha^T
即
可以推导出:
其中,h=[h11,h12,h13,h21,h22,h23,h31,h32,h33]Th[h11,h12,h13,h21,h22,h23,h31,h32,h33]T,是一个9维的列向量。若令:
经过变换可以得出:Ah=0
这里的A∈R2×9A∈R2×9。这只是1对点所得到的矩阵AA,若有4对点,则得到的矩A∈R8×9A∈R8×9。如何求解向量hh呢?方法很简单,真接对AA进行SVD分解,即
U∗Σ∗VT
然后取VV的最后一列出来作为求解hh。因为矩阵AA是行满秩,即只有一个*度。具体实现时,先要得到两幅图,然后在两幅图之间找到4对点的坐标,由此得到矩阵AA,然后在matlab中可以这样实现:
[U,S,V]=svd(A);
h=V(:,9);
H= reshape(h,3,3);
二、α通道
阿尔法通道(α Channel或Alpha Channel)是指一张图片的透明和半透明度。例如:一个使用每个像素16比特存储的位图,对于图形中的每一个像素而言,可能以5个比特表示红色,5个比特表示绿色,5个比特表示蓝色,最后一个比特是阿尔法。在这种情况下,它要么表示透明要么不是,因为阿尔法比特只有0或1两种不同表示的可能性。又如一个使用32个比特存储的位图,每8个比特表示红绿蓝,和阿尔法通道。在这种情况下,就不光可以表示透明还是不透明,阿尔法通道还可以表示256级的半透明度,因为阿尔法通道有8个比特可以有256种不同的数据表示可能性。
**三、仿射变换
仿射变换,又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一平移,变换为另一个向量空间。仿射变换是在几何上定义为两个向量空间之间的一个仿射变换或者仿射映射(来自拉丁语,affine,“和…相关”)由一个非奇异的线性变换(运用一次函数进行的变换)接上一个平移变换组成。在有限维的情况,每个仿射变换可以由一个矩阵A和一个向量b给出,它可以写作A和一个附加的列b。一个仿射变换对应于一个矩阵和一个向量的乘法,而仿射变换的复合对应于普通的矩阵乘法,只要加入一个额外的到矩阵的底下,这一行全部是0除了最右边是一个1,而列向量的底下要加上一个1。
典型的仿射变换有:
(1)平移,将每一点移到到(x+t , y+t),变换矩阵为
(2)缩放变换 将每一点的横坐标放大或缩小sx倍,纵坐标放大(缩小)到sy倍,变换矩阵为:
(3)旋转变换原点:目标图形围绕原点顺时针旋转Θ 弧度,变换矩阵为:
(4) 旋转变换 :目标图形以(x , y )为轴心顺时针旋转θ弧度,变换矩阵为:
相当于两次平移与一次原点旋转变换的复合,即先将轴心(x,y)移到到原点,然后做旋转变换,最后将图片的左上角置为图片的原点,即:
实现代码为:
def H_from_points(fp,tp):
if fp.shape != tp.shape:
raise RuntimeError('number of points do not match')
#对点进行归一化
#映射起始点
m = mean(fp[:2], axis=1)
maxstd = max(std(fp[:2], axis=1)) + 1e-9
C1 = diag([1/maxstd, 1/maxstd, 1])
C1[0][2] = -m[0]/maxstd
C1[1][2] = -m[1]/maxstd
fp = dot(C1,fp)
# 映射对应点
m = mean(tp[:2], axis=1)
maxstd = max(std(tp[:2], axis=1)) + 1e-9
C2 = diag([1/maxstd, 1/maxstd, 1])
C2[0][2] = -m[0]/maxstd
C2[1][2] = -m[1]/maxstd
tp = dot(C2,tp)
# 因为归一化后的均值为0,所以平移量为0
A = zeros((2*nbr_correspondences,9))
for i in range(nbr_correspondences):
A[2*i] = [-fp[0][i],-fp[1][i],-1,0,0,0,
tp[0][i]*fp[0][i],tp[0][i]*fp[1][i],tp[0][i]]
A[2*i+1] = [0,0,0,-fp[0][i],-fp[1][i],-1,
tp[1][i]*fp[0][i],tp[1][i]*fp[1][i],tp[1][i]]
U,S,V = linalg.svd(A)
H = V[8].reshape((3,3))
H = dot(linalg.inv(C2),dot(H,C1))
return H / H[2,2]
此函数实现计算H,仿射变换,使得tp是fp经过仿射变换H得到的。
仿射扭曲的一个简单例子是,将图像或者图像的一部分放置在另一幅图像中,使得它们能够和指定的区域或者标记物对齐。
#import matplotlib.delaunay as md
from scipy import ndimage
from pylab import *
from numpy import *
import homography
def image_in_image(im1,im2,tp):
#该函数的输入参数为两幅图像和一个坐标,该坐标为将第一幅图像放置到第二幅图像中的角点坐标。
#使用仿射变换将im1放置在im2上,使m1图像的角和tp尽可能的靠近tp是齐次表示的,并且是按照从左上角逆时针计算的
m,n = im1.shape[:2]
#扭曲的点
fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
#计算仿射变换,并且将其应用于图像im1
H = homography.Haffine_from_points(tp,fp)
#Haffine_from_points()会返回给定点对应点对的最优仿射变换,在下面的例子中,对应点对为人像和风景的图的角点。
im1_t = ndimage.affine_transform(im1,H[:2,:2],
#AffineTransform 类表示 2D 仿射变换,它执行从 2D 坐标到其他 2D 坐标的线性映射,保留了线的“直线性”和“平行性”。
#可以使用一系列平移 (translation)、缩放 (scale)、翻转 (flip)、旋转 (rotation) 和错切 (shear) 来构造仿射变换。
(H[0,2],H[1,2]),im2.shape[:2])
alpha = (im1_t > 0)
return (1-alpha)*im2 + alpha*im1_t
def combine_images(im1,im2,alpha):
return (1-alpha)*im1 + alpha*im2
实验运行主代码为:
from PIL import Image
from pylab import *
import warp
im1 = array(Image.open('16.jpg').convert('L'))
im2 = array(Image.open('3.jpg').convert('L'))
tp = array([[1364,1838,1840,1364],[440,436,1005,1005],[1,1,1,1]])
#前面四组数据表示图像的纵坐标,后面的数据是横坐标
im3 = warp.image_in_image(im1,im2,tp)
figure()
gray()
imshow(im3)
axis('equal')
axis('off')
show()
实验结果为:
原图像分别是:
将图片2放到图片1上,效果显示为:
要是坐标找的不准,仿射就会出现问题,就像下面的结果一样:
出现这样的问题主要是坐标没有根据齐次坐标的规范来写,而且坐标之间的间隔不合理,在第三幅图中照片看起来很小,只要改变两个纵坐标直接的间距,稍微大一点,贴上去的照片也会变大。