OpenCV——仿射变换

本文详细介绍了如何使用OpenCV实现图像的仿射变换和旋转,包括定义源图像和目标图像上的对应点,求解仿射变换矩阵,以及对图像进行扭曲和旋转的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是仿射变换?

  1. 一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移).

  2. 综上所述, 我们能够用仿射变换来表示:

    1. 旋转 (线性变换)
    2. 平移 (向量加)
    3. 缩放操作 (线性变换)

    你现在可以知道, 事实上, 仿射变换代表的是两幅图之间的 关系 .

              我们通常使用 2 \times 3 矩阵来表示仿射变换.

A = \begin{bmatrix}      a_{00} & a_{01} \\      a_{10} & a_{11}      \end{bmatrix}_{2 \times 2}  B = \begin{bmatrix}      b_{00} \\      b_{10}      \end{bmatrix}_{2 \times 1}   M = \begin{bmatrix}      A & B      \end{bmatrix}  = \begin{bmatrix}      a_{00} & a_{01} & b_{00} \\      a_{10} & a_{11} & b_{10} \end{bmatrix}_{2 \times 3}

             考虑到我们要使用矩阵 A 和 B 对二维向量 X = \begin{bmatrix}x \\ y\end{bmatrix} 做变换, 所以也能表示为下列形式:

T =  \begin{bmatrix}     a_{00}x + a_{01}y + b_{00} \\     a_{10}x + a_{11}y + b_{10}     \end{bmatrix}

 

如何求怎样才能求得一个仿射变换?

 

实现代码:

 

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>

using namespace cv;
using namespace std;

/// 全局变量
char* source_window = "Source image";
char* warp_window = "Warp";
char* warp_rotate_window = "Warp + Rotate";

/** @function main */
int main(int argc, char** argv)
{
	Point2f srcTri[3];
	Point2f dstTri[3];

	Mat rot_mat(2, 3, CV_32FC1);
	Mat warp_mat(2, 3, CV_32FC1);
	Mat src, warp_dst, warp_rotate_dst;

	/// 加载源图像
	src = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\03.jpg", 1);

	/// 设置目标图像的大小和类型与源图像一致
	warp_dst = Mat::zeros(src.rows, src.cols, src.type());

	/// 设置源图像和目标图像上的三组点以计算仿射变换
	srcTri[0] = Point2f(0, 0);
	srcTri[1] = Point2f(src.cols - 1, 0);
	srcTri[2] = Point2f(0, src.rows - 1);

	dstTri[0] = Point2f(src.cols*0.0, src.rows*0.33);
	dstTri[1] = Point2f(src.cols*0.85, src.rows*0.25);
	dstTri[2] = Point2f(src.cols*0.15, src.rows*0.7);

	/// 求得仿射变换
	warp_mat = getAffineTransform(srcTri, dstTri);

	/// 对源图像应用上面求得的仿射变换
	warpAffine(src, warp_dst, warp_mat, warp_dst.size());

	/** 对图像扭曲后再旋转 */

	/// 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵
	Point center = Point(warp_dst.cols / 2, warp_dst.rows / 2);
	double angle = -50.0;
	double scale = 0.6;

	/// 通过上面的旋转细节信息求得旋转矩阵
	rot_mat = getRotationMatrix2D(center, angle, scale);

	/// 旋转已扭曲图像
	warpAffine(warp_dst, warp_rotate_dst, rot_mat, warp_dst.size());

	/// 显示结果
	namedWindow(source_window, CV_WINDOW_AUTOSIZE);
	imshow(source_window, src);

	namedWindow(warp_window, CV_WINDOW_AUTOSIZE);
	imshow(warp_window, warp_dst);

	namedWindow(warp_rotate_window, CV_WINDOW_AUTOSIZE);
	imshow(warp_rotate_window, warp_rotate_dst);

	/// 等待用户按任意按键退出程序
	waitKey(0);

	return 0;
}

 结果如下:

代码解析:

 仿射变换: 正如上文所说, 我们需要源图像和目标图像上分别一一映射的三个点来定义仿射变换:

/// 设置源图像和目标图像上的三组点以计算仿射变换
    srcTri[0] = Point2f(0, 0);
    srcTri[1] = Point2f(src.cols - 1, 0);
    srcTri[2] = Point2f(0, src.rows - 1);

    dstTri[0] = Point2f(src.cols*0.0, src.rows*0.33);
    dstTri[1] = Point2f(src.cols*0.85, src.rows*0.25);
    dstTri[2] = Point2f(src.cols*0.15, src.rows*0.7);

    /// 求得仿射变换
    warp_mat = getAffineTransform(srcTri, dstTri);

我们获得了用以描述仿射变换的 2 \times 3 矩阵 (在这里是 warp_mat)

将刚刚求得的仿射变换应用到源图像

warpAffine()函数有以下参数:
  • src: 输入源图像
  • warp_dst: 输出图像
  • warp_mat: 仿射变换矩阵
  • warp_dst.size(): 输出图像的尺寸
/// 对源图像应用上面求得的仿射变换
    warpAffine(src, warp_dst, warp_mat, warp_dst.size());

旋转: 想要旋转一幅图像, 你需要两个参数:

  1. 旋转图像所要围绕的中心
  2. 旋转的角度. 在OpenCV中正角度是逆时针的
  3. 可选择: 缩放因子

我们通过下面的代码来定义这些参数:

再利用OpenCV函数 getRotationMatrix2D 来获得旋转矩阵, 这个函数返回一个 2 \times 3 矩阵 (这里是 rot_mat)

最后把旋转应用到仿射变换的输出.

/// 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵
    Point center = Point(warp_dst.cols / 2, warp_dst.rows / 2);
    double angle = -50.0;
    double scale = 0.6;

    /// 通过上面的旋转细节信息求得旋转矩阵
    rot_mat = getRotationMatrix2D(center, angle, scale);

    /// 旋转已扭曲图像
    warpAffine(warp_dst, warp_rotate_dst, rot_mat, warp_dst.size());

 

转载于:https://www.cnblogs.com/fcfc940503/p/11305485.html

### OpenCV仿射变换的几何性质解释 #### 什么是仿射变换仿射变换是一种二维坐标到二维坐标的线性变换,它保持了共线性和比例关系不变。具体来说,在仿射变换下,直线仍然会映射成直线,并且平行线仍保持平行[^1]。 #### 几何性质描述 仿射变换由六个自由度组成:两个旋转角度、两个缩放因子以及两个平移向量 \(t_x\) 和 \(t_y\)。其矩阵形式如下所示: \[ M = \begin{bmatrix} a & b & t_x \\ c & d & t_y \\ 0 & 0 & 1 \end{bmatrix} \] - **旋转**:通过调整参数 \(a, b, c,\) 和 \(d\) 可以实现图像绕某个中心点的旋转操作[^1]。 - **缩放**:\(a\) 和 \(d\) 控制着水平方向和垂直方向上的尺度变化;当它们大于零小于一的时候代表缩小,反之则放大[^1]。 - **剪切**:如果仅改变其中一个维度的比例而另一个固定,则会产生倾斜效果即所谓的“shearing”现象。 - **平移**:最后两项 \(t_x\) 和 \(t_y\) 负责整体位移,使得整个图形可以在平面内移动而不发生形变[^2]。 这些基本的操作组合起来能够完成复杂的空间转换需求,比如校正透视失真或者匹配不同视角下的同一场景对象等应用场合。 ```python import numpy as np import cv2 # 定义原始图片中的三个点及其对应的目标位置 pts_src = np.float32([[50, 50], [200, 50], [50, 200]]) pts_dst = np.float32([[10, 100], [200, 50], [100, 250]]) # 计算仿射变换矩阵 M M = cv2.getAffineTransform(pts_src, pts_dst) # 应用仿射变换至输入图像 img_input 上得到结果图 img_output img_output = cv2.warpAffine(img_input, M, (cols, rows)) ``` 以上代码片段展示了如何基于给定点集计算出相应的仿射变换矩阵并应用于实际图像之上。 #### 插值的作用 由于仿射变换可能不会正好落在整数像素网格上,因此需要采用某种方法估计新位置处的颜色值。这就是为什么提到过插值的重要性——为了使最终输出更加连续和平滑。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值