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 个点的点集(它们分别定义了两个平行四边形 ),才能推导出仿射变换关系:

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);

(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;
	}
}
相关推荐
赋创小助手1 小时前
企业开始批量部署Qwen3.6后,AI服务器应该怎么选?
运维·服务器·人工智能
星恒随风1 小时前
从机器学习基础到 MLP(下):神经网络为什么能起作用?
人工智能·笔记·神经网络·学习·机器学习
承渊政道1 小时前
RAG:让大模型从“会回答“走向“有依据地回答“
人工智能·自然语言处理·chatgpt·架构·数据挖掘·langchain·全文检索
未来和明天1 小时前
领嵌AI边缘计算开发高算力低功耗数据采集行为分析-边缘计算盒子
人工智能·边缘计算
草莓熊Lotso1 小时前
【Linux网络】打造工业级 TCP 自定义协议网络计算器:从理论到手写实现
linux·运维·服务器·网络·人工智能·网络协议·tcp/ip
z小猫不吃鱼1 小时前
02 从 RNN 到 Transformer:为什么语言建模需要新结构?
人工智能·rnn·transformer
生成论实验室1 小时前
如何让AI成为生产力工具——判断力是最后的拼图
人工智能·深度学习·语言模型·agi·安全架构
Deepoch1 小时前
Deepoc开发板:基于VLA的采摘机器人多模态自主决策系统
人工智能·机器人·开发板·具身模型·deepoc
小宋0011 小时前
电脑选项建议及深度学习环境安装指导(旧资料整理)
人工智能·深度学习·电脑