计算机视觉学习3_图片映射(单应性变换)_图片中的图片

图片中的图片
在这里插入图片描述
我们的目标是将im1贴到im2上。

主代码部分:

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

# example of affine warp of im1 onto im2
im1 = array(Image.open('../mydata/ch3_3.jpg').convert('L'))
im2 = array(Image.open('../mydata/ch3_1.jpg').convert('L'))

# set to points 选定一些目标点
tp = array([[90,180,180,90],[55,55,150,150],[1,1,1,1]])

im3 = warp.image_in_image(im1,im2,tp)
# im3是我们希望输出的图片,即映射后的结果图
figure()
gray()
subplot(141)
axis('off')
imshow(im1)
subplot(142)
axis('off')
imshow(im2)
subplot(143)
axis('off')
imshow(im3)
plt.imshow(im3)
plt.show()

实验结果:
在这里插入图片描述

代码及原理分析:
下面是代码的关键部分的详细解释以及原理解释

知识点:坐标点

tp = array([[90,180,180,90],[55,55,150,150],[1,1,1,1]])
fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
# 它们处于两个地方,这里为了方便,我把它们放在一起

这里 tp 为 映射目标坐标,即图片2中想要被贴上的位置的 4个点形成的矩阵
在这里插入图片描述
知识点:实现image_in_image

def image_in_image(im1,im2,tp):
    """ 使用仿射变换将 im1 放置在 im2 上,使 im1 图像的角和 tp 尽可能的靠近    
    	tp 是齐次表示的,并且是按照从左上角逆时针计算的
        """

    # 扭曲的点
    m,n = im1.shape[:2]
    # im1为二维矩阵尺寸 m=im1.shape[1] 为第一维的长度,n=im2.shape[0] 为第二维的长度
    fp = array([[0,m,m,0],[0,0,n,n],[1,1,1,1]])
    # 四个边角的坐标 fp当前坐标 tp映射坐标

	# 计算仿射变换,并且将其应用于图像 im1
    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)   # 将im1_t求出的映射区域找出 
    #  alpha通道 1前景 0背景 做加法运算 点对点相加        
    return (1-alpha)*im2 + alpha*im1_t  #  alpha通道 1前景 0背景 做加法运算 点对点相加       

这里我做了详细的注释

知识点:单应性矩阵
image_in_image方法中的 H = homography.Haffine_from_points(tp,fp)
这里,调用到homography.py的Haffine_from_points方法

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)    #  mean平均值  axis=1,输出矩阵是1列,求每一行的平均(按照每一列去求平均)
 	maxstd = max(std(fp[:2], axis=1)) + 1e-9   # max 最大值 /std 计算标准差

    C1 = diag([1/maxstd, 1/maxstd, 1])   # diag(参数1:v,主对角线数值,参数 2:k,对角线元素) 创建对角矩阵
    C1[0][2] = -m[0]/maxstd
    C1[1][2] = -m[1]/maxstd
    fp_cond = dot(C1,fp)  # dot()函数 对于二维数组,执行矩阵乘法运算

    # 映射对应点
    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)

    # 求解 H
    # 因为归一后点的均值为0,所以平移量为0
    A = concatenate((fp_cond[:2],tp_cond[:2]), axis=0)   # concatenate()数组拼接 按轴axis连接array组成一个新的array
 	
 	U, S, V = linalg.svd(A.T)
    # 假设我们的矩阵A是一个m×n的矩阵
    # 求AA ^ {T}的特征值和特征向量,用单位化的特征向量构成 U   U是一个m×m的矩阵
    # 求 A ^ {T} A的特征值和特征向量,用单位化的特征向量构成V   V是一个n×n的矩阵
    # 将AA ^ {T}或者 A ^ {T}A 的特征值求平方根,然后构成S   s是对矩阵a的奇异值分解
   
    # 创建B和C矩阵
    tmp = V[:2].T
    # X.T 求矩阵X的逆矩阵 矩阵求逆,转置
    B = tmp[:2]
    C = tmp[2:4]

    #  数组拼接concatenate
    tmp2 = concatenate((dot(C,linalg.pinv(B)),zeros((2,1))), axis=1)
    # vstack()按列合并,即增加行数
    H = vstack((tmp2,[0,0,1]))

    # decondition
    # 反归一化
    H = dot(linalg.inv(C2),dot(H,C1))
    # 返回归一化
    return H / H[2,2]

它的作用是——返回给定点对的最优仿射变换
调用这个方法(代码中有详细注释),我们得到单应性矩阵 H
这里补充一下单应性矩阵的知识,关于矩阵计算部分不是本文重点,因此未涉及。
在这里插入图片描述
在这里插入图片描述

知识点: 仿射矩阵

im1_t = ndimage.affine_transform(im1, H[:2,:2], (H[0,2],H[1,2]), im2.shape[:2])

H为单应性矩阵 im1_t为仿射矩阵
warp.py的image_in_image方法
通过scipy.ndimage.affine_transform可以实现图像的仿射变换

affine_transform(input, matrix, offset=0.0, output_shape=None, output=None, order=3, mode=‘constant’, cval=0.0, prefilter=True)

第一个参数代表处理的图像即原图像,
第二个参数是旋转矩阵,矩阵必须是二维的,或者也可以作为一维序列或阵列给出。在后一种情况下,假设矩阵是对角线的。然后应用更有效的算法,利用问题的可分离性。
第三个参数是平移(纵向,横向)应用变换的数组的偏移量。如果浮点数,每个轴的偏移量相同。如果序列,offset应包含每个轴的一个值,
第四个参数是output_shape:整数元组,可选 形状元组。

知识点:alpha通道

alpha = (im1_t > 0)
# 将im1_t求出的映射区域找出
#  alpha通道 1前景 0背景 做加法运算 点对点相加

return (1-alpha)*im2 + alpha*im1_t
 # 新建了一个和  im2完全一样大的图片 其实是两张图片
# 做加法运算 点对点相加 alpha来确定是否处于变换范围内 做叠加操作
# 返回的即为im3

我们利用alpha通道,将扭曲的图像和第二幅图像融合,

该图像定义了每个像素从各个图像中获取的像素值成分多少。这里我们基于以下事实,扭曲的图像是在扭曲区域边界之外以 0 来填充的图像,来创建一个二值的 alpha 图像。严格意义上说,我们需要在第一幅图像中的潜在 0 像素上加上一个小的数值,或者合理地处理这些 0 像素。

alpha通道的值取值范围是0-255。0表示完全透明,255表示完全不透明。
我们将其将alpha通道值取值范围由0-255转换到0-1
alpha=1 当前图片完全不透明 得到前景(即当前图片)
alpha=0 当前图片完全透明 得到背景图

参考博客:
https://blog.csdn.net/kbccs/article/details/82590032

PS:代码中的小问题

问题一:imshow(im3)未显示图片
解决:https://blog.csdn.net/alickr/article/details/72804258
修改为
plt.imshow(im3)
plt.show()

问题二:import matplotlib.delaunay as md 报错 ModuleNotFoundError: No module named ‘matplotlib.delaunay’
解决:https://blog.csdn.net/qq_33363973/article/details/80181950
修改为
from scipy.spatial import Delaunay as md

猜你喜欢

转载自blog.csdn.net/zxm_jimin/article/details/88670234
今日推荐