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. 最后

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

相关推荐
2301_7644413312 小时前
基于HVNS算法和分类装载策略的仓储系统仿真平台
人工智能·算法·分类
aitoolhub12 小时前
在线设计技术实践:稿定设计核心架构与能力拆解
图像处理·人工智能·计算机视觉·自然语言处理·架构·视觉传达
shayudiandian12 小时前
AI生成内容(AIGC)在游戏与影视行业的落地案例
人工智能·游戏·aigc
木头左12 小时前
深度学习驱动的指数期权定价与波动率建模技术实现
人工智能·深度学习
AI科技星12 小时前
统一场论变化的引力场产生电磁场推导与物理诠释
服务器·人工智能·科技·线性代数·算法·重构·生活
不会用AI的老炮12 小时前
【AI coding 智能体设计系列-05】上下文治理:清空压缩摘要与预算控制
人工智能·ai·ai编程
速易达网络12 小时前
AI工具全景:从概念到产业的深度变革
人工智能
点云SLAM12 小时前
Algebraic 英文单词学习
人工智能·英文单词学习·雅思备考·代数形式的·代数的 / 与代数相关的·algebraic
狮子座明仔12 小时前
DISCOG:知识图谱+LLM双引擎驱动的法律电子取证系统
人工智能·深度学习·知识图谱
Ydwlcloud12 小时前
2026年1月云服务器优惠活动全解析:聪明选云的新策略
大数据·服务器·人工智能·云计算