OpenCV-python小玩意20 仿射变换

0. 什么是仿射变换?

0.1 概念

仿射变换(Affine Transformation)是计算机视觉中的基础空间变换技术,它是一种对图像进行几何形变 的基本方法,通过一个线性映射 + 平移 的方式,对图像中的每个像素点进行重新定位,从而实现如旋转、缩放、平移、错切(shear) 等操作。

0.2 公式

它的公式如下图:

0.3 常见的仿射变换类型

变换类型 效果 应用场景
平移(Translation) 图像整体移动 图像对齐、ROI裁剪
旋转(Rotation) 绕某点转动 图像校正、数据增强
缩放(Scaling) 放大或缩小 图像金字塔、多尺度分析
错切(Shear) 倾斜变形(矩形→平行四边形) 字体变形、数据增强
组合变换 如"先旋转再平移" 任意仿射形变

1. OpenCV中的关键函数

OpenCV 提供两个关键函数:

  • cv2.getAffineTransform(src_pts, dst_pts):通过3对对应点计算仿射矩阵。
  • cv2.warpAffine(img, M, dsize):用仿射矩阵 M 对图像 img 进行变换。

1.1 getAffineTransform

函数原型:

python 复制代码
M = cv2.getAffineTransform(src_pts, dst_pts)
  • src_pts:源图像中的3个点(浮点数数组,形状为 (3,2)
  • dst_pts:目标图像中对应3个点的位置(浮点数数组,形状为 (3,2)),通过指定这些点,你可以控制图像如何变换(如旋转、缩放、平移或错切)。
  • M:返回的3x2的仿射变换矩阵

1.2 warpAffine

它是OpenCV中用于对图像应用**仿射变换(Affine Transformation)**的核心函数。它根据给定的2×3仿射变换矩阵M,将输入图像img映射到一个新的坐标系中,生成变换后的图像。

函数原型:

python 复制代码
warped_img = cv2.warpAffine(
    src,                # 输入图像
    M,                  # 2x3 仿射变换矩阵
    dsize,              # 输出图像尺寸 (width, height)
    dst=None,           # (可选)输出图像
    flags=cv2.INTER_LINEAR,     # 插值方法
    borderMode=cv2.BORDER_CONSTANT,  # 边界填充方式
    borderValue=(0, 0, 0)        # 边界填充颜色(默认黑色)
)
  • img:输入图像,类型是numpy.ndarray,可以是灰度图(单通道)或彩色图(多通道,如 BGR)
  • M:仿射变换矩阵(3x2)
  • dsize:输出图像的尺寸(宽度、高度)
  • flags:插值方法,控制如何计算非整数坐标的像素值(因为变换后坐标可能是小数),默认是 cv2.INTER_LINEAR
标志 说明
cv2.INTER_NEAREST 最近邻插值(最快,但锯齿明显)
cv2.INTER_LINEAR 双线性插值(默认,平衡速度与质量)
cv2.INTER_CUBIC 双三次插值(较慢,质量高)
cv2.INTER_LANCZOS4 Lanczos 插值(高质量,适合缩放)
  • borderMode:边界填充方式,默认是 cv2.BORDER_CONSTANT等。
模式 效果
cv2.BORDER_CONSTANT 用固定颜色填充(由 borderValue 指定,默认黑色)
cv2.BORDER_REPLICATE 复制边缘像素(如 [a b c] → [a a a b c c c]
cv2.BORDER_REFLECT 镜像反射(如 [a b c] → [c b a b c b a]
cv2.BORDER_WRAP 平铺(环绕)
  • borderValue:边界填充颜色,默认是黑色(0,0,0),可以根据需要修改。
  • warped_img:输出变换后的图像

1.3 示例

python 复制代码
import cv2
import numpy as np

# 创建一个简单的示例图像
img = np.zeros((400, 400, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (350, 350), (0, 255, 0), -1)  # 绿色矩形框
cv2.circle(img, (200, 200), 30, (255, 0, 0), -1)  # 蓝色圆圈

# 定义源图像上的三个点
src_points = np.float32([[50, 50], [350, 50], [50, 350]])
# 定义目标图像上的三个对应点
dst_points = np.float32([[100, 150], [300, 50], [100, 300]])

# 计算仿射变换矩阵
affine_matrix = cv2.getAffineTransform(src_points, dst_points)

# 应用仿射变换
rows, cols = img.shape[:2]
transformed_img = cv2.warpAffine(img, affine_matrix, (cols, rows))

# 显示原图和变换后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Transformed Image', transformed_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

效果如下图:

它实现的是一个组合变换(Combined Transformations)。

2. 经典案例

2.1 旋转

这里需要再介绍一个函数:

python 复制代码
rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)

该函数用于生成一个 2×3 的变换矩阵,用于对图像进行 绕指定中心点的旋转 + 缩放操作。
参数说明如下:

  • center : 这是一个包含两个值的元组 (x, y),表示你希望图像围绕哪个点进行旋转。通常情况下,我们会选择图像的中心作为旋转中心。例如,对于一张 400x400 像素大小的图像,其中心点可以是 (200, 200)

  • angle : 表示旋转的角度(以度为单位)。正值表示逆时针方向旋转,而负值则表示顺时针方向旋转。比如,如果你想让图像逆时针旋转 90 度,则设置 angle=90

  • scale : 缩放因子。允许你在旋转的同时对图像进行放大或缩小。如果 scale=1.0,则图像尺寸保持不变;若 scale>1.0 则图像被放大;反之,若 scale<1.0,图像则会被缩小。

返回值

该函数返回一个 2x3 的浮点数数组(即矩阵),这个矩阵包含了旋转和缩放所需的所有信息。它可以直接用作 cv2.warpAffine() 函数的输入参数之一,用于执行实际的仿射变换。

旋转实现

python 复制代码
import cv2
import numpy as np

# 创建一个示例图像
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (250, 250), (0, 255, 0), -1)  # 绿色矩形
cv2.circle(img, (150, 150), 30, (255, 0, 0), -1)  # 蓝色圆
cv2.putText(img, 'Affine', (90, 160), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

# 获取图像尺寸
(h, w) = img.shape[:2]
center = (w // 2, h // 2)

# 定义旋转角度
angle = 30  # 逆时针旋转30度
scale = 1.0

# 计算旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)

# 应用仿射变换
rotated_img = cv2.warpAffine(img, rotation_matrix, (w, h))

# 显示原图和旋转后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Rotated Image (Affine Transform)', rotated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.2 缩放

根据上面基础例子稍加修改,就可以得到一个缩放demo。

python 复制代码
import cv2
import numpy as np

# 创建一个简单的示例图像
img = np.zeros((400, 400, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (350, 350), (0, 255, 0), -1)  # 绿色矩形框
cv2.circle(img, (200, 200), 30, (255, 0, 0), -1)  # 蓝色圆圈

scale = 0.75
src_points = np.float32([[50, 50], [350, 50], [50, 350]])
dst_points = np.float32([[int(50*scale), int(50*scale)],
                         [int(350*scale), int(50*scale)],
                         [int(50*scale), int(350*scale)]])

# 计算仿射变换矩阵
affine_matrix = cv2.getAffineTransform(src_points, dst_points)

# 应用仿射变换
rows, cols = img.shape[:2]
transformed_img = cv2.warpAffine(img, affine_matrix, (cols, rows))

# 显示原图和变换后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Transformed Image', transformed_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下:

还有另外一种简单的玩法:

python 复制代码
import cv2
import numpy as np

img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (250, 250), (0, 255, 0), -1)
cv2.circle(img, (150, 150), 40, (255, 0, 0), -1)
cv2.putText(img, 'Affine Scale', (60, 160), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

# 缩放因子
sx = 1.5  # x方向
sy = 0.8  # y方向

# 构建仿射变换矩阵(绕原点缩放)
M = np.float32([[sx, 0, 0],
                [0, sy, 0]])

# 计算输出图像尺寸
new_w = int(img.shape[1] * sx)
new_h = int(img.shape[0] * sy)

# 应用仿射变换
scaled_img = cv2.warpAffine(img, M, (new_w, new_h), flags=cv2.INTER_LINEAR)

cv2.imshow('Original', img)
cv2.imshow('Scaled (Affine)', scaled_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下:

2.3 平移

根据上面基础例子稍加修改,就可以得到一个平移demo。

python 复制代码
import cv2
import numpy as np

# 创建一个简单的示例图像
img = np.zeros((400, 400, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (350, 350), (0, 255, 0), -1)  # 绿色矩形框
cv2.circle(img, (200, 200), 30, (255, 0, 0), -1)  # 蓝色圆圈

src_points = np.float32([[50, 50], [350, 50], [50, 350]])
dst_points = np.float32([[100, 150], [400, 150], [100, 450]])  # 向右50像素,向下100像素

# 计算仿射变换矩阵
affine_matrix = cv2.getAffineTransform(src_points, dst_points)

# 应用仿射变换
rows, cols = img.shape[:2]
transformed_img = cv2.warpAffine(img, affine_matrix, (cols, rows))

# 显示原图和变换后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Transformed Image', transformed_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

效果如下:

还可以用另一种玩法:

python 复制代码
import cv2
import numpy as np

# 假设img是你想要平移的图像。这里创建一个简单的示例图像。
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (250, 250), (0, 255, 0), -1)  # 绿色矩形
cv2.circle(img, (150, 150), 30, (255, 0, 0), -1)  # 蓝色圆
cv2.putText(img, 'Translation', (90, 160), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

# 定义平移距离(dx, dy),即在x轴和y轴上的位移量
dx, dy = 50, 30

# 创建平移变换矩阵
translation_matrix = np.float32([[1, 0, dx], [0, 1, dy]])

# 应用仿射变换
translated_img = cv2.warpAffine(img, translation_matrix, (img.shape[1], img.shape[0]))

# 显示原图和平移后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Translated Image', translated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.4 、错切(shear)

python 复制代码
import cv2
import numpy as np

# 同样地,假设img是你想要进行错切变换的图像。这里创建一个简单的示例图像。
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (250, 250), (0, 255, 0), -1)  # 绿色矩形
cv2.circle(img, (150, 150), 30, (255, 0, 0), -1)  # 蓝色圆
cv2.putText(img, 'Shear', (90, 160), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

# 定义错切系数(shx, shy),分别为水平方向和垂直方向的错切系数
shx, shy = 0.3, 0.2

# 创建错切变换矩阵
shear_matrix = np.float32([[1, shx, 0], [shy, 1, 0]])

# 计算变换后图像的新尺寸,避免裁剪
new_width = int(img.shape[1] + abs(shx * img.shape[0]))
new_height = int(img.shape[0] + abs(shy * img.shape[1]))
shear_matrix[0, 2] += (new_width / 2) - img.shape[1] / 2
shear_matrix[1, 2] += (new_height / 2) - img.shape[0] / 2

# 应用仿射变换
sheared_img = cv2.warpAffine(img, shear_matrix, (new_width, new_height))

# 显示原图和错切后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Sheared Image', sheared_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

效果如下:

3. 最后

本章节研究了仿射变换各种玩法,最重要的还是要融会贯通。组合变换时才能发挥它真正的实力。

相关推荐
DogDaoDao5 分钟前
【GitHub】 Headroom 深度解析:AI Agent 上下文压缩层的完整技术拆解
人工智能·深度学习·程序员·github·ai agent·智能体·agent skill
挖坑的张师傅12 分钟前
方便 Mac 本机运行 e2b 的沙箱方案 e2b-local
人工智能·后端
生成论实验室17 分钟前
认知芯片:让判断力在物理定律上运行——AI芯片的第三条路
人工智能·语言模型·机器人·自动驾驶·安全架构
浦信仿真大讲堂18 分钟前
达索系统SIMULIA Abaqus 2026接触和约束的增强新功能介绍
人工智能·python·算法·仿真软件·达索软件
文艺倾年24 分钟前
【强化学习】MDP、贝尔曼方程与CartPole 编程,20W字总结(二)
人工智能·软件工程·强化学习
ttt606_30 分钟前
门店业绩上报系统功能拆解:门店业绩上报如何提高数据精确度与时效性?
大数据·人工智能
phltxy31 分钟前
Spring AI 可观测性与 Zipkin 实战
java·人工智能·spring
xufengzhu35 分钟前
第三方 Python 库 Loguru 的进阶实战
python·loguru
ACP广源盛1392462567335 分钟前
GSV2221@ACP#DP 1.4 MST 多屏转换芯片,物理 AI 多模态交互的视觉中枢
大数据·人工智能·嵌入式硬件·gpt·spark
HIT_Weston42 分钟前
117、【Agent】【OpenCode】项目配置(根目录&子包配置)
人工智能·agent·opencode