图像几何变换--缩放 旋转 偏移

该代码示例展示了如何使用OpenCV库进行图像的仿射变换,包括先进行缩放、然后旋转、最后偏移的过程。通过双线性插值处理像素,确保变换后的图像质量。提供的函数`affine_trans_comb`接收源图像、目标图像及变换参数,生成结果图像。

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

#include <iostream>
#include <opencv2/opencv.hpp>


/*变换顺序:缩放—>旋转—>偏移*/
void affine_trans_comb(cv::Mat & src, cv::Mat & dst, double cx, double cy, double Angle, double sx, double sy)
{
	double angle = Angle * CV_PI / 180;
	//构造输出图像
	int dst_s_rows = round(cy * src.rows);//尺度变换后图像高度
	int dst_s_cols = round(cx * src.cols);//尺度变换后图像宽度

	int dst_sr_rows = round(fabs(dst_s_rows * cos(angle)) + fabs(dst_s_cols * sin(angle)));//再经过旋转后图像高度
	int dst_sr_cols = round(fabs(dst_s_cols * cos(angle)) + fabs(dst_s_rows * sin(angle)));//再经过旋转后图像宽度

	int dst_srd_rows = fabs(sy) * dst_sr_cols + dst_sr_rows;//最后经过偏移后图像高度
	int dst_srd_cols = fabs(sx) * dst_sr_rows + dst_sr_cols;//最后经过偏移后图像宽度

	dst = cv::Mat::zeros(dst_srd_rows, dst_srd_cols, CV_8UC1); //灰度图初始

	cv::Mat T1 = (cv::Mat_<double>(3, 3) << cx, 0, 0, 0, cy, 0, 0, 0, 1); //尺度变换矩阵

	cv::Mat T21 = (cv::Mat_<double>(3, 3) << 1, 0, 0, 0, -1, 0, -0.5 * dst_s_cols, 0.5 * dst_s_rows, 1); // 将尺度变换后的图像坐标映射到数学笛卡尔坐标
	cv::Mat T22 = (cv::Mat_<double>(3, 3) << cos(angle), -sin(angle), 0, sin(angle), cos(angle), 0, 0, 0, 1); //数学笛卡尔坐标下顺时针旋转的变换矩阵
	cv::Mat T2 = T21 * T22;// 这里不需要转回图像坐标了,因为下面的偏移变换是在笛卡尔坐标下进行的

	cv::Mat T32 = (cv::Mat_<double>(3, 3) << 1, sy, 0, sx, 1, 0, 0, 0, 1); //数学笛卡尔坐标偏移变换矩阵
	double t33[3][3] = { { 1, 0, 0 }, { 0, -1, 0 }, { 0.5 * dst.cols, 0.5 * dst.rows, 1 } }; // 将数学笛卡尔坐标映射到偏移后的图像坐标
	cv::Mat T33 = cv::Mat(3, 3, CV_64FC1, t33);
	cv::Mat T3 = T32 * T33;

	cv::Mat T = T1 * T2 * T3; //矩阵相乘按照组合变换的顺序
	cv::Mat T_inv = T.inv(); // 求逆矩阵

	for (int i = 0; i < dst.rows; i++) 
	{
		for (int j = 0; j < dst.cols; j++)
		{
			cv::Mat dst_coordinate = (cv::Mat_<double>(1, 3) << j, i, 1);
			cv::Mat src_coordinate = dst_coordinate * T_inv;
			double v = src_coordinate.at<double>(0, 0); // 原图像的横坐标,列,宽
			double w = src_coordinate.at<double>(0, 1); // 原图像的纵坐标,行,高

			/*双线性插值*/
			// 判断是否越界
			if (int(Angle) % 90 == 0) 
			{
				if (v < 0) v = 0; if (v > src.cols - 1) v = src.cols - 1;
				if (w < 0) w = 0; if (w > src.rows - 1) w = src.rows - 1; //必须要加上,否则会出现边界问题
			}

			if (v >= 0 && w >= 0 && v <= src.cols - 1 && w <= src.rows - 1) 
			{
				int top = floor(w), bottom = ceil(w), left = floor(v), right = ceil(v); //与映射到原图坐标相邻的四个像素点的坐标
				double pw = w - top; //pw为坐标 行 的小数部分(坐标偏差)
				double pv = v - left; //pv为坐标 列 的小数部分(坐标偏差)
				dst.at<uchar>(i, j) = (1 - pw) * (1 - pv) * src.at<uchar>(top, left) + (1 - pw) * pv * src.at<uchar>(top, right) \
					+ pw * (1 - pv) * src.at<uchar>(bottom, left) + pw * pv * src.at<uchar>(bottom, right);
			}
		}
	}
}


int main() 
{
	cv::Mat src = cv::imread("1.jpg", 0), dst;

	double angle = 250;  //旋转角度
	double cx = 1.5, cy = 1.5; //缩放尺度
	double sx = 0.2, sy = 0.2; //偏移尺度

	affine_trans_comb(src, dst, cx, cy, angle, sx, sy); // 缩放->旋转—>偏移

	cv::imwrite("result.jpg", dst);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值