Datawhale OpenCV -Task2 几何变换 小白学习笔记

文章目录

    • 2.1 简介
    • 2.2 算法理论介绍
      • 2.2.1 变换形式
      • 2.2.2 坐标系变换
      • 2.2.3 图像坐标系与笛卡尔坐标系转换关系
      • 个人的理解
    • 2.3 代码实现
      • 2.3.1 缩放变换
      • 2.3.2 平移变换
      • 2.3.3 旋转变换
      • 2.3.4 仿射变换
      • 2.3.5 透视变换
    • 2.3 小结
    • 2.4 参考资料

2.1 简介

该部分将对基本的几何变换进行学习,几何变换的原理大多都是相似,只是变换矩阵不同,因此,我们以最常用的平移和旋转为例进行学习。在深度学习领域,我们常用平移、旋转、镜像等操作进行数据增广;在传统CV领域,由于某些拍摄角度的问题,我们需要对图像进行矫正处理,而几何变换正是这个处理过程的基础,因此了解和学习几何变换也是有必要的。

这次我们带着几个问题进行,以旋转为例:

  1. 变换的形式(公式)是什么?
  2. 旋转中心是什么?毕竟以不同位置为旋转中心得到的结果是不一样的。
  3. 采用前向映射还是反向映射?(反向映射更为有效)
  4. 采用反向映射后,采用何种插值算法?最常用的的是双线性插值,OpenCV也是默认如此。

2.2 算法理论介绍

2.2.1 变换形式

先看第一个问题,变换的形式。与OpencV不同的是这里采取冈萨雷斯的《数字图像处理_第三版》的变换矩阵方式,关于OpenCV的策略可以看它的官方文档。根据冈萨雷斯书中的描述,仿射变换的一般形式如下:

[ x y 1 ] = [ v w 1 ] T = [ v w 1 ] [ t 11 t 12 0 t 21 t 22 0 t 31 t 32 1 ] \begin{bmatrix} x & y & 1 \end{bmatrix} = \begin{bmatrix} v & w & 1 \end{bmatrix} T = \begin{bmatrix} v & w & 1 \end{bmatrix} \begin{bmatrix} t_{11} & t_{12} & 0 \\ t_{21} & t_{22} & 0 \\ t_{31} & t_{32} & 1 \end{bmatrix} [xy1]=[vw1]T=[vw1]t11t21t31t12t22t32001

式中的T就是变换矩阵,其中 (v,w)为原坐标,(x,y) 为变换后的坐标,不同的变换对应不同的矩阵,这里也贴出来吧,一些常见的变换矩阵及作用如下表:

变换名称 仿射变换矩阵T 坐标公式
恒等变换 [ 1 0 0 0 1 0 0 0 1 ] \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} 100010001 { x = v y = w \begin{cases} x = v \\ y = w \end{cases} {x=vy=w
尺度变换 [ c x 0 0 0 c y 0 0 0 1 ] \begin{bmatrix} c_x & 0 & 0 \\ 0 & c_y & 0 \\ 0 & 0 & 1 \end{bmatrix} cx000cy0001 { x = v c i y = w c y \begin{cases} x = vc_i \\ y = wc_y \end{cases} {x=vciy=wcy
旋转变换(以逆时针为正) [ cos ⁡ θ sin ⁡ θ 0 − sin ⁡ θ cos ⁡ θ 0 0 0 1 ] \begin{bmatrix} \cos\theta & \sin\theta & 0 \\ -\sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} cosθsinθ0sinθcosθ0001 { x = v cos ⁡ θ − w sin ⁡ θ y = v sin ⁡ θ + w cos ⁡ θ \begin{cases} x = v\cos\theta - w\sin\theta \\ y = v\sin\theta + w\cos\theta \end{cases} {x=vcosθwsinθy=vsinθ+wcosθ
旋转变换(以顺时针为正) [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} cosθsinθ0sinθcosθ0001 { x = v cos ⁡ θ + w sin ⁡ θ y = − v sin ⁡ θ + w cos ⁡ θ \begin{cases} x = v\cos\theta + w\sin\theta \\ y = -v\sin\theta + w\cos\theta \end{cases} {x=vcosθ+wsinθy=vsinθ+wcosθ
平移变换 [ 1 0 0 0 1 0 t x t y 1 ] \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ t_x & t_y & 1 \end{bmatrix} 10tx01ty001 { x = v + t x y = w + t y \begin{cases} x = v + t_x \\ y = w + t_y \end{cases} {x=v+txy=w+ty
偏移变换(水平) [ 1 0 0 s h 1 0 0 0 1 ] \begin{bmatrix} 1 & 0 & 0 \\ s_h & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} 1sh0010001 { x = v + w s h y = w \begin{cases} x = v + w s_h \\ y = w \end{cases} {x=v+wshy=w
偏移变换(垂直) [ 1 s v 0 0 1 0 0 0 1 ] \begin{bmatrix} 1 & s_v & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} 100sv10001 { x = v y = v s v + w \begin{cases} x = v \\ y = vs_v + w \end{cases} {x=vy=vsv+w

也就是说,我们根据自己的目的选择不同变换矩阵就可以了。

2.2.2 坐标系变换

再看第二个问题,变换中心,对于缩放、平移可以以图像坐标原点(图像左上角为原点)为中心变换,这不用坐标系变换,直接按照一般形式计算即可。而对于旋转和偏移,一般是以图像中心为原点,那么这就涉及坐标系转换了。

我们都知道,图像坐标的原点在图像左上角,水平向右为 X 轴,垂直向下为 Y 轴。数学课本中常见的坐标系是以图像中心为原点,水平向右为 X 轴,垂直向上为 Y 轴,称为笛卡尔坐标系。看下图

因此,对于旋转和偏移,就需要3步(3次变换):

  1. 将输入原图图像坐标转换为笛卡尔坐标系;
  2. 进行旋转计算。旋转矩阵前面已经给出了;
  3. 将旋转后的图像的笛卡尔坐标转回图像坐标。

2.2.3 图像坐标系与笛卡尔坐标系转换关系

在图像中我们的坐标系通常是AB和AC方向的,原点为A,而笛卡尔直角坐标系是DE和DF方向的,原点为D。
令图像表示为M×N的矩阵,对于点A而言,两坐标系中的坐标分别是(0,0)和(-N/2,M/2),则图像某像素点(x’,y’)转换为笛卡尔坐标(x,y)转换关系为,x为列,y为行:

x = x ′ − N / 2 y = − y ′ + M / 2 x = x' - N/2 \\ y = -y' + M/2 x=xN/2y=y+M/2
逆变换:
x ′ = x + N / 2 y ′ = − y + M / 2 x' = x + N/2 \\ y' = -y + M/2 x=x+N/2y=y+M/2

于是,根据前面说的3个步骤(3次变换),旋转(顺时针旋转)的变换形式就为,3次变换就有3个矩阵:

( x , y , 1 ) = ( x ′ , y ′ , 1 ) T = ( x ′ , y ′ , 1 ) [ 1 0 0 0 − 1 0 − 0.5 ∗ N 0.5 ∗ M 1 ] [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] [ 1 0 0 0 − 1 0 0.5 ∗ N 0.5 ∗ M 1 ] (x,y,1) = (x',y',1)T = (x',y',1) \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ -0.5*N & 0.5*M & 1 \end{bmatrix} \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0.5*N & 0.5*M & 1 \end{bmatrix} (x,y,1)=(x,y,1)T=(x,y,1)100.5N010.5M001cosθsinθ0sinθcosθ0001100.5N010.5M001

个人的理解

(1) 图像像素点(x’,y’) ——> 笛卡尔坐标(x,y)
- 已知条件:

{ x ′ = x + N / 2 y ′ = − y + M / 2 \begin{cases} x' = x + N/2 \\ y' = -y + M/2 \end{cases} {x=x+N/2y=y+M/2

[ c x 0 0 0 c y 0 0 0 1 ] { x = v c i y = w c y \begin{bmatrix} c_x & 0 & 0 \\ 0 & c_y & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{cases} x = vc_i \\ y = wc_y \end{cases} cx000cy0001{x=vciy=wcy
[ 1 0 0 0 1 0 t x t y 1 ] { x = v + t x y = w + t y \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ t_x & t_y & 1 \end{bmatrix} \begin{cases} x = v + t_x \\ y = w + t_y \end{cases} 10tx01ty001{x=v+txy=w+ty

  • 得出:
    ( x , y , 1 ) = ( x ′ , y ′ , 1 ) T = ( x ′ , y ′ , 1 ) [ 1 0 0 0 − 1 0 N / 2 M / 2 1 ] (x,y,1)=(x',y',1)T=(x',y',1) \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ N/2 & M/2 & 1 \end{bmatrix} (x,y,1)=(x,y,1)T=(x,y,1)10N/201M/2001

(2) 旋转计算(顺时针)
- 已知条件:

[ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] { x = v cos ⁡ θ + w sin ⁡ θ y = − v sin ⁡ θ + w cos ⁡ θ \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{cases} x = v\cos\theta + w\sin\theta \\ y = -v\sin\theta + w\cos\theta \end{cases} cosθsinθ0sinθcosθ0001{x=vcosθ+wsinθy=vsinθ+wcosθ

  • 得出:
    ( x , y , 1 ) = ( x ′ , y ′ , 1 ) T = ( x ′ , y ′ , 1 ) [ 1 0 0 0 − 1 0 N / 2 M / 2 1 ] [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] (x,y,1)=(x',y',1)T=(x',y',1) \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ N/2 & M/2 & 1 \end{bmatrix} \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} (x,y,1)=(x,y,1)T=(x,y,1)10N/201M/2001cosθsinθ0sinθcosθ0001

(3) 笛卡尔坐标 ——> 原图像坐标
- 已知:
{ x = x ′ − N / 2 y = y ′ + M / 2 \begin{cases} x = x' - N/2 \\ y = y' + M/2 \end{cases} {x=xN/2y=y+M/2
[ c x 0 0 0 c y 0 0 0 1 ] { x = v c i y = w c y \begin{bmatrix} c_x & 0 & 0 \\ 0 & c_y & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{cases} x = vc_i \\ y = wc_y \end{cases} cx000cy0001{x=vciy=wcy
[ 1 0 0 0 1 0 t x t y 1 ] { x = v + t x y = w + t y \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ t_x & t_y & 1 \end{bmatrix} \begin{cases} x = v + t_x \\ y = w + t_y \end{cases} 10tx01ty001{x=v+txy=w+ty

  • 得出:
    ( x , y , 1 ) = ( x ′ , y ′ , 1 ) T = ( x ′ , y ′ , 1 ) [ 1 0 0 0 − 1 0 N / 2 M / 2 1 ] [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] [ 1 0 0 0 − 1 0 − N / 2 M / 2 1 ] (x,y,1)=(x',y',1)T=(x',y',1) \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ N/2 & M/2 & 1 \end{bmatrix} \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ -N/2& M/2 & 1 \end{bmatrix} (x,y,1)=(x,y,1)T=(x,y,1)10N/201M/2001cosθsinθ0sinθcosθ000110N/201M/2001

2.3 代码实现

import cv2
import numpy as np
from matplotlib import pyplot as plt

2.3.1 缩放变换

img = cv2.imread('photo.jpg',1)

res1=cv2.resize(img,None,fx=1,fy=1,interpolation= cv2.INTER_CUBIC)

cv2.imshow('res1',res1)
cv2.waitKey(0)

2.3.2 平移变换

rows,cols,channel = img.shape

M = np.float32([[1,0,100],[0,1,50]])
dst = cv2.warpAffine(img,M,(cols,rows))

cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 沿(x,y)方向移动,移动距离为 ( t x , t y ) (t_x,t_y) (tx,ty),tx:水平移动:正数右移,负数左移;ty:垂直移动,正数上移,负数下移。构建移动矩阵:

M = [ 1 0 t x 0 1 t y ] M = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \end{bmatrix} M=[1001txty]

  • M是使用Numpy数组构建数据类型为np.float32的矩阵。

  • 函数cv2.warpAffine()是放射变换函数,可实现选择,平移,缩放;变换后的直线依旧平行。
    第三个参数是输出图像的大小,格式为图像的(宽,高):图像的宽是对应列数,高是对应行数。

2.3.3 旋转变换

M = cv2.getRotationMatrix2D((cols/2,rows/3),90,0.4)
dst = cv2.warpAffine(img,M,(cols,rows))

cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 对一个图像选择角度 θ \theta θ,使用旋转矩阵: M = [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] M = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} M=[cosθsinθsinθcosθ]
  • OpenCV允许在任意地方选择,因此选择矩阵又为: [ α β ( 1 − α ) ∗ c e n t e r . x − β ∗ c e n t e r . y − β α β ∗ c e n t e r . x + ( 1 − α ) ∗ c e n t e r . x ] \begin{bmatrix} \alpha & \beta & (1 - \alpha)*center.x - \beta* center.y \\ -\beta & \alpha & \beta * center.x+ (1 - \alpha )* center.x \end{bmatrix} [αββα(1α)center.xβcenter.yβcenter.x+(1α)center.x],其中 α = s c a l e ∗ cos ⁡ θ \alpha = scale * \cos \theta α=scalecosθ(scale:缩放尺度)
  • 函数cv2.getRotationMatrix2D():第一个参数是选择中心,第二个是选择角度,第三个是选择后缩放的因子。
    可以通过设置旋转中心,缩放因子以及窗口大小来防止旋转后超边界的问题。

2.3.4 仿射变换

pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[10,100],[200,50],[100,250]])

M = cv2.getAffineTransform(pts1,pts2)

dst = cv2.warpAffine(img,M,(cols,rows))

plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

Datawhale OpenCV -Task2 几何变换 小白学习笔记_第1张图片

  • 仿射变换是一种二维坐标到二维坐标间的线性变换。原图中所有平行线在结果图像中保持平行。
  • 为了创建这个矩阵,要从原图像中找到三个点以及他们在输出图像中的位置。
  • 函数cv2.getAffineTransform():创建 2 × 3 2\times3 2×3的矩阵。第一个参数,原图像中的三个点坐标,第二个参数是变换后三个点的坐标。

2.3.5 透视变换

pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])

M = cv2.getPerspectiveTransform(pts1,pts2)

dst = cv2.warpPerspective(img,M,(300,300))

plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

Datawhale OpenCV -Task2 几何变换 小白学习笔记_第2张图片

  • 透视变换需3$\times$3的变换矩阵,直线变换前后都保持直线。
  • 原图象中找4个点,对应输出图像中的4个点。并且要求4个点中3个点不共线。
  • 函数cv2.getPerspectiveTransform():构造透视变换矩阵。第一个参数:原图像上的四个坐标点,第二个参数:输出图像的四个坐标点。
  • 函数cv2.warpPerspective():透视变换函数,可保持直线不变形,但平行线可能不再平行。

2.3 小结

在这次简单的学习了图像的一些原理和基本操作。 图片是网络上找的。
感谢DataWhale这次组队学习,提供了学习机会以及学习资料。

关于Datawhale:

Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域
院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。

2.4 参考资料

Datawhale的学习资料:https://github.com/datawhalechina/team-learning/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89%E5%9F%BA%E7%A1%80%EF%BC%9A%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%EF%BC%88%E4%B8%8A%EF%BC%89/Task02%20%E5%87%A0%E4%BD%95%E5%8F%98%E6%8D%A2.md
其他的学习链接:https://blog.csdn.net/g11d111/article/details/79978582
https://www.kancloud.cn/aollo/aolloopencv/264331

你可能感兴趣的:(Datawhale OpenCV -Task2 几何变换 小白学习笔记)