在图像映射章节初始,老师向我们介绍了图像之间变换的几种形式,其中包括平移、旋转、尺度变换、仿射变换、透视变换等。而对图像块应用仿射变换称之为图像扭曲或者仿射扭曲,仿射变换的一个简单例子是能够将图像或图像的一部分放置在另一幅图像中,使他们能够和指定的区域或者标记物对齐。扭曲操作可以使用 SciPy 工具包中的 ndimage 包完成。
仿射变换:它相当于一个平移变换和一个非均匀变换的复合,用A矩阵表示,A为3×3矩阵,A={{a11,a12,tx},{a21,a22,ty},{0,0,1}} 其中A可以分解为:A=R(a)R(-b)DR(b),其中D={{c1,0},{0,c2}},左上角2×2矩阵为旋转部分,tx和ty为平移因子,它有6个自由度,即旋转4个,x方向平移,y方向平移。他能保持平行性,不能保持垂直性,Image中各部分变换前后面积比保持不变,共线线段或者平行线段的长度比保持不变,矢量的线性组合不变。面积被缩放了c1*c2=detA倍。
仿射变换参数( t )求解时,可以用等式两边同时乘上已知矩阵的转置矩阵的方法将往往不可逆的矩阵变换求解:
A t = b
AT A t = AT b
t = ( AT A ) -1 AT b
放置图像的过程中,使用 warp.py 文件中的 image_in_image() 函数可以将目标图像中的角点坐标作为输入参数将第一幅扭曲后的图像与目标图像融合成了 Alpha 图像,扭曲的图像是在扭曲区域边界之外用 0 填充的图像,因此可以得到放置后的覆盖效果,而当前我们处理过程中的角点坐标是通过查看绘制好的图像人为手工确定的。
Alpha是一个8位的灰度图像通道,该通道用256级灰度来记录图像中的透明度信息,定义透明、不透明和半透明区域,其中黑表示透明,白表示不透明,灰表示半透明。
代码如下:
# -*- 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('./images/1.jpg').convert('L'))
im2 = array(Image.open('./images/2.jpg').convert('L'))
# set to points
tp = array([[800, 1400, 1400, 800], [450, 450, 750, 800], [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')
figure()
imshow(im2)
# subplot(143)
# axis('off')
figure()
imshow(im3)
warp.py 当中的 image_in_image() 函数如下:
def image_in_image(im1,im2,tp):
""" Put im1 in im2 with an affine transformation
such that corners are as close to tp as possible.
tp are homogeneous and counter-clockwise from top left. """
# 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
原书中的代码在我的环境中直接运行会出现报错:
要将 warp.py 中首行导入语句修改为:
from scipy.spatial import Delaunay
(上述代码中已修改)
使用的两张原图:
融合后的结果:
代码 image_in_image() 函数中:
shape 方法的功能是读取矩阵的长度,比如shape[0]就是读取矩阵第一维度的长度。它的输入参数可以使一个整数表示维度,也可以是一个矩阵。
Haffine_from_points() 会返回给定对应点对的最优仿射变换,适用于透视效应比较弱的情况下。