【OpenCV】Chapter3.图像的仿射变换

2022-09-22 11:56:55 浏览数 (1)

最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。 【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487】 程序仓库:https://github.com/zstar1003/OpenCV-Learning

仿射变换原理

仿射变换其实包含了一系列的操作:平移,缩放,旋转等,不过所有的操作都可以通过这个仿射变换矩阵来实现。

仿射变换矩阵:

begin{bmatrix}x \y\1end{bmatrix} =begin{bmatrix} a_0 &a_1 & a_2 \ a_3 & a_4 & a_5 \ 0 & 0 & 1 end{bmatrix} begin{bmatrix} x_0 \ y_0 \ 1 end{bmatrix}

其中

x

y

表示输出图像像素的坐标,

x_0

,

y_0

表示输入图像像素的坐标

变换名称

a 0 a_0 a0​

a 1 a_1 a1​

a 2 a_2 a2​

a 3 a_3 a3​

a 4 a_4 a4​

a 5 a_5 a5​

平移

1

0

△ x triangle x △x

0

1

△ y triangle y △y

均匀缩放

s s s

0

0

0

s s s

0

不均匀缩放

s x s_x sx​

0

0

0

s y s_y sy​

0

顺时针旋转角度 θ theta θ

c o s θ costheta cosθ

s i n θ sintheta sinθ

0

− s i n θ -sintheta −sinθ

c o s θ costheta cosθ

0

逆时针旋转角度 θ theta θ

c o s θ costheta cosθ

− s i n θ -sintheta −sinθ

0

s i n θ sintheta sinθ

c o s θ costheta cosθ

0

垂直偏移变换

1

0

0

h

1

0

水平偏移变换

1

h

0

0

1

0

a_0
a_1
a_2
a_3
a_4
a_5

平移10

triangle x

01

triangle y

均匀缩放

s

000

s

0不均匀缩放

s_x

000

s_y

0顺时针旋转角度

theta
costheta
sintheta

0

-sintheta
costheta

0逆时针旋转角度

theta
costheta
-sintheta

0

sintheta
costheta

0垂直偏移变换100h10水平偏移变换1h0010

表格来源:https://github.com/datawhalechina/magic-cv

在OpenCV中,需要定义的核心就是2行3列的仿射变换矩阵。

主要函数:

cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst

参数说明:

  • scr:变换操作的输入图像
  • M:仿射变换矩阵,2行3列
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • flags:插值方法,整型(int),可选项
    • cv2.INTER_LINEAR:线性插值,默认选项
    • cv2.INTER_NEAREST:最近邻插值
    • cv2.INTER_AREA:区域插值
    • cv2.INTER_CUBIC:三次样条插值
    • cv2.INTER_LANCZOS4:Lanczos 插值
  • borderMode:边界像素方法,整型(int),可选项,默认值为 cv2.BORDER_REFLECT
  • borderValue:边界填充值,可选项,默认值为 0(黑色填充)
  • 返回值:dst,变换操作的输出图像,ndarray 多维数组

平移

参照上面的表格,实现平移操作就需要保证

a_0,a_4=1

,同时

a_2,a_5

分别为x和y的平移距离。

示例程序:

代码语言:javascript复制
"""
图像平移
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
rows, cols, ch = img.shape

dx, dy = 100, 50  # dx=100 向右偏移量, dy=50 向下偏移量
MAT = np.float32([[1, 0, dx], [0, 1, dy]])  # 构造平移变换矩阵
# dst = cv2.warpAffine(img, MAT, (cols, rows))  # 默认为黑色填充
dst = cv2.warpAffine(img, MAT, (cols, rows), borderValue=(255, 255, 255))  # 设置白色填充

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("Original")
plt.subplot(122), plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)), plt.title("Translational")
plt.show()

旋转

以原点为中心

仿射变换矩阵:

示例程序:

代码语言:javascript复制
"""
图像旋转(以原点为中心)
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
rows, cols, ch = img.shape

theta = np.pi / 8.0  # 顺时针旋转角度
cosTheta = np.cos(theta)
sinTheta = np.sin(theta)
MAT = np.float32([[cosTheta, -sinTheta, 0], [sinTheta, cosTheta, 0]])  # 构造旋转变换矩阵
# dst = cv2.warpAffine(img, MAT, (cols, rows))  # 默认为黑色填充
dst = cv2.warpAffine(img, MAT, (cols, rows), borderValue=(255, 255, 255))  # 设置白色填充

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("Origin")
plt.subplot(122), plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)), plt.title("Rotation")
plt.show()

以任意点为中心

以任意点为中心旋转似乎乍一看有些难度,不过换一种思路,可以采用先平移后旋转再平移的操作。 仿射变换矩阵:

为了操作简便,OpenCV提供了cv2.getRotationMatrix2D函数, 根据旋转角度和位移计算旋转变换矩阵 MAR.

cv2.getRotationMatrix2D(center, angle, scale) → M

参数说明:

  • center:旋转中心坐标,二元元组 (x0, y0)
  • angle:旋转角度,单位为角度,逆时针为正数,顺时针为负数
  • scale: 缩放因子
  • 返回值:M, 旋转变换矩阵,2行3列

示例程序:

代码语言:javascript复制
"""
图像旋转(以任意点为中心)
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
height, width = img.shape[:2]  # 图片的高度和宽度

theta1, theta2 = 30, 45  # 顺时针旋转角度,单位为角度
x0, y0 = width // 2, height // 2  # 以图像中心作为旋转中心
MAR1 = cv2.getRotationMatrix2D((x0, y0), theta1, 1.0)
MAR2 = cv2.getRotationMatrix2D((x0, y0), theta2, 1.0)
imgR1 = cv2.warpAffine(img, MAR1, (width, height))  # 旋转变换,默认为黑色填充
imgR2 = cv2.warpAffine(img, MAR2, (width, height), borderValue=(255, 255, 255))  # 设置白色填充

plt.figure(figsize=(10, 6))
plt.subplot(131), plt.axis('off'), plt.title(r"$Origin$")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.subplot(132), plt.axis('off'), plt.title(r"$Rotation {}^o$".format(theta1))
plt.imshow(cv2.cvtColor(imgR1, cv2.COLOR_BGR2RGB))
plt.subplot(133), plt.axis('off'), plt.title(r"$Rotation {}^o$".format(theta2))
plt.imshow(cv2.cvtColor(imgR2, cv2.COLOR_BGR2RGB))
plt.show()

直角旋转

直角旋转指的是旋转角度为 90,180,270度,这时无需再去写仿射变换矩阵。 OpenCV提供了cv2.rotate(src, rotateCode) 这个函数可以快速实现该功能。

cv2.rotate( src, rotateCode[, dst] ) → M

参数说明:

  • src:变换操作的输入图像
  • rotateCode:枚举,指定旋转角度。
    • cv2.ROTATE_90_CLOCKWISE:顺时针旋转 90 度
    • cv2.ROTATE_180: 旋转 180 度
    • cv2.ROTATE_90_COUNTERCLOCKWISE:逆时针旋转 90 度
  • 返回值:dst,变换操作的输出图像,ndarray 多维数组

示例程序:

代码语言:javascript复制
"""
图像旋转(直角旋转)
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)

imgR90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
imgR180 = cv2.rotate(img, cv2.ROTATE_180)
imgR270 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)

plt.figure(figsize=(9, 7))
plt.subplot(221), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title(r"$Origin$")
plt.subplot(222), plt.imshow(cv2.cvtColor(imgR90, cv2.COLOR_BGR2RGB)), plt.title(r"$Rotation 90^{o}$")
plt.subplot(223), plt.imshow(cv2.cvtColor(imgR180, cv2.COLOR_BGR2RGB)), plt.title(r"$Rotation 180^{o}$")
plt.subplot(224), plt.imshow(cv2.cvtColor(imgR270, cv2.COLOR_BGR2RGB)), plt.title(r"$Rotation 270^{o}$")
plt.show()

翻转

仿射变换矩阵:

OpenCV提供了cv2.flip函数,可以将图像沿水平方向、垂直方向、或水平/垂直方向同时进行翻转。

cv2.flip(src, flipCode[, dst]) -> dst

参数说明:

  • scr:变换操作的输入图像
  • flipCode:控制参数,整型(int),flipCode>0 水平翻转,flipCode=0 垂直翻转,flipCode<0 水平和垂直翻转
  • dst:变换操作的输出图像,可选项

示例程序:

代码语言:javascript复制
"""
图像翻转
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)

imgFlip1 = cv2.flip(img, 0)  # 垂直翻转
imgFlip2 = cv2.flip(img, 1)  # 水平翻转
imgFlip3 = cv2.flip(img, -1)  # 水平和垂直翻转

plt.figure(figsize=(9, 6))
plt.subplot(221), plt.axis('off'), plt.title("Original")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  # 原始图像
plt.subplot(222), plt.axis('off'), plt.title("Flipped Horizontally")
plt.imshow(cv2.cvtColor(imgFlip2, cv2.COLOR_BGR2RGB))  # 水平翻转
plt.subplot(223), plt.axis('off'), plt.title("Flipped Vertically")
plt.imshow(cv2.cvtColor(imgFlip1, cv2.COLOR_BGR2RGB))  # 垂直翻转
plt.subplot(224), plt.axis('off'), plt.title("Flipped Horizontally & Vertically")
plt.imshow(cv2.cvtColor(imgFlip3, cv2.COLOR_BGR2RGB))  # 水平垂直翻转
plt.show()

缩放

OpenCV提供了cv2.resize函数,实现图像的缩放。

cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) → dst

参数说明:

  • scr:变换操作的输入图像
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • fx, fy:x 轴、y 轴上的缩放比例,实型,可选项
  • interpolation:插值方法,整型,可选项
    • cv2.INTER_LINEAR:双线性插值(默认方法)
    • cv2.INTER_AREA:使用像素区域关系重采样,缩小图像时可以避免波纹出现
    • cv2.INTER_NEAREST:最近邻插值
    • cv2.INTER_CUBIC:4x4 像素邻域的双三次插值
    • cv2.INTER_LANCZOS4:8x8 像素邻域的Lanczos插值
  • 返回值:dst,变换操作的输出图像,ndarray 多维数组

示例程序:

代码语言:javascript复制
"""
图像缩放
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)

height, width = img.shape[:2]  # 图片的高度和宽度
imgZoom1 = cv2.resize(img, (int(0.75 * width), int(height)))
imgZoom2 = cv2.resize(img, None, fx=0.75, fy=1.0, interpolation=cv2.INTER_AREA)

plt.figure(figsize=(8, 6))
plt.subplot(121), plt.axis('off'), plt.title("Zoom: 0.75*W,1.0*H")
plt.imshow(cv2.cvtColor(imgZoom1, cv2.COLOR_BGR2RGB))
plt.subplot(122), plt.axis('off'), plt.title("Zoom: fx=0.75,fy=1.0")
plt.imshow(cv2.cvtColor(imgZoom2, cv2.COLOR_BGR2RGB))
plt.show()

错切

错切指平面景物在投影平面上的非垂直投影,使图像中的图形在水平方向或垂直方向产生扭变。 以水平错切为例,仿射矩阵为

示例程序:

代码语言:javascript复制
"""
图像错切
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
height, width = img.shape[:2]  # 图片的高度和宽度

MAS = np.float32([[1, 0.2, 0], [0, 1, 0]])  # 构造错切变换矩阵
imgShear = cv2.warpAffine(img, MAS, (width, height))

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("imgOrigin")
plt.subplot(122), plt.imshow(cv2.cvtColor(imgShear, cv2.COLOR_BGR2RGB)), plt.title("imgShear")
plt.show()

投影变换

投影变换也称透视变换,指的是建立两平面场之间的对应关系,即将图片投影到一个新的视平面。 OpenCV提供了cv2.warpPerspective函数实现投影变换的操作。

cv2.getPerspectiveTransform(src, dst[,solveMethod]) → MP cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst

参数说明:

  • src:变换前图像四边形顶点坐标
  • dst:变换后图像四边形顶点坐标
  • solveMethod:矩阵分解方法,传递给 cv2.solve 求解变换矩阵 MP
    • cv2.DECOMP_LU:选择最优轴的高斯消去法,默认方法
    • cv2.DECOMP_SVD:奇异值分解(SVD)方法
    • cv2.DECOMP_EIG:特征值分解方法,src 必须对称
    • cv2.DECOMP_QR:QR(正交三角)分解
    • cv2.DECOMP_CHOLESKY:Cholesky LLT 分解
  • MP:透视变换矩阵,3行3列
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • flags:插值方法,整型(int),可选项
    • cv2.INTER_LINEAR:线性插值,默认选项
    • cv2.INTER_NEAREST:最近邻插值
    • cv2.INTER_AREA:区域插值
    • cv2.INTER_CUBIC:三次样条插值
    • cv2.INTER_LANCZOS4:Lanczos 插值
  • borderMode:边界像素方法,整型(int),可选项,默认值为 cv2.BORDER_REFLECT
  • borderValue:边界填充模式,可选项,默认值为 0(黑色填充)
  • 返回值:dst,透视变换操作的输出图像,ndarray 多维数组

示例程序:

代码语言:javascript复制
"""
投影变换
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
h, w = img.shape[:2]  # 图片的高度和宽度

pointSrc = np.float32([[0, 0], [w - 1, 0], [0, h - 100], [w - 1, h - 100]])  # 原始图像中 4点坐标
pointDst = np.float32([[180, 50], [w - 180, 50], [0, h - 100], [w - 1, h - 100]])  # 变换图像中 4点坐标
MP = cv2.getPerspectiveTransform(pointSrc, pointDst)  # 计算投影变换矩阵 M
imgP = cv2.warpPerspective(img, MP, (512, 512))  # 用变换矩阵 M 进行投影变换

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("Original")
plt.subplot(122), plt.imshow(cv2.cvtColor(imgP, cv2.COLOR_BGR2RGB)), plt.title("Projective")
plt.show()

0 人点赞