OpenCV图像处理——仿射变换

目录

[1. cv::warpAffine():稠密仿射变换(Dense affine transformations)](#1. cv::warpAffine():稠密仿射变换(Dense affine transformations))

[1.1 参数说明](#1.1 参数说明)

[1.2 C++ 代码](#1.2 C++ 代码)

[1.3 代码说明](#1.3 代码说明)

[1.4 运行结果](#1.4 运行结果)

[2. cv::transform():稀疏仿射变换(Sparse affine transformations)](#2. cv::transform():稀疏仿射变换(Sparse affine transformations))


对于仿射变换,存在两种情况。第一种情况是,我们有一幅图像(或感兴趣区域),我们希望对其进行变换;而第二种情况是,我们有一个点的列表,我们想计算变换结果。这两种情况在概念上非常类似,但就实践实现而言,却非常不同。因此,OpenCV 在处理这两种情况时,分别使用了两个不同的函数。

1. cv::warpAffine():稠密仿射变换(Dense affine transformations)

在第一种情况下,输入输出格式都是图像,而隐含的要求是,该变形(warp)假设变换的像素是底层图像的稠密表示。这意味着,图像变形必须处理插值,以使得输出图像平滑从而看起来自然。而OpenCV 提供的稠密变换函数是 cv::warpAffine(...):

cpp 复制代码
void cv::warpAffine(

InputArray src,

OutputArray dst,

InputArray M,

Size dsize,

int flags=INTER_LINEAR,

int borderMode=BORDER_CONSTANT,

const Scalar& borderValue=Scalar());

1.1 参数说明

(1) src------输入图像

(2) dst------输出图像

(3) M ------ 一个 2 × 3 变换矩阵

(4) 对于旋转,可以使用函数 cv::getRotationMatrix2D 获得旋转矩阵,而对于一般仿射变换,可以通过提供3对对应点而使用 cv::getAffineTransform 求得变换矩阵。这3对点可以是原图像内的任意3点。

(5) dsize ------输出图像的大小(width,height)。

(6) flags------(内)插值方法(例如,INTER_LINEAR, INTER_NEAREST)的组合及可选项 WARP_INVERSE_MAP 标识。

(7) borderValue------这些设置决定了在变换后,如何填充落在原始图像边界之外的像素。

1.2 C++ 代码

cpp 复制代码
const char* source_window = "Source image";
const char* warp_window = "Warp";
const char* warp_rotate_window = "Warp + Rotate";

void Test_warpAffine()
{
	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("D:\\TestVideo\\Flower6-2.jpg", IMREAD_COLOR);

	warp_dst = Mat::zeros(src.rows, src.cols, src.type());

	srcTri[0] = Point2f(0, 0);
	srcTri[1] = Point2f(src.cols - 1.f, 0);
	srcTri[2] = Point2f(0, src.rows - 1.f);

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

	warp_mat = getAffineTransform(srcTri, dstTri);

	warpAffine(src, warp_dst, warp_mat, warp_dst.size());

	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, WINDOW_AUTOSIZE);
	imshow(source_window, src);

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

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

	waitKey(0);
}

1.3 代码说明

(1) 将目标图像初始化为与源图像具有相同的大小和类型:

warp_dst = Mat::zeros(src.rows, src.cols, src.type());

(2) 将目标图像初始化为与源图像具有相同的大小和类型:

warp_dst = Mat::zeros(src.rows, src.cols, src.type());

(3) 仿射变换:我们需要两组各包含 3 个点的点集(它们分别定义了两个平行四边形 ),才能推导出仿射变换关系:

srcTri0 = Point2f(0, 0);

srcTri1 = Point2f(src.cols - 1.f, 0);

srcTri2 = Point2f(0, src.rows - 1.f);

dstTri0 = Point2f(src.cols * 0.0f, src.rows * 0.33f);

dstTri1 = Point2f(src.cols * 0.85f, src.rows * 0.25f);

dstTri2 = Point2f(src.cols * 0.15f, src.rows * 0.7f);

(4) 利用这两组点,我们调用 OpenCV 函数 cv::getAffineTransform 来计算仿射变换:

该函数的返回值是一个 2 × 3 矩阵本质上,函数 getAffineTransform 中的两个点数组src 和 dst定义了两个平行四边形

1.4 运行结果

变换前图像:

仿射变换后的图像(扭曲状):

仿射变换后再绕图像中心旋转后的图像:

2. cv::transform():稀疏仿射变换(Sparse affine transformations)

对于稀疏映射(即单点列表映射),最好使用函数 cv::transform(),其函数原型为:

cpp 复制代码
void cv::transform(cv::InputArray src,  // N × 1 数组(数据通道)

cv::OutputArray dst, // N × 1 数组(数据通道)

cv::InputArray mtx); // 变换矩阵

使用示例 (注意:不同的 OpenCV 版本可能有所差异 ):

cpp 复制代码
void Test_transform()
{
	// 1. Create a set of points (e.g., vertices of a triangle)
	std::vector<cv::Point2f> srcPoints = { {0, 0}, {100, 0}, {50, 100} };
	std::vector<cv::Point2f> dstPoints;

	// 2. Define a 2x3 transformation matrix (Affine)
	// This matrix rotates by 45 degrees and translates by (10, 20)
	cv::Matx23f transMat(0.707, -0.707, 10,
		0.707, 0.707, 20);

	// 3. Apply the transformation
	cv::transform(srcPoints, dstPoints, transMat);

	// Output transformed points
	for (const auto& p : dstPoints) {
		std::cout << "Point: (" << p.x << ", " << p.y << ")" << std::endl;
	}
}
相关推荐
小小测试开发5 小时前
Playwright vs Selenium vs Cypress:从浏览器协议到 API 设计的全面对比与实测
人工智能·selenium·测试工具
Ai_easygo5 小时前
AI Agent开发入门——从ReAct到Tool Calling,拆解Agent的底层运行逻辑
前端·人工智能·react.js
IT_陈寒6 小时前
Redis的持久化配置把我坑惨了:你以为数据安全了?
前端·人工智能·后端
miaowu3576 小时前
AI智能体推动数字化转型:从流程自动化到决策辅助的完整路线图
大数据·人工智能·自动化
阿里云大数据AI技术6 小时前
阿里云 Elasticsearch 日志采集与加工服务:让日志链路少一串组件,多一份稳定
人工智能·elasticsearch
ksueh6 小时前
AI写小说长篇创作中的上下文局限与外部记忆系统实践
人工智能
互联网江湖6 小时前
珞石机器人:向左科技股,向右零部件制造商?
大数据·人工智能
阿里云大数据AI技术7 小时前
从数据湖到多模态湖仓-基于阿里云EMR Serverless StarRocks与DLF Paimon构建AI时代的统一分析检索架构
人工智能
前端的阶梯7 小时前
浅谈Workflow和Agent的区别
人工智能
正在走向自律7 小时前
Deepseek V4 Flash 高效应用实战指南
人工智能·deepseek·deepseek v4·ai赋能中心