cv2技术原理-仿射变换原理及手动实现


上一篇文章 图像旋转原理以及实现-手动实现cv2.getRotationMatrix2D的功能

1、仿射变换

仿射变换(Affine Transformation)是指在向量空间中进行一次线性变换(乘以一个矩阵)和一次平移(加上一个向量),变换到另一个向量空间的过程。
仿射变换代表的是两幅图之间的映射关系,仿射变换矩阵为2x3的矩阵,如下图中的矩阵M,其中的B起着平移的作用,而A中的对角线决定缩放,反对角线决定旋转或错切
变换矩阵
那么M矩阵该怎么计算出来呢?先留个悬念。。。
假设得到变换矩阵M之后该怎么使用呢?

2、变换矩阵M的使用

因为图像中像素点p的坐标可以使用(x,y)表示,变换之后该像素p的位置变换到了坐标(u ,v)。
图2
所以仿射变换是一种二维坐标(x,y)到二维坐标(u,v)之间的线性变换,其数学表达式如下:
数学表达
这个矩阵是2×3的,但是这会改变原始图像的维度,为此,增加一个维度,构造齐次变换矩阵3×3。
矩阵
这就保持了图像的‘平直性’和‘平行性’。
平直性:直线、圆弧不变
平行性:平行关系不变,直线相对位置不变,但是夹角可能会改变。

3、opencv实现

import numpy as np
import matplotlib.pyplot as plt
import cv2
path = "5a1672eb1027c.jpg"
img = cv2.imread(path)
def show_img(img):
    plt.figure(figsize=(10, 10))
    plt.imshow(img[:,:,::-1])
    plt.axis('off')
    plt.show()
height, width = img.shape[:2] 
# 在原图像和目标图像上各选择三个点  
mat_src = np.float32([[0, 0],[0, height-1],[width-1, 0]])  
mat_dst = np.float32([[0, 0],[100, height-100],[width-100, 100]]) 
# 得到变换矩阵  
mat_trans = cv2.getAffineTransform(mat_src, mat_dst) 
# 进行仿射变换  
dsts = cv2.warpAffine(img, mat_trans, (width,height)) 
# 显示  
imgs = np.hstack([img,dsts])  
show_img(imgs)
show_img(dsts)
print(mat_trans)

左边原图,右边结果
左边原图,右边结果。

同时得到了变换矩阵M:
M

4、主要步骤

从opencv的实现可以看出,主要有三步:

  1. 选取原始图像和目标图像中的各三个点(为什么是三个?稍后说)
  2. 使用cv2.getAffineTransform(mat_src, mat_dst)获得变换矩阵M
  3. 使用cv2.warpAffine函数,完成变换;
    为什么是这三个步骤呢?
    首先介绍变换矩阵M的计算吧:
    还记得这个公式吗?
    公式

5、手动实现

我们要求的就是变换矩阵M,每个方程3个未知数,所以需要三个点解三个未知数。
我们的图像高宽分别为height=1080 ,width=1920。
代码中我们在原始图像中选了三个点[0,0]、[0,1079]、[1919,0]。
代码中我们在目标图像中选了三个点[0,0]、[100,979]、[1819,100]。
原始图像上的点和目标图像上的点一一对应。
得到方程:
0=a1×0+b1×0+c1
100=a1×0+b1×1079+c1
1819=a1×1919+b1×0+c1
解得a1= 0.9479,b1=0.09267,c1=0
0=a2×0+b2×0+c2
979=a2×0+b2×1079+c2
100=a2×1919+b2×0+c2
解得a2=0.05211,b2=0.9073,c2=0
与opencv的输出结果略有差异,opencv应该是做了特殊处理。

5.1、使用M进行变换

mat_trans = np.array([[0.9479,0.09267,0],[0.05211,0.9073,0]])
out_img = np.zeros(img.shape,dtype=np.uint8)
for i in range(width):
    for j in range(height):
        # print(imgs[i,j,:])
        ori = np.array([[i],[j],[1]])
        dst = np.dot(mat_trans,ori).astype(int)
        if dst[1][0]>=height:
            dst[1][0] = height-1
        if dst[0][0]>=width:
            dst[0][0] = width-1
        out_img[dst[1][0],dst[0][0],:] = img[j,i,:]
imgs = np.hstack([out_img,dsts])  #dsts为opencv输出的结果
show_img(imgs)

左边为手动实现的效果,右边为opencv结果
左边为手动实现的效果,右边为opencv结果。

输出结果对比发现,实现基本正确。
再解释一下上面三个点的直观解释就是把下图中的红点拉到绿点,然后图像的其余点跟着进行线性变换。
直观解释
粗浅理解,如有问题,请批评指正。
感谢参考文献的作者。
参考:图像的仿射变换:cv2.warpAffine()

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值