目录
[1. 基本概念](#1. 基本概念)
[2. OpenCV实现](#2. OpenCV实现)
[3. 应用示例:图像校正](#3. 应用示例:图像校正)
[1. 基本概念](#1. 基本概念)
[2. OpenCV实现](#2. OpenCV实现)
[3. 应用示例:视角转换](#3. 应用示例:视角转换)
[1. 仿射变换应用](#1. 仿射变换应用)
[2. 透视变换应用](#2. 透视变换应用)
一、引言
在计算机视觉中,几何变换是图像处理的基础操作之一。除了基本的缩放、旋转和平移外,仿射变换和透视变换是两种更高级的几何变换技术,它们可以实现更复杂的图像变形效果。本文将详细介绍这两种变换的原理和在OpenCV中的实现方法。
二、仿射变换
1. 基本概念
仿射变换是一种保持平行性和共线性的几何变换,它可以看作是平移、旋转、缩放和倾斜的组合。在仿射变换中,原图中的直线变换后仍然是直线,平行线变换后仍然是平行线。
仿射变换的数学表达式为:
x' y'\] = \[x y 1\] \[\[a b\]\[c d\]\[tx ty\]
其中,(x, y)是原图坐标,(x', y')是变换后的坐标,矩阵[[a b][c d]]负责旋转、缩放和倾斜,(tx, ty)负责平移。
2. OpenCV实现
在OpenCV中,仿射变换通过cv2.warpAffine()函数实现,需要先获取仿射变换矩阵。获取变换矩阵有两种方法:
方法1:使用cv2.getAffineTransform()
该函数需要原图中三个点和变换后对应三个点的坐标,自动计算仿射变换矩阵。
//python
原图中的三个点
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
变换后对应的三个点
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
获取仿射变换矩阵
M = cv2.getAffineTransform(pts1, pts2)
进行仿射变换
dst = cv2.warpAffine(img, M, (cols, rows))
方法2:组合基本变换
可以手动构造旋转、缩放、倾斜等基本变换的矩阵,然后组合成仿射变换矩阵。
3. 应用示例:图像校正
仿射变换常用于校正倾斜的图像,例如文档扫描后的校正。
三、透视变换
1. 基本概念
透视变换(也称为投影变换)是一种更一般的几何变换,它可以保持共线性但不保持平行性。透视变换可以将3D空间中的物体投影到2D平面上,常用于实现"鸟瞰图"效果。
透视变换的数学表达式为:
x' y' z'\] = \[x y 1\] \[\[a b c\]\[d e f\]\[g h i\]
其中,(x, y)是原图坐标,(x'/z', y'/z')是变换后的坐标。
2. OpenCV实现
在OpenCV中,透视变换通过cv2.warpPerspective()函数实现,需要先获取透视变换矩阵。获取变换矩阵的方法是使用cv2.getPerspectiveTransform()函数,该函数需要原图中四个点和变换后对应四个点的坐标。
原图中的四个点(四边形)
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
变换后对应的四个点(矩形)
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
获取透视变换矩阵
M = cv2.getPerspectiveTransform(pts1, pts2)
进行透视变换
dst = cv2.warpPerspective(img, M, (300, 300))
3. 应用示例:视角转换
透视变换常用于将倾斜视角的图像转换为正视图,例如将拍摄的名片、文档转换为平面视图。
四、完整代码示例
//python实现
python
import cv2
import numpy as np
import matplotlib.pyplot as plt
读取图像
img = cv2.imread('lena.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
rows, cols, _ = img_rgb.shape
1. 仿射变换
定义原图和变换后的三个点
pts1_affine = np.float32([[50, 50], [200, 50], [50, 200]])
pts2_affine = np.float32([[10, 100], [200, 50], [100, 250]])
获取仿射变换矩阵
M_affine = cv2.getAffineTransform(pts1_affine, pts2_affine)
进行仿射变换
affine_result = cv2.warpAffine(img_rgb, M_affine, (cols, rows))
2. 透视变换
定义原图和变换后的四个点
pts1_perspective = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
pts2_perspective = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
获取透视变换矩阵
M_perspective = cv2.getPerspectiveTransform(pts1_perspective, pts2_perspective)
进行透视变换
perspective_result = cv2.warpPerspective(img_rgb, M_perspective, (300, 300))
显示结果
plt.figure(figsize=(12, 8))
plt.subplot(131)
plt.imshow(img_rgb)
plt.title('Original Image')
plt.axis('off')
plt.subplot(132)
plt.imshow(affine_result)
plt.title('Affine Transformation')
plt.axis('off')
plt.subplot(133)
plt.imshow(perspective_result)
plt.title('Perspective Transformation')
plt.axis('off')
plt.tight_layout()
plt.show()
//C++实现
cpp
include <opencv2/opencv.hpp>
include <iostream>
using namespace cv;
using namespace std;
int main()
{
//读取图像
Mat img = imread("lena.jpg");
if (img.empty()) {
cout << "无法读取图像文件" << endl;
return 1;
}
Mat img_rgb;
cvtColor(img, img_rgb, COLOR_BGR2RGB); // 转换为RGB格式用于显示
int rows = img.rows;
int cols = img.cols;
//1. 仿射变换
//定义原图和变换后的三个点
vector<Point2f> pts1_affine(3);
vector<Point2f> pts2_affine(3);
pts1_affine[0] = Point2f(50, 50);
pts1_affine[1] = Point2f(200, 50);
pts1_affine[2] = Point2f(50, 200);
pts2_affine[0] = Point2f(10, 100);
pts2_affine[1] = Point2f(200, 50);
pts2_affine[2] = Point2f(100, 250);
//获取仿射变换矩阵
Mat M_affine = getAffineTransform(pts1_affine, pts2_affine);
//进行仿射变换
Mat affine_result;
warpAffine(img, affine_result, M_affine, Size(cols, rows));
//2. 透视变换
//定义原图和变换后的四个点
vector<Point2f> pts1_perspective(4);
vector<Point2f> pts2_perspective(4);
pts1_perspective[0] = Point2f(56, 65);
pts1_perspective[1] = Point2f(368, 52);
pts1_perspective[2] = Point2f(28, 387);
pts1_perspective[3] = Point2f(389, 390);
pts2_perspective[0] = Point2f(0, 0);
pts2_perspective[1] = Point2f(300, 0);
pts2_perspective[2] = Point2f(0, 300);
pts2_perspective[3] = Point2f(300, 300);
//获取透视变换矩阵
Mat M_perspective = getPerspectiveTransform(pts1_perspective, pts2_perspective);
//进行透视变换
Mat perspective_result;
warpPerspective(img, perspective_result, M_perspective, Size(300, 300));
//显示结果
namedWindow("Original Image", WINDOW_NORMAL);
namedWindow("Affine Transformation", WINDOW_NORMAL);
namedWindow("Perspective Transformation", WINDOW_NORMAL);
imshow("Original Image", img);
imshow("Affine Transformation", affine_result);
imshow("Perspective Transformation", perspective_result);
waitKey(0);
destroyAllWindows();
return 0;
}
五、仿射变换与透视变换的区别
1、基本概念
仿射变换 :保持平行性和共线性的几何变换,可看作平移、旋转、缩放和倾斜的组合。
透视变换 :更一般的几何变换,只保持共线性,可模拟真实世界的透视效果。
2、核心区别对比
3、应用场景区别
(1)仿射变换适用场景 :
图像校正(如倾斜文档校正)
图像配准
数据增强(生成不同角度的训练样本)
简单的图像变形动画
(2)透视变换适用场景 :
文档扫描(将拍摄的文档转换为平面视图)
视角转换(如将监控画面转换为鸟瞰图)
增强现实(将虚拟物体投影到真实场景)
全景图像拼接
地图投影
4、视觉效果区别
仿射变换 后的图像边缘仍然平行,适合保持物体形状的变换
透视变换会产生近大远小的效果,更符合人眼观察真实世界的方式
六、应用场景
1. 仿射变换应用
- 图像校正:校正倾斜的文档、照片
- 图像配准:将多幅图像对齐到同一坐标系
- 数据增强:生成不同角度、尺度的训练样本
- 动画效果:实现图像的复杂变形动画
2. 透视变换应用
- 文档扫描:将拍摄的文档转换为平面视图
- 地图投影:将3D地球投影到2D地图
- 增强现实:将虚拟物体投影到真实场景
- 全景拼接:将多张照片拼接成全景图
- 交通监控:将监控画面转换为鸟瞰图,便于车辆检测和跟踪
七、注意事项
点的选择:在使用cv2.getAffineTransform()和cv2.getPerspectiveTransform()时,点的选择非常重要,应选择图像中明显的特征点,以提高变换精度。
图像边界:变换后图像可能会超出原图像范围,可以通过设置borderMode参数来处理边界,常用的有cv2.BORDER_CONSTANT(常量填充)和cv2.BORDER_REPLICATE(复制边界像素)。
性能优化:对于大尺寸图像,透视变换可能比较耗时,可以考虑先缩小图像进行变换,再放大到原尺寸。
逆变换:如果需要从变换后的图像恢复原图,可以使用cv2.invertAffineTransform()(仿射变换)或np.linalg.inv()(透视变换)计算逆变换矩阵,然后应用逆变换。
八、总结
本文详细介绍了OpenCV中仿射变换和透视变换的原理、实现方法和应用场景。仿射变换适用于需要保持平行性的场景,而透视变换适用于需要模拟真实透视效果的场景。