OpenCV仿射变换与透视变换实战

目录

一、引言

二、仿射变换

[1. 基本概念](#1. 基本概念)

[2. OpenCV实现](#2. OpenCV实现)

[3. 应用示例:图像校正](#3. 应用示例:图像校正)

三、透视变换

[1. 基本概念](#1. 基本概念)

[2. OpenCV实现](#2. OpenCV实现)

[3. 应用示例:视角转换](#3. 应用示例:视角转换)

四、完整代码示例

五、仿射变换与透视变换的区别

1、基本概念

2、核心区别对比​编辑

3、应用场景区别

4、视觉效果区别

六、应用场景

[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地图
  • 增强现实:将虚拟物体投影到真实场景
  • 全景拼接:将多张照片拼接成全景图
  • 交通监控:将监控画面转换为鸟瞰图,便于车辆检测和跟踪

七、注意事项

  1. 点的选择:在使用cv2.getAffineTransform()和cv2.getPerspectiveTransform()时,点的选择非常重要,应选择图像中明显的特征点,以提高变换精度。

  2. 图像边界:变换后图像可能会超出原图像范围,可以通过设置borderMode参数来处理边界,常用的有cv2.BORDER_CONSTANT(常量填充)和cv2.BORDER_REPLICATE(复制边界像素)。

  3. 性能优化:对于大尺寸图像,透视变换可能比较耗时,可以考虑先缩小图像进行变换,再放大到原尺寸。

  4. 逆变换:如果需要从变换后的图像恢复原图,可以使用cv2.invertAffineTransform()(仿射变换)或np.linalg.inv()(透视变换)计算逆变换矩阵,然后应用逆变换。

八、总结

本文详细介绍了OpenCV中仿射变换和透视变换的原理、实现方法和应用场景。仿射变换适用于需要保持平行性的场景,而透视变换适用于需要模拟真实透视效果的场景。

相关推荐
之歆4 小时前
Spring AI入门到实战到原理源码-MCP
java·人工智能·spring
知乎的哥廷根数学学派4 小时前
面向可信机械故障诊断的自适应置信度惩罚深度校准算法(Pytorch)
人工智能·pytorch·python·深度学习·算法·机器学习·矩阵
且去填词4 小时前
DeepSeek :基于 Schema 推理与自愈机制的智能 ETL
数据仓库·人工智能·python·语言模型·etl·schema·deepseek
待续3014 小时前
订阅了 Qoder 之后,我想通过这篇文章分享一些个人使用心得和感受。
人工智能
weixin_397578024 小时前
人工智能发展历史
人工智能
强盛小灵通专卖员4 小时前
基于深度学习的山体滑坡检测科研辅导:从论文实验到系统落地的完整思路
人工智能·深度学习·sci·小论文·山体滑坡
OidEncoder4 小时前
从 “粗放清扫” 到 “毫米级作业”,编码器重塑环卫机器人新能力
人工智能·自动化·智慧城市
Hcoco_me5 小时前
大模型面试题61:Flash Attention中online softmax(在线softmax)的实现方式
人工智能·深度学习·自然语言处理·transformer·vllm
阿部多瑞 ABU5 小时前
`chenmo` —— 可编程元叙事引擎 V2.3+
linux·人工智能·python·ai写作
极海拾贝5 小时前
GeoScene解决方案中心正式上线!
大数据·人工智能·深度学习·arcgis·信息可视化·语言模型·解决方案