#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;
}
图像几何变换--缩放 旋转 偏移
最新推荐文章于 2023-09-25 18:07:37 发布