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;
	}
}
相关推荐
fthux21 小时前
装闭 RenoPit 源码解析(07):装修闭坑知识库与AI Prompt构建
人工智能·ai·开源·github·open source·renopit
zyplayer-doc1 天前
VuePress类静态文档站和动态知识库怎么选:两种技术路线的适用场景
javascript·人工智能·后端·安全·智能手机
KKKlucifer1 天前
拨开接口黑盒迷雾:运营商第三方合作接口安全审计与准入管控落地实践
网络·人工智能·安全
梦梦代码精1 天前
连锁品牌数字化:从门店扩张到用户资产运营的技术底座
大数据·人工智能·低代码·docker·开源·代码规范
咕噜咕噜啦啦1 天前
vLLM框架
人工智能·qwen·vllm
启雀AI1 天前
AI 驱动的视频课程自动摘要与知识点提取:ASR + LLM 流水线工程实践
人工智能·阿里云·华为云·音视频·培训saas平台
Mac的实验室1 天前
2026年8月最新实操:谷歌Gmail邮箱手机号注册扫码发短信提示“无法验证”怎么办?(附100%成功绕过指南)
人工智能
MindUp1 天前
AI辅助PPT生成工具的内容组织能力实测:8款产品的文档解析与排版效果对比
人工智能
寒草1 天前
【寒草呈献】当巴菲特走进 AI 投研助手
人工智能·架构