哈喽各位同学!今天咱们来解锁 OpenCV 里一个超实用的技能 —— 仿射变换。很多小伙伴看到 “仿射” 这两个字就头大,觉得是高深的数学知识,其实完全不用怕!咱们不聊复杂公式,只讲 “怎么用、怎么看懂”,看完这篇,你也能轻松写出仿射变换的代码,实现图片的 “变形魔法”~

先给大家打个预防针:仿射变换一点都不抽象,它本质就是对图片做线性变换 + 平移的组合操作,比如咱们平时把图片旋转、拉伸、平移,都属于仿射变换的范畴。它最核心的特点是:直线还是直线,平行线依然平行,只是形状、位置会发生变化,就像把一张纸轻轻拉伸、旋转,不会把直线拧成曲线一样。

话不多说,咱们直接上干货 —— 先看完整代码(可直接复制运行),再逐行拆解,保证每一位同学都能看懂!

一、完整可运行代码(复制就完事)

python

运行

"""仿射变换"""
import cv2
import numpy as np

"""----------------仿射变换核心代码----------------"""
# 1. 读取图片(替换成你自己的图片路径哦)
img = cv2.imread('teacherli.jpg')
# 2. 获取图片的高度和宽度(后续用得上)
height, width = img.shape[:2]

# 3. 定义原图像和目标图像上的3个对应点(关键!)
mat_src = np.float32([[0, 0],[0, height-1],[width-1, 0]])  # 原图像3个点
mat_dst = np.float32([[0, 0],[100, height-100],[width-100, 100]])  # 目标图像3个点

# 4. 计算仿射变换矩阵
M = cv2.getAffineTransform(mat_src, mat_dst)# 得到变换矩阵M

# 5. 执行仿射变换(详解看下方)
# 函数warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
# src:输入图像
# M:运算矩阵,2行3列的.
# dsize:运算后矩阵的大小,也就是输出图片的尺寸
# dst:输出图像
# flags:插值方法的组合,与resize函数中的插值一样,可以查看cv2.resize
# borderMode:像素外推方法,详情参考官网
# borderValue:在恒定边框的情况下使用的borderValue值;默认情况下,它是0
dst = cv2.warpAffine(img, M, dsize=(width,height))# 进行仿射变换

# 6. 显示对比结果
# np.hstack()按水平方向(列顺序)堆叠数组构成一个新的数组
# np.vstack()按垂直方向(行顺序)堆叠数组构成一个新的数组
imgs = np.hstack([img,dst])
cv2.namedWindow(winname='imgs', cv2.WINDOW_NORMAL)#可自动调整窗口大小,避免图片太大不便查阅
cv2.imshow(winname="imgs",imgs)
cv2.waitKey(0)

二、逐行拆解代码(看懂每一步,不踩坑)

很多同学看代码会犯怵,其实只要把每一行的作用搞清楚,就会发现特别简单。咱们从开头开始,一步步拆解,每一步都讲得明明白白,不用怕跟不上~

1. 导入必备库(基础操作)

python

运行

import cv2
import numpy as np

这两行是 OpenCV 编程的 “标配”,必须先导入:

  • cv2:就是咱们要用的 OpenCV 库,专门用来处理图像、视频,所有和图像相关的操作,都要靠它;
  • numpy:简称 np,是 Python 里处理数组、矩阵的神器,仿射变换会用到矩阵运算,离不开它。

不用纠结这两行的原理,只要记住 “开头先写这两句” 就行,复制粘贴都可以~

2. 读取图片(关键第一步)

python

运行

img = cv2.imread('teacherli.jpg')

这一行的作用是 “读取电脑里的图片”,重点注意 2 点:① 括号里的teacherli.jpg,是图片的路径—— 如果你的图片和代码文件放在同一个文件夹里,直接写图片名字 + 后缀(.jpg、.png 都可以)就行;② 如果图片不在同一个文件夹,就要写完整路径,比如C:/Users/XXX/Desktop/teacherli.jpg(Windows 系统),不然会读取失败,运行时会报错哦。

3. 获取图片尺寸(后续要用)

python

运行

height, width = img.shape[:2]

简单说,这一行是 “获取图片的高度和宽度”:

  • img.shape:可以获取图片的尺寸信息,返回的是(高度,宽度,通道数),比如一张 1080×1920 的图片,返回的就是(1080, 1920, 3);
  • img.shape [:2]:意思是 “取前两个数值”,也就是高度和宽度,分别赋值给 height(高度)和 width(宽度),后续定义变换点、输出图片时都会用到。

4. 定义对应点(仿射变换的核心!)

python

运行

mat_src = np.float32([[0, 0],[0, height-1],[width-1, 0]])
mat_dst = np.float32([[0, 0],[100, height-100],[width-100, 100]])

这一步是仿射变换的关键,也是最容易理解的一步 —— 仿射变换的本质,就是通过 3 个点的对应关系,确定整个图片的变换规则

咱们用通俗的话解释:

  • mat_src:原图片上的 3 个点,相当于 “参照物”;
  • mat_dst:目标图片上的 3 个点,相当于 “参照物要移动到的位置”;

举个例子:原图片的第一个点[0,0],就是图片的 “左上角”;目标图片的第一个点还是[0,0],说明左上角位置不变;原图片的第二个点[0, height-1]是 “左下角”,目标点是[100, height-100],说明左下角要向右移动 100 个像素、向上移动 100 个像素;第三个点同理。

记住:必须是 3 个点,少一个、多一个都不行,这 3 个点确定了,整个图片的变换方式就固定了。

5. 计算变换矩阵(不用懂原理,会用就行)

python

运行

M = cv2.getAffineTransform(mat_src, mat_dst)

这一行的作用是 “根据刚才定义的 3 对对应点,计算出仿射变换矩阵 M”。

这里不用纠结 “矩阵 M 是什么、怎么算的”(涉及线性代数,咱们暂时用不上),只要知道:cv2.getAffineTransform这个函数,会自动帮我们计算出变换矩阵,后续只要用这个矩阵,就能完成图片的变换。

简单记:输入原点点和目标点,输出变换矩阵 M,直接复制代码即可。

6. 执行仿射变换(核心操作)

python

运行

dst = cv2.warpAffine(img, M, dsize=(width,height))

这一行就是 “真正执行仿射变换”,把原图片 img,通过矩阵 M,变成目标图片 dst。

重点解释 3 个关键参数(其他参数默认即可,不用改):

  • img:输入的原图片;
  • M:刚才计算出的变换矩阵,决定了图片怎么变形;
  • dsize:输出图片的尺寸,这里我们设置成(width, height),和原图片尺寸一样,避免图片被拉伸或压缩变形。

7. 显示变换结果(看效果!)

python

运行

imgs = np.hstack([img,dst])
cv2.namedWindow(winname='imgs', cv2.WINDOW_NORMAL)
cv2.imshow(winname="imgs",imgs)
cv2.waitKey(0)

这几行的作用是 “显示原图片和变换后的图片,方便对比效果”,逐句解释:① imgs = np.hstack([img,dst]):把原图片 img 和目标图片 dst水平拼接在一起,这样就能同时看到两张图,对比变换效果;② cv2.namedWindow(...):创建一个显示窗口,设置成 “可自动调整大小”,避免图片太大,看不到完整内容;③ cv2.imshow(...):把拼接好的图片显示在窗口里;④ cv2.waitKey(0):让窗口一直显示,直到你按键盘上的任意键,窗口才会关闭(如果没有这一行,窗口会一闪而过,看不到效果)。

三、常见问题 & 避坑指南(必看!)

很多同学运行代码会报错,大多是这几个问题,提前避开,少走弯路:

  1. 图片读取失败:报错提示error: (-215:Assertion failed),大概率是图片路径错了!要么把图片和代码放在同一个文件夹,要么写完整路径;
  2. 点的数量不对:必须是 3 个点,不能多也不能少,而且 mat_src 和 mat_dst 的点要一一对应,不能乱序;
  3. 窗口一闪而过:忘记写cv2.waitKey(0),加上这一行就好;
  4. 图片显示不全:没有设置cv2.WINDOW_NORMAL,加上这个参数,窗口可以拖动调整大小。

四、总结(快速回顾,加深记忆)

其实仿射变换的核心就 3 步,记牢这 3 步,就能轻松上手:

  1. 读取图片,获取图片尺寸;
  2. 定义原图片和目标图片的 3 对对应点;
  3. 计算变换矩阵,执行变换,显示效果。

整个过程不用懂复杂的数学公式,只要跟着代码一步步来,复制、修改图片路径,就能实现图片的变形效果。大家可以试着修改 mat_dst 里的 3 个点,看看图片会发生什么变化,多尝试几次,就能熟练掌握啦~

如果运行过程中遇到其他问题,欢迎在评论区留言,一起交流解决!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐