单应性变换、仿射变换

一、 单应性变换homography

1 概念

如果场景是平面,或者近似平面,或者低视差时,我们能应用单应性矩阵(homography)

单应性变换是将一个平面内的点映射到另一个平面内的二维投影变换。在这里,平面是指图像或者三维中的平面表面。
单应性变换、仿射变换
如图,两张图片中红色的点叫做对应点对。
单应性变换的目标是通过给定的几个点(通常是4对点)来得到单应性矩阵。
用一个3x3的矩阵来表示单应性,可以写成
单应性变换、仿射变换
考虑第一组对应点 (X1,Y1) 在第一张图像和 (X2,Y2)第二张图像中。然后,Homography H 以下列方式映射它们
单应性变换、仿射变换
坐标变换:
单应性变换、仿射变换
要得到两张图片的H矩阵,就必须至少知道4个相同对应位置的点,通常,这些点对应是通过匹配图像之间的SIFT或Harris等特征自动找到的

单应性变换、仿射变换

二、仿射变换

1. 定义

仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,旋转。也就是说,当两幅图像存位移差,就可以通过对应的参数算出平移量,就可实现两张图片的重叠。
由于仿射变换具有6个*度,因此需要三个对应点对来估计矩阵 H
单应性变换、仿射变换
单应性变换、仿射变换
所以所要解决的问题就是如何求解(xt,yt)

2 原理

单应性变换、仿射变换
通过sift算法或其他匹配算法找出两幅图像的对应点以及对应点的坐标,但是问题所在与怎么求出一堆对应点的位移差呢
假设第i个特征点的位移量为
单应性变换、仿射变换
单应性变换、仿射变换
由于方程数量与大于未知量,所以这里我们采用最小二乘解来求解。
最小二乘法通过最小化误差的平方和寻找数据的最佳函数匹配。
给定任意的像素值点(Xi,Yi),
单应性变换、仿射变换
定义映射残差为
单应性变换、仿射变换
目标是:残差平方和最小化
单应性变换、仿射变换
最小二乘法采用的手段是尽量使得等号两边的方差最小,也就是找出这个函数的最小值。

仿射变换最小二乘法

  景物在成像过程中产生的扭曲,会使图像的比例失调,可用仿射变换来校正畸变。而仿射变换的参数可以用最小二乘法进行估算。
 设原图像为f(x,y),畸变后的图像为F(X’,Y’),要将F(X’,Y’)恢复为f(x,y),就是要找到(X’,Y’)坐标与(x,y)坐标的转换关系,这个转换关系称为坐标变换,表示为(x,y)=T(X’,Y’)。
景物在成像过程中产生的扭曲,会使图像的比例失调,可用仿射变换来校正各种畸变。 
 先计算出坐标变换的系数,仿射变换的表达式为:R(x)=Px+Q, x=(x,y)是像素的平面位置,P是2x2的旋转矩阵,Q是2x1的平移向量,P、Q即为仿射变换参数,即:
     x= AX’ + BY’ + C
    y= DX’ + EY’ + F
 因此,几何畸变的校正归根结底为坐标转换系数A,B,C,D,E,F的求解。
 在这个过程中,可能会有空像素的存在,这是因为图片在映射时,可能有两个或以上的像素映射到同一点上,此时这个点周围的像素就会出现空像素的现象,即会出现黑线。
 如何避免这种现象呢,使用双线性差值,再反向映射。
 反向映射:对于 g(x’)中的每个像素 x’,根据变换模型计算相应的映射坐标 x = h-1(x’),并将x的像素值赋给g(x’)。`

三、 alpha通道

 alpha通道是一个8位的灰度通道,该通道用256级灰度来记录图像中的透明度信息,定义透明、不透明和半透明区域,其中白表示不透明,黑表示透明,灰表示半透明。
叠加到原图的方法是,就是去除要叠加的图片的对应位置的像素值,和底层图像的像素值进行混合。混合的方法有很多种,但是要注意alpha通道的值要转化为float类型,这个过程为归一化,最终产生的图像还是3通道的。

四、 实现

 # -*- coding: utf-8 -*-
from PCV.geometry import warp, homography
from PIL import  Image
from pylab import *
from scipy import ndimage

# example of affine warp of im1 onto im2
im1 = array(Image.open('../data/JMU/02.jpg').convert('L'))
im2 = array(Image.open('../data/JMU/03.jpg').convert('L'))
# set to points
tp = array([[120,260,260,120],[16,16,305,305],[1,1,1,1]])
#tp = array([[675,826,826,677],[55,52,281,277],[1,1,1,1]])
im3 = warp.image_in_image(im1,im2,tp)
figure()
gray()
subplot(141)
axis('off')
imshow(im1)
subplot(142)
axis('off')
imshow(im2)
subplot(143)
axis('off')
imshow(im3)

# set from points to corners of im1
m,n = im1.shape[:2]
fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
# first triangle
tp2 = tp[:,:3]
fp2 = fp[:,:3]
# compute H
H = homography.Haffine_from_points(tp2,fp2)
im1_t = ndimage.affine_transform(im1,H[:2,:2],
(H[0,2],H[1,2]),im2.shape[:2])
# alpha for triangle
alpha = warp.alpha_for_triangle(tp2,im2.shape[0],im2.shape[1])
im3 = (1-alpha)*im2 + alpha*im1_t
# second triangle
tp2 = tp[:,[0,2,3]]
fp2 = fp[:,[0,2,3]]
# compute H
H = homography.Haffine_from_points(tp2,fp2)
im1_t = ndimage.affine_transform(im1,H[:2,:2],
(H[0,2],H[1,2]),im2.shape[:2])
# alpha for triangle
alpha = warp.alpha_for_triangle(tp2,im2.shape[0],im2.shape[1])
im4 = (1-alpha)*im3 + alpha*im1_t
subplot(144)
imshow(im4)
axis('off')
show()

单应性变换、仿射变换
单应性变换、仿射变换
单应性变换、仿射变换
通过实验结果可以看到,图片1映射在图片2上,两幅大小不同图片合成,且在另一幅上扣除一块区域合成。这里需要注意的就是先从一副图上取出那块区域生成一个小的图,然后目标图片指定贴上去的区域。如果是两幅图片大小尺寸不一样的,就会在图片上裁去一部分。
由于叠加的图像大小不一定相等,比如载入一张png,和一张jpg,原先在png里面是透明的地方,现在成了黑色,可见是透明的地方被认为是值0。