今天学习了仿射变换,将一张图片放置到另一张图片,感觉十分有趣,所以写了这篇博客,与大家一起分享一下!
一、仿射变换
1.什么是仿射变换
2.alpha通道
3.仿射变换的求解
二、仿射变换的实现
1.主函数
2.主要函数的介绍
(1)放射变换的定义
仿射变换是将一个平面的点映射到另一个平面内的二维投影。仿射变换保持了二维图形的“平直性”,即原来是直线的地方还是直线。仿射变换具有很强的实用性,比如图像配准、图像纠正和纹理纠正,以及创建全景图像等。仿射变换的效果如下图所示:
(2)仿射变换的数学表示
仿射变换是一种二维坐标(x, y)到二维坐标(u, v)的线性变换,其数学表达式形式如下:
对应的齐次坐标矩阵表示形式为:
其中,取、为0,为1。
坐标变换:
实现将一张图片放置到另一张图片上去,其实就是已知和求,而中的3个变量已知,我们只需求另外6个变量即可。
(3)仿射变换的几何意义
仿射变换可以理解成一系列的原子变换复合实现的,具体暴扣:平移、旋转、尺度变换等。
a.平移
数学形式:
矩阵形式:
平移变换包含两个未知量,即和。计算两个图形的平移量,其实就是求这两个未知量。
b.旋转
矩阵形式:
c.尺度变换
矩阵形式:
d.刚体运动:旋转平移缩放
矩阵形式:
alpha用来衡量一个像素或图像的透明度。在非压缩的32位RGB图像中,每个像素是由四个部分组成:一个alpha通道和三个颜色分量(R、G和B)。当Alpha值为0时,该像素是完全透明的,而当Alpha值为255时,则该像素是完全不透明。
alpha用于图像m1经过仿射变换后,将图像m1放置到图像m2时,只有alpha大于0的区域才能放置到图像m2上,alpha为0的区域在融合的时候就变成透明了。
(1)流程
a.输入两张图像m1和m2
b.设置所需要放置的区域矩阵tp
c.获取m1的宽m和高n,并设置原图区域矩阵为fp
d.根据fp和tp计算仿射变换参数矩阵H
e.将m1放置到m2上去
(2)仿射变换参数矩阵H的求解
放射变换求解的关键就是H的求解,我们已知原图区域矩阵fp和需要放置的tp,需要求得仿射变换参数矩阵H。
我们利用最小二乘法求解H,其基本步骤如下:
a.检查tp和fp大小是否一致,若不一致则报错
b.对fp进行归一化
c.对tp进行归一化
d.利用SVD进行奇异值分解,剔除异常值,求得H
# -*- coding: utf-8 -*-
from PCV.geometry import warp, homography
from PIL import Image
from pylab import *
from scipy import ndimage
# im1仿射到im2的例子
#打开两张图片
im1 = array(Image.open('D:/test/beatles.jpg').convert('L'))
im2 = array(Image.open('D:/test/billboard_for_rent.jpg').convert('L'))
# 选定一些目标点,数组tp表示目标区域,三行分别代表y、x和齐次,其中区域按从左上角逆时针旋转,
tp = array([[120,260,260,120],[16,16,305,305],[1,1,1,1]])#将一些目标点存储到数组中
im3 = warp.image_in_image(im1,im2,tp) #使用仿射变换将m1放置到m2
figure()
gray()
subplot(141)
axis('off')
imshow(im1)
subplot(132)
axis('off')
imshow(im2)
subplot(133)
axis('off')
imshow(im3)
show()
(1)wrap.image_in_image(im1,im2,tp)
函数的意义是通过方式变换将图像m1放置到图像m2的指定区域tp中
def image_in_image(im1,im2,tp):
"""使用仿射变换将m1放置到m2,使得m1图像的角和tp尽可能的靠近。
tp是齐次表示的,并且按照从左上角逆时针计算的"""
# 扭曲的点
m,n = im1.shape[:2]#获取m1的高m宽n
fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
#计算仿射变换,并且将其应用于图像im1
H = homography.Haffine_from_points(tp,fp)#返回给定点对的最优仿射变换求H
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
(2)homography.Haffine_from_points(tp,fp)
函数的意义是通过原图像矩阵fp和仿射变换后的图像矩阵tp求得H
def Haffine_from_points(fp,tp):
"""计算H,仿射变换,使得tp是fp经过仿射变换H得到的"""
if fp.shape != tp.shape:
raise RuntimeError('number of points do not match')
# 对点进行归一化
# 映射起始点
m = mean(fp[:2], axis=1)#取fp的前两行,压缩列求平均值
maxstd = max(std(fp[:2], axis=1)) + 1e-9 #取fp的前两行,压缩列求标准差,取最大值
C1 = diag([1/maxstd, 1/maxstd, 1])
C1[0][2] = -m[0]/maxstd
C1[1][2] = -m[1]/maxstd
fp_cond = dot(C1,fp)
# 映射对应点
m = mean(tp[:2], axis=1)
C2 = C1.copy() #两个点集,必须都进行相同的缩放
C2[0][2] = -m[0]/maxstd
C2[1][2] = -m[1]/maxstd
tp_cond = dot(C2,tp)
#因为归一化后点的均值为0,因此平移为0
A = concatenate((fp_cond[:2],tp_cond[:2]), axis=0)#数组拼接
U,S,V = linalg.svd(A.T)#奇异值分解,用小的多的数据集来表示原始数据集
#创建B和C矩阵
tmp = V[:2].T
B = tmp[:2]
C = tmp[2:4]
#B的伪逆矩阵和C逐元素相乘,与zero(2,1)拼接
tmp2 = concatenate((dot(C,linalg.pinv(B)),zeros((2,1))), axis=1)
H = vstack((tmp2,[0,0,1]))#数组合并
# 反归一化
H = dot(linalg.inv(C2),dot(H,C1))
return H / H[2,2]
效果如下图所示:
左边为图像m1,中间为图像m2,右边为仿射变换后的效果图。我们可以清楚的看到m1放到了m2的指定区域,实现了两张图片的融合。