最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。 【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487】 程序仓库:https://github.com/zstar1003/OpenCV-Learning
仿射变换原理
仿射变换其实包含了一系列的操作:平移,缩放,旋转等,不过所有的操作都可以通过这个仿射变换矩阵来实现。
仿射变换矩阵:
其中
,
表示输出图像像素的坐标,
,
表示输入图像像素的坐标
变换名称 | 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 |
平移10
01
均匀缩放
000
0不均匀缩放
000
0顺时针旋转角度
0
0逆时针旋转角度
0
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 多维数组
平移
参照上面的表格,实现平移操作就需要保证
,同时
分别为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()