【opencv】OpenCV 几何变换
推荐阅读
几何变换
OpenCV 几何变换介绍
几何变换是图像处理中改变图像空间关系的关键操作,常用于图像校正、视角变换、缩放等场景。OpenCV 提供的主要几何变换包括:
-
缩放 (Resize)
- 改变图像尺寸
- 插值方法:
INTER_NEAREST(最近邻)、INTER_LINEAR(双线性)、INTER_CUBIC(双三次)等
-
平移 (Translation)
- 沿X/Y轴移动图像
- 使用仿射变换矩阵实现
-
旋转 (Rotation)
- 绕指定中心点旋转图像
- 可同时控制缩放比例
-
仿射变换 (Affine Transformation)
- 保持平行性的变换(平移+旋转+缩放+错切)
- 需3对非共线点定义变换
-
透视变换 (Perspective Transformation)
- 实现视角转换(如鸟瞰图)
- 需4对点定义变换矩阵
代码实现(Python)
import cv2
import numpy as np
# 读取图像
img = cv2.imread('image.jpg')
height, width = img.shape[:2]
# 1. 缩放 (缩小50%)
resized = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)
# 2. 平移 (向右100px,向下50px)
M_trans = np.float32([[1, 0, 100], [0, 1, 50]])
translated = cv2.warpAffine(img, M_trans, (width, height))
# 3. 旋转 (中心点旋转45度,缩放0.8)
center = (width//2, height//2)
M_rot = cv2.getRotationMatrix2D(center, 45, 0.8)
rotated = cv2.warpAffine(img, M_rot, (width, height))
# 4. 仿射变换 (三角形顶点映射)
pts1 = np.float32([[50,50], [200,50], [50,200]]) # 原始点
pts2 = np.float32([[10,100], [200,50], [100,250]]) # 变换后点
M_affine = cv2.getAffineTransform(pts1, pts2)
affined = cv2.warpAffine(img, M_affine, (width, height))
# 5. 透视变换 (四边形到任意四边形)
src_pts = np.float32([[56,65], [368,52], [28,387], [389,390]])
dst_pts = np.float32([[0,0], [300,0], [0,300], [300,300]])
M_persp = cv2.getPerspectiveTransform(src_pts, dst_pts)
perspected = cv2.warpPerspective(img, M_persp, (300, 300))
# 显示结果
cv2.imshow("Original", img)
cv2.imshow("Resized", resized)
cv2.imshow("Translated", translated)
cv2.imshow("Rotated", rotated)
cv2.imshow("Affine", affined)
cv2.imshow("Perspective", perspected)
cv2.waitKey(0)
cv2.destroyAllWindows()
关键函数解析
| 函数 | 作用 | 核心参数说明 |
|---|---|---|
cv2.resize() | 图像缩放 | fx/fy: XY缩放因子 |
cv2.warpAffine() | 执行仿射变换 | M: 2x3变换矩阵 |
getRotationMatrix2D() | 生成旋转矩阵 | center, angle, scale |
getAffineTransform() | 通过点对计算仿射矩阵 | 3对对应点 |
getPerspectiveTransform() | 计算透视变换矩阵 | 4对对应点 |
warpPerspective() | 执行透视变换 | M: 3x3变换矩阵 |
注意事项
- 插值方法选择:
- 缩小图像优先用
INTER_AREA - 放大图像用
INTER_CUBIC(慢但质量好)或INTER_LINEAR
- 缩小图像优先用
- 变换矩阵类型:
- 仿射变换:2×3 矩阵
- 透视变换:3×3 矩阵
- 边界处理:
- 使用
borderValue参数设置填充颜色(如borderValue=(255,0,0)填充蓝色)
- 使用
建议:处理几何变换时始终保留原始图像副本,避免多次变换导致图像质量下降。
仿射变换
仿射变换(Affine Transformation)概念详解
核心定义
仿射变换是一种二维平面到二维平面的线性变换,由以下两个核心操作组成:
- 线性变换(旋转、缩放、错切)
- 平移变换(位置偏移)
数学表达:
[x'] [a b] [x] [tx]
[y'] = [c d] [y] + [ty]
或使用齐次坐标(更简洁):
[x'] [a b tx] [x]
[y'] = [c d ty] [y]
[1 ] [0 0 1 ] [1]
六大核心特性
- 直线保持性:直线变换后仍是直线
- 平行性保持:平行线变换后仍保持平行
- 比例不变性:直线上点的相对位置比例不变
- 共线性保持:共线点变换后仍共线
- 凸性保持:凸图形变换后仍是凸图形
- 组合封闭性:多个仿射变换组合仍是仿射变换
变换自由度(6个参数)
| 参数 | 数学符号 | 控制效果 |
|---|---|---|
| a | 缩放/旋转 | X方向缩放和旋转分量 |
| b | 错切/旋转 | XY耦合变换分量 |
| c | 错切/旋转 | YX耦合变换分量 |
| d | 缩放/旋转 | Y方向缩放和旋转分量 |
| tx | 平移 | X方向位移 |
| ty | 平移 | Y方向位移 |
基础变换分解
任何仿射变换都可分解为:
仿射变换 = 旋转 × 缩放 × 错切 × 平移
与透视变换的关键区别
| 特性 | 仿射变换 | 透视变换 |
|---|---|---|
| 平行保持 | ✓ 保持平行 | ✗ 平行线可能相交 |
| 矩阵形式 | 2×3 或 3×3(最后行001) | 3×3(自由参数) |
| 点对需求 | 3对非共线点 | 4对点(任意四边形) |
| 深度变化 | ✗ 保持平面性 | ✓ 产生近大远小效果 |
| 变换类型 | 平面刚体变换+缩放/错切 | 投影变换(3D->2D映射) |
OpenCV实现关键
# 计算变换矩阵 (需3对对应点)
src_points = np.float32([[x1,y1], [x2,y2], [x3,y3]])
dst_points = np.float32([[x1',y1'], [x2',y2'], [x3',y3']])
M = cv2.getAffineTransform(src_points, dst_points)
# 应用变换
result = cv2.warpAffine(image, M, (width, height))
典型应用场景
- 图像几何校正(文档扫描、板件定位)
- 机械视觉中的物体位姿估计
- 倾斜文字/车牌校正
- 简单图像配准(不同角度拍摄的平面物体)
- 2D动画中的刚体运动模拟
重要原则:选择变换点时必须确保三点不共线,否则无法解出唯一变换矩阵。实际应用中常使用特征检测算法(如SIFT, ORB)自动匹配特征点。
透视变换
透视变换(Perspective Transformation)概念详解
核心定义
透视变换(又称投影变换)是一种非线性的二维空间变换,通过3×3变换矩阵实现从二维平面到二维平面的映射,其核心特点是:
- 模拟真实世界中的透视效果
- 能够产生近大远小的视觉现象
- 直线保持性(直线变换后仍是曲线)
数学表达(齐次坐标):
[x'] [a b c] [x]
[y'] = [d e f] [y]
[w'] [g h 1] [1]
实际坐标:
x'' = x'/w' = (ax + by + c)/(gx + hy + 1)
y'' = y'/w' = (dx + ey + f)/(gx + hy + 1)
核心特性
- 直线保持性:直线变换后仍是直线(但平行性可能丢失)
- 深度模拟:能表现物体距离观察者的远近关系
- 可逆性:存在逆变换矩阵
- 非线性性:变换后的坐标与原始坐标呈分式线性关系
- 8自由度:变换矩阵有8个可调参数(最后元素固定为1)
与仿射变换的关键对比
| 特性 | 透视变换 | 仿射变换 |
|---|---|---|
| 矩阵形式 | 3×3(任意值,最后元素为1) | 3×3(最后行固定为[0,0,1]) |
| 平行保持 | ✗ 平行线可能相交于消失点 | ✓ 保持平行关系 |
| 点对需求 | 4对点 | 3对非共线点 |
| 变换复杂度 | 非线性变换 | 线性变换 |
| 典型应用 | 视角校正、虚拟视角生成 | 平面物体旋转/缩放/平移 |
变换自由度(8个参数)
| 参数 | 控制效果 |
|---|---|
| a,e | X/Y方向缩放和旋转分量 |
| b,d | XY/YX耦合变换分量 |
| c,f | X/Y方向平移 |
| g,h | 透视分量(产生非线性效果) |
OpenCV实现关键
# 计算变换矩阵 (需4对对应点)
src_pts = np.float32([[x1,y1], [x2,y2], [x3,y3], [x4,y4]])
dst_pts = np.float32([[x1',y1'], [x2',y2'], [x3',y3'], [x4',y4']])
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
# 应用变换 (可控制输出尺寸)
result = cv2.warpPerspective(image, M, (new_width, new_height))
典型应用场景
-
文档/车牌矫正:将倾斜拍摄的平面物体转换为正视图
# 示例:将梯形区域转换为矩形 src = np.float32([[56,65],[368,52],[28,387],[389,390]]) dst = np.float32([[0,0],[300,0],[0,300],[300,300]]) -
虚拟视角生成:改变观察视角(如鸟瞰图变换)
# 生成鸟瞰图 (俯视视角) src = np.float32([[200,100],[400,100],[0,400],[600,400]]) dst = np.float32([[0,0],[600,0],[0,800],[600,800]]) -
增强现实:将虚拟物体按透视关系嵌入真实场景
-
图像拼接:对齐不同视角拍摄的图像
特殊效果实现
-
消失点控制:通过调整变换矩阵的g,h参数控制透视强度
# 增强透视效果(增大g,h值) M[2,0] = 0.001 # g参数 M[2,1] = 0.002 # h参数 -
逆透视变换:将透视视图转换为正视图(常用于自动驾驶的车道线检测)
M_inv = cv2.getPerspectiveTransform(dst_pts, src_pts)
注意事项
-
点选择原则:
- 4个点中任意3点不能共线
- 点对应关系必须准确(建议使用特征点匹配算法)
-
图像边界处理:
# 设置填充色(默认黑色) warped = cv2.warpPerspective(img, M, (w,h), borderMode=cv2.BORDER_CONSTANT, borderValue=(255,255,255)) -
性能优化:
- 对大图像先缩小计算变换矩阵,再应用于原图
- 对视频流可缓存变换矩阵(当视角固定时)
高级技巧:结合特征检测算法(如SIFT/SURF/ORB)自动获取匹配点对,实现智能透视变换,适用于扫描APP等自动校正场景。
直线保持性
直线保持性(Line Preservation Property)深度解析
核心定义
直线保持性是几何变换的核心性质,指变换前后直线仍保持为直线的特性。数学表述为:
若三点 ( P_1, P_2, P_3 ) 共线,则变换后的点 ( P_1’, P_2’, P_3’ ) 也必须共线。
关键特性图解
| 变换类型 | 直线保持性 | 典型表现 |
|---|---|---|
| 仿射变换 | ✓ 严格保持 | 平行线仍平行 |
| 透视变换 | ✓ 保持 | 平行线可能相交于消失点 |
| 非线性变换 | ✗ 破坏 | 直线可能变为曲线(如鱼眼矫正) |
数学本质
对于线性变换(含仿射/透视):
[
\begin{cases}
x’ = \frac{a_{11}x + a_{12}y + a_{13}}{a_{31}x + a_{32}y + 1} \
y’ = \frac{a_{21}x + a_{22}y + a_{23}}{a_{31}x + a_{32}y + 1}
\end{cases}
]
当 ( a_{31} = a_{32} = 0 ) 时退化为仿射变换(保持平行性),否则为透视变换(保持直线但不保持平行)。
OpenCV验证实验
import cv2
import numpy as np
# 创建测试图像(带十字线)
img = np.zeros((400, 400, 3), dtype=np.uint8)
cv2.line(img, (100,100), (300,100), (0,0,255), 2) # 水平红线
cv2.line(img, (100,200), (300,200), (0,255,0), 2) # 水平绿线
cv2.line(img, (200,50), (200,350), (255,0,0), 2) # 垂直蓝线
# 透视变换(产生消失点)
src_pts = np.float32([[100,100],[300,100],[100,300],[300,300]])
dst_pts = np.float32([[80,80],[320,80],[120,320],[280,320]])
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(img, M, (400,400))
# 显示结果
cv2.imshow("Original", img)
cv2.imshow("Perspective", warped)
cv2.waitKey(0)
实验结果:
- 原始图像中的三条直线(红/绿/蓝)在透视变换后:
- 仍保持笔直(验证直线保持性)
- 水平线不再平行(红绿线向一点汇聚)
- 垂直线产生倾斜(蓝线角度变化)
工程意义
- 文档处理:确保矫正后的文字行仍为直线
- CAD/CAM:保持设计图纸的几何精度
- 视觉测量:避免引入曲线畸变误差
- AR/VR:正确渲染虚拟物体的边缘
特殊场景分析
当直线保持性被破坏时(如鱼眼矫正):
# 鱼眼效果(非线性变换)
map_x = np.zeros(img.shape[:2], np.float32)
map_y = np.zeros(img.shape[:2], np.float32)
for i in range(img.shape[0]):
for j in range(img.shape[1]):
# 非线性映射公式
map_x[i,j] = j + (j-200)*((i-200)/400)**2
map_y[i,j] = i + (i-200)*((j-200)/400)**2
distorted = cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)
此时直线将变为曲线(如建筑摄影中的桶形畸变)
扩展知识
- 射影几何:研究直线保持变换的数学分支
- 齐次坐标:用三维向量表示二维点的数学工具
[
\begin{bmatrix} x’ \ y’ \ w’ \end{bmatrix} = M \cdot \begin{bmatrix} x \ y \ 1 \end{bmatrix}
] - 变换层级:
- 欧式变换(仅旋转+平移)
- 相似变换(+均匀缩放)
- 仿射变换(+非均匀缩放/错切)
- 射影变换(+透视效果)
最佳实践建议:在需要测量真实长度的场景(如工业检测)优先选择仿射变换,在需要模拟人眼视觉的场景(如游戏开发)使用透视变换。
更多推荐


所有评论(0)