OpenCV 从零学习 · 第 4 讲:几何变换 resize / flip / 旋转
现实里的图片总不"完美":头像太大要压缩、扫描件拍歪了要摆正、自拍想镜像、训练数据要翻转增广……这一讲把三种最常用的几何变换讲透,重点说清每个参数怎么选、不同插值/翻转方式差在哪。
1. cv2.resize —— 缩放(最常用)
函数签名
dst = cv2.resize(src, dsize, fx=0, fy=0, interpolation=cv2.INTER_LINEAR)
# dsize : 目标尺寸 (宽, 高) —— 注意是(宽,高), 和 img.shape 的(高,宽)顺序相反!
# fx,fy: 缩放比例(二选一: 给 dsize 就不给 fx/fy, 反之亦然)
# interpolation: 插值方式
新手必踩坑 ①:dsize 是 (宽, 高),不是 (高, 宽)!
img.shape 返回 (高, 宽),但 resize 的 dsize 要填 (宽, 高)。写反了图会被拉变形。
插值方式怎么选(核心差异):缩小用
INTER_AREA(防锯齿/摩尔纹),放大用 INTER_CUBIC 或 INTER_LANCZOS4(更清晰,慢一点);想要"像素块"不模糊用 INTER_NEAREST。
| 插值常量 | 特点 | 适用 |
|---|---|---|
| INTER_NEAREST | 最近邻,不平滑 | 图标/分类标签,保持硬边不糊 |
| INTER_LINEAR | 双线性(默认) | 一般放大,速度均衡 |
| INTER_AREA | 区域重采样 | 缩小首选,避免波纹 |
| INTER_CUBIC | 双三次,慢而清 | 放大首选 |
| INTER_LANCZOS4 | 8x8 兰索斯,最清 | 放大且对清晰度要求高 |
场景案例 A:上传头像前压缩到 512×512
缩小用
缩小用
INTER_AREA 最稳:
resized = cv2.resize(img, (512, 512), interpolation=cv2.INTER_AREA)
场景案例 B:把小图标按比例放大展示
放大追求清晰,用
放大追求清晰,用
INTER_CUBIC;这里用比例 fx/fy 更直观:
big = cv2.resize(img, None, fx=3, fy=3, interpolation=cv2.INTER_CUBIC)
2. cv2.flip —— 翻转 / 镜像
函数签名
dst = cv2.flip(src, flipCode)
# flipCode: 0 = 上下翻转(绕水平轴)
# 1 = 左右翻转(镜像)
# -1 = 同时上下+左右翻转
| flipCode | 效果 | 典型场景 |
|---|---|---|
| 0 | 上下颠倒转正 | 扫描件/手机拍的 PDF 倒过来了,翻回来 |
| 1 | 左右镜像 | 自拍镜像、文字镜像校正 |
| -1 | 180° 旋转 | 快速把图倒个头 |
场景案例 C:自拍做成镜像效果
人习惯照镜子,左右镜像更顺眼:
人习惯照镜子,左右镜像更顺眼:
mirror = cv2.flip(img, 1)
场景案例 D:训练数据增强
把数据集图片左右翻转,样本量直接翻倍、模型更抗左右差异:
把数据集图片左右翻转,样本量直接翻倍、模型更抗左右差异:
aug = cv2.flip(img, 1)
3. 旋转(任意角度)—— warpAffine + getRotationMatrix2D
函数签名
M = cv2.getRotationMatrix2D(center, angle, scale)
# center: 旋转中心 (宽/2, 高/2) 这样的 (x, y)
# angle : 角度, 正数=逆时针, 负数=顺时针
# scale : 缩放, 1.0 表示不缩放
dst = cv2.warpAffine(img, M, (宽, 高)) # 注意输出尺寸也是 (宽, 高)!
新手必踩坑 ②:旋转默认会"裁掉四角"
warpAffine 的输出尺寸若还用原图大小,转完图的四角会被切掉。要么把输出尺寸放大,要么用 borderValue 填边。另外 angle 正数在 OpenCV 里是逆时针,和直觉的"顺时针为正"相反。
场景案例 E:把拍歪 5° 的扫描文档摆正
以图中心为轴逆时针转 5°,并放大输出尺寸避免裁角,黑边填充:
以图中心为轴逆时针转 5°,并放大输出尺寸避免裁角,黑边填充:
h, w = img.shape[:2]
center = (w / 2, h / 2)
M = cv2.getRotationMatrix2D(center, 5, 1.0)
# 输出尺寸放大一点, 旋转后不裁角; 空白填黑色(也可填 255 白边)
fixed = cv2.warpAffine(img, M, (w + 60, h + 60), borderValue=(0, 0, 0))
场景案例 F:数据增强——随机旋转 ±15°
训练模型时随机小角度旋转,提升鲁棒性:
训练模型时随机小角度旋转,提升鲁棒性:
import random
ang = random.uniform(-15, 15)
M = cv2.getRotationMatrix2D((w/2, h/2), ang, 1.0)
rot = cv2.warpAffine(img, M, (w, h))
4. 平移(把图挪个位置)—— 手写仿射矩阵
平移是更简单的仿射变换:构造一个 2×3 矩阵,指定 x/y 方向移动多少像素。
import numpy as np
M = np.float32([[1, 0, tx], # tx: 向右移多少像素(负则向左)
[0, 1, ty]]) # ty: 向下移多少像素(负则向上)
moved = cv2.warpAffine(img, M, (w, h))
场景案例 G:把水印从中间挪到右下角
先算出水印该去的偏移量,平移一张透明水印图再叠加即可:
先算出水印该去的偏移量,平移一张透明水印图再叠加即可:
tx, ty = w - 200, h - 80 # 挪到右下角附近
M = np.float32([[1, 0, tx], [0, 1, ty]])
wm_moved = cv2.warpAffine(watermark, M, (w, h))
5. 本讲要点清单
resize的dsize是 (宽, 高),和img.shape的 (高,宽) 相反。- 缩小用
INTER_AREA,放大用INTER_CUBIC/LANCZOS4,硬边不糊用INTER_NEAREST。 flip:0 上下、1 左右镜像、-1 都翻;自拍/数据增广常用 1。- 旋转用
getRotationMatrix2D+warpAffine;角度正=逆时针;注意放大输出尺寸防裁角。 - 平移手写 2×3 矩阵
[[1,0,tx],[0,1,ty]]再 warpAffine。
✅ 下一讲预告:图片有噪点、想变平滑怎么办?第 5 讲:图像滤波与平滑(均值/高斯/中值/双边)。
