目录

  • 一.图像几何变换基础
  • 二.平移
  • 三.缩放
  • 四.旋转
  • 五.镜像
  • 六.复合变换
  • 七.变换矩阵总结
  • 八.完整代码

一.图像几何变换基础

1.图像的几何变换是指原始图像按照需要产生大小、形状和位置的变化。
2.不改变图像的像素值,而是改变像素所在的几何位置。
3.对于原图像f(x,y),坐标变换函数

4.唯一确定了几何变换:

g(x,y)是目标图像。
5.图像是矩阵,图像的几何变换用矩阵运算来实现。

6.在数字图像处理中,几何变换由两个基本操作组成:
(1)坐标的空间变换
坐标变换可由下式表示:

其中,(v,w)是原图像中像素的坐标,(x,y)是变换后图像中像素的坐标。
最常用的空间坐标变换之一是仿射变换,其一般形式如下:

这个变换可根据矩阵T中元素所选择的值,对一组坐标点做尺度、旋转、平移或偏移。
(2)灰度内插,即对空间变换后的像素赋灰度值。

二.平移


坐标变换函数:

用矩阵的形式可表示为:

(1)齐次坐标
通常将2×3阶矩阵扩充为3×3阶矩阵,以拓宽功能。
点P(x,y)按照3×3的变换矩阵T平移变换的结果。

可以看出,引入附加坐标后,扩充了矩阵的第3行,并没有使变换结果受到影响。
这种用n+1维向量表示n维向量的方法称为齐次坐标表示法。
(2)平移的变换矩阵

//平移变换
//tx:水平平移距离,正数向右移动,负数向左移动
//ty:垂直平移距离,正数向下移动,负数向上移动
void trans(Mat& src, Mat& dst, double tx, double ty)
{//创建输出图像dst.create(src.size(), src.type());//平移变换矩阵Mat T = (Mat_<double>(3, 3) << 1, 0, 0, 0, 1, 0, tx, ty, 1);//求逆矩阵Mat T_inv = T.inv();for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){Mat dst_coordinate = (Mat_<double>(1, 3) << j, i, 1);Mat src_coordinate = dst_coordinate * T_inv;double v = src_coordinate.at<double>(0, 0);  //原图像的横坐标,列,宽double w = src_coordinate.at<double>(0, 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为坐标 列 的小数部分if (src.channels() == 1){//灰度图像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);}else{//彩色图像dst.at<Vec3b>(i, j)[0] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[0]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[0]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[0]+ pw * pv * src.at<Vec3b>(bottom, right)[0];dst.at<Vec3b>(i, j)[1] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[1]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[1]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[1]+ pw * pv * src.at<Vec3b>(bottom, right)[1];dst.at<Vec3b>(i, j)[2] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[2]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[2]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[2]+ pw * pv * src.at<Vec3b>(bottom, right)[2];}}}}}

三.缩放

(1)变换矩阵
图像的几何变换改变了像素的空间位置,建立一种原图像像素与变换后图像像素之间的映射关系,通过这种映射关系能够实现下面两种计算:
向前映射:原图像任意像素计算该像素在变换后图像的坐标位置。
向后映射:换后图像的任意像素在原图像的坐标位置。

(2)插值
①最近邻插值
赋予点(x,y)在像素矩阵中离它最近的点的像素值。
最近邻插值的定位误差最大是半个像素。这种误差在物体具有直线边界时就会显现出来,在变换后可能会呈现阶梯状。

//最近邻差值缩放
void nearest(Mat& src, Mat& dst, float sx, float sy)  //原图,处理后图,x方向倍数,y方向倍数
{//round函数计算之后结果是小数点后全变为0,但其还是float的,需要强制类型转换//计算目标图形尺寸int dst_cols = (int)round(src.cols * sx);int dst_rows = (int)round(src.rows * sy);//创建目标图形dst.create(dst_rows, dst_cols, src.type());//灰度图像处理if (src.channels() == 1){for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){//插值计算,输出图像的像素点由原图像对应的最近的像素点得到(四舍五入)int i_index = (int)round(i / sy);    //y方向缩放,则行数发生改变int j_index = (int)round(j / sx);//防止越界if (i_index > src.rows - 1){i_index = src.rows - 1;}if (j_index > src.cols - 1){j_index = src.cols - 1;}dst.at<uchar>(i, j) = src.at<uchar>(i_index, j_index);}}}//彩色图像处理else{for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){//差值计算int i_index = (int)round(i / sy);int j_index = (int)round(j / sx);//防止越界if (i_index > src.rows - 1){i_index = src.rows - 1;}if (j_index > src.cols - 1){j_index = src.cols - 1;}//bdst.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i_index, j_index)[0];//gdst.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i_index, j_index)[1];//rdst.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i_index, j_index)[2];}}}
}

②双线性插值
当求出的分数地址与像素点不一致时,求出周围四个像素点的距离比,根据该比率,由四个邻域的像素灰度值进行线性插值。

void linear(Mat& src, Mat& dst, float sx, float sy)
{//计算目标图形尺寸int dst_cols = (int)round(src.cols * sy);int dst_rows = (int)round(src.rows * sx);//创建目标图形dst.create(dst_rows, dst_cols, src.type());for (int i = 0; i < dst.rows; i++){//几何中心对齐//中心对齐(OpenCV也是如此)://SrcX = (dstX + 0.5) * (srcWidth / dstWidth) - 0.5//SrcY = (dstY + 0.5) * (srcHeight / dstHeight) - 0.5double index_i = (i + 0.5) / sx - 0.5;//防止越界if (index_i < 0){index_i = 0;}if (index_i >= (double)src.rows - 1){index_i = (double)src.rows - 2;}//相邻4×4像素的行(坐标)int i1 = floor(index_i);int i2 = ceil(index_i);//u为得到浮点型坐标的小数部分double u = index_i - i1;for (int j = 0; j < dst.cols; j++){//几何中心对齐double index_j = (j + 0.5) / sy - 0.5;//防止越界if (index_j < 0){index_j = 0;}if (index_j >= (double)src.cols - 1){index_j = (double)src.cols - 2;}//相邻4×4像素的列(坐标)int j1 = floor(index_j);int j2 = ceil(index_j);//u为得到浮点型坐标的小数部分double v = index_j - j1;//处理灰度图像if (src.channels() == 1){dst.at<uchar>(i, j) = (1 - u) * (1 - v) * src.at<uchar>(i1, j1)+ (1 - u) * v * src.at<uchar>(i1, j2)+ u * (1 - v) * src.at<uchar>(i2, j1)+ u * v * src.at<uchar>(i2, j2);}//处理彩色图像else{dst.at<Vec3b>(i, j)[0] = (1 - u) * (1 - v) * src.at<Vec3b>(i1, j1)[0] + (1 - u) * v * src.at<Vec3b>(i1, j2)[0]+ u * (1 - v) * src.at<Vec3b>(i2, j1)[0] + u * v * src.at<Vec3b>(i2, j2)[0];dst.at<Vec3b>(i, j)[1] = (1 - u) * (1 - v) * src.at<Vec3b>(i1, j1)[1] + (1 - u) * v * src.at<Vec3b>(i1, j2)[1] + u * (1 - v) * src.at<Vec3b>(i2, j1)[1] + u * v * src.at<Vec3b>(i2, j2)[1];dst.at<Vec3b>(i, j)[2] = (1 - u) * (1 - v) * src.at<Vec3b>(i1, j1)[2]+ (1 - u) * v * src.at<Vec3b>(i1, j2)[2]+ u * (1 - v) * src.at<Vec3b>(i2, j1)[2]+ u * v * src.at<Vec3b>(i2, j2)[2];}}}
}

四.旋转

1.一般图像的旋转是以图像的中心为原点,将图像上的所有像素都旋转一个相同的角度。
2.图像的旋转变换是图像的位置变换,但旋转后,图像的大小一般会改变。
3.图像旋转变换后,既可以把转出显示区域的图像截去,也可以扩大图像范围以显示所有的图像。
4.变换矩阵

//图像旋转
void img_rotate(Mat& src, Mat& dst, double Angle)
{double angle = Angle * CV_PI / 180.0;//构造输出图形int dst_rows = (int)round(fabs(src.rows * cos(angle)) + fabs(src.cols * sin(angle)));//图像高度int dst_cols = (int)round(fabs(src.cols * cos(angle)) + fabs(src.rows * sin(angle)));//图像宽度dst.create(dst_rows, dst_cols, src.type());//将原图像坐标映射到数学笛卡尔坐标Mat T1 = (Mat_<double>(3, 3) << 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.5 * src.cols, 0.5 * src.rows, 1.0);//数学笛卡尔坐标下顺时针旋转的变换矩阵Mat T2 = (Mat_<double>(3, 3) << cos(angle), -sin(angle), 0.0, sin(angle), cos(angle), 0.0, 0.0, 0.0, 1.0);//将数学笛卡尔坐标映射到旋转后的图像坐标double t3[3][3] = { {1.0,0.0,0.0},{0.0,-1.0,0.0},{0.5 * dst.cols,0.5 * dst.rows,1.0} };Mat T3 = Mat(3.0, 3.0, CV_64FC1, t3);Mat T = T1 * T2 * T3;Mat T_inv = T.inv();    //求逆矩阵for (double i = 0.0; i < dst.rows; i++){for (double j = 0.0; j < dst.cols; j++){Mat dst_coordinate = (Mat_<double>(1, 3) << j, i, 1.0);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为坐标 列 的小数部分if (src.channels() == 1){//灰度图像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);}else{//彩色图像dst.at<Vec3b>(i, j)[0] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[0]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[0]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[0]+ pw * pv * src.at<Vec3b>(bottom, right)[0];dst.at<Vec3b>(i, j)[1] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[1]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[1]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[1]+ pw * pv * src.at<Vec3b>(bottom, right)[1];dst.at<Vec3b>(i, j)[2] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[2]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[2]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[2]+ pw * pv * src.at<Vec3b>(bottom, right)[2];}}}}
}

五.镜像

1.水平镜像操作是将图像左半部分和右半部分以图像垂直中轴线为中心进行镜像对换。
2.垂直镜像操作是将图片上半部分和下半部分以图像水平中轴线为中心进行镜像对换。

3.变换矩阵

六.复合变换

1.图像的复合变换是指对给定的图像连续施行若干次如前所述的平移、镜像、比例缩放、旋转等基本操作后所完成的变换,图像的复合变换又叫级联变换。
2.利用齐次坐标,对给定的图像依次按一定顺序连续施行若干次基本变换,其变换的矩阵仍可以用3×3阶的矩阵表示。复合变换的矩阵等于基本变换的矩阵按顺序依次相乘得到的组合矩阵。
3.设对给定的图像依次进行了基本变换F1,F2,…,FN,他们的变换矩阵分别为T1,T2,…TN,按照几何变换公式的表示形式,图像复合变换的矩阵T可以表示为:T=TNTN-1···T1

七.变换矩阵总结

八.完整代码

#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<iostream>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/imgproc/types_c.h>using namespace cv;
using namespace std;//最近邻差值缩放
void nearest(Mat& src, Mat& dst, float sx, float sy)  //原图,处理后图,x方向倍数,y方向倍数
{//round函数计算之后结果是小数点后全变为0,但其还是float的,需要强制类型转换//计算目标图形尺寸int dst_cols = (int)round(src.cols * sx);int dst_rows = (int)round(src.rows * sy);//创建目标图形dst.create(dst_rows, dst_cols, src.type());//灰度图像处理if (src.channels() == 1){for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){//插值计算,输出图像的像素点由原图像对应的最近的像素点得到(四舍五入)int i_index = (int)round(i / sy);    //y方向缩放,则行数发生改变int j_index = (int)round(j / sx);//防止越界if (i_index > src.rows - 1){i_index = src.rows - 1;}if (j_index > src.cols - 1){j_index = src.cols - 1;}dst.at<uchar>(i, j) = src.at<uchar>(i_index, j_index);}}}//彩色图像处理else{for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){//差值计算int i_index = (int)round(i / sy);int j_index = (int)round(j / sx);//防止越界if (i_index > src.rows - 1){i_index = src.rows - 1;}if (j_index > src.cols - 1){j_index = src.cols - 1;}//bdst.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i_index, j_index)[0];//gdst.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i_index, j_index)[1];//rdst.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i_index, j_index)[2];}}}
}//双线性插值缩放
//对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(i+u,j+v) (其中i、j均为浮点坐标的整数部分,
//u、v为浮点坐标的小数部分,是取值[0,1)区间的浮点数),则这个像素得值 f(i+u,j+v) 可由原图像中坐标为
//(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:
//f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)
//其中f(i, j)表示源图像(i, j)处的的像素值,以此类推。
void linear(Mat& src, Mat& dst, float sx, float sy)
{//计算目标图形尺寸int dst_cols = (int)round(src.cols * sy);int dst_rows = (int)round(src.rows * sx);//创建目标图形dst.create(dst_rows, dst_cols, src.type());for (int i = 0; i < dst.rows; i++){//几何中心对齐//中心对齐(OpenCV也是如此)://SrcX = (dstX + 0.5) * (srcWidth / dstWidth) - 0.5//SrcY = (dstY + 0.5) * (srcHeight / dstHeight) - 0.5double index_i = (i + 0.5) / sx - 0.5;//防止越界if (index_i < 0){index_i = 0;}if (index_i >= (double)src.rows - 1){index_i = (double)src.rows - 2;}//相邻4×4像素的行(坐标)int i1 = floor(index_i);int i2 = ceil(index_i);//u为得到浮点型坐标的小数部分double u = index_i - i1;for (int j = 0; j < dst.cols; j++){//几何中心对齐double index_j = (j + 0.5) / sy - 0.5;//防止越界if (index_j < 0){index_j = 0;}if (index_j >= (double)src.cols - 1){index_j = (double)src.cols - 2;}//相邻4×4像素的列(坐标)int j1 = floor(index_j);int j2 = ceil(index_j);//u为得到浮点型坐标的小数部分double v = index_j - j1;//处理灰度图像if (src.channels() == 1){dst.at<uchar>(i, j) = (1 - u) * (1 - v) * src.at<uchar>(i1, j1)+ (1 - u) * v * src.at<uchar>(i1, j2)+ u * (1 - v) * src.at<uchar>(i2, j1)+ u * v * src.at<uchar>(i2, j2);}//处理彩色图像else{dst.at<Vec3b>(i, j)[0] = (1 - u) * (1 - v) * src.at<Vec3b>(i1, j1)[0] + (1 - u) * v * src.at<Vec3b>(i1, j2)[0]+ u * (1 - v) * src.at<Vec3b>(i2, j1)[0] + u * v * src.at<Vec3b>(i2, j2)[0];dst.at<Vec3b>(i, j)[1] = (1 - u) * (1 - v) * src.at<Vec3b>(i1, j1)[1] + (1 - u) * v * src.at<Vec3b>(i1, j2)[1] + u * (1 - v) * src.at<Vec3b>(i2, j1)[1] + u * v * src.at<Vec3b>(i2, j2)[1];dst.at<Vec3b>(i, j)[2] = (1 - u) * (1 - v) * src.at<Vec3b>(i1, j1)[2]+ (1 - u) * v * src.at<Vec3b>(i1, j2)[2]+ u * (1 - v) * src.at<Vec3b>(i2, j1)[2]+ u * v * src.at<Vec3b>(i2, j2)[2];}}}
}//图像旋转
void img_rotate(Mat& src, Mat& dst, double Angle)
{double angle = Angle * CV_PI / 180.0;//构造输出图形int dst_rows = (int)round(fabs(src.rows * cos(angle)) + fabs(src.cols * sin(angle)));//图像高度int dst_cols = (int)round(fabs(src.cols * cos(angle)) + fabs(src.rows * sin(angle)));//图像宽度dst.create(dst_rows, dst_cols, src.type());//将原图像坐标映射到数学笛卡尔坐标Mat T1 = (Mat_<double>(3, 3) << 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.5 * src.cols, 0.5 * src.rows, 1.0);//数学笛卡尔坐标下顺时针旋转的变换矩阵Mat T2 = (Mat_<double>(3, 3) << cos(angle), -sin(angle), 0.0, sin(angle), cos(angle), 0.0, 0.0, 0.0, 1.0);//将数学笛卡尔坐标映射到旋转后的图像坐标double t3[3][3] = { {1.0,0.0,0.0},{0.0,-1.0,0.0},{0.5 * dst.cols,0.5 * dst.rows,1.0} };Mat T3 = Mat(3.0, 3.0, CV_64FC1, t3);Mat T = T1 * T2 * T3;Mat T_inv = T.inv();    //求逆矩阵for (double i = 0.0; i < dst.rows; i++){for (double j = 0.0; j < dst.cols; j++){Mat dst_coordinate = (Mat_<double>(1, 3) << j, i, 1.0);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为坐标 列 的小数部分if (src.channels() == 1){//灰度图像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);}else{//彩色图像dst.at<Vec3b>(i, j)[0] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[0]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[0]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[0]+ pw * pv * src.at<Vec3b>(bottom, right)[0];dst.at<Vec3b>(i, j)[1] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[1]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[1]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[1]+ pw * pv * src.at<Vec3b>(bottom, right)[1];dst.at<Vec3b>(i, j)[2] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[2]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[2]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[2]+ pw * pv * src.at<Vec3b>(bottom, right)[2];}}}}
}//平移变换
//tx:水平平移距离,正数向右移动,负数向左移动
//ty:垂直平移距离,正数向下移动,负数向上移动
void trans(Mat& src, Mat& dst, double tx, double ty)
{//创建输出图像dst.create(src.size(), src.type());//平移变换矩阵Mat T = (Mat_<double>(3, 3) << 1, 0, 0, 0, 1, 0, tx, ty, 1);//求逆矩阵Mat T_inv = T.inv();for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){Mat dst_coordinate = (Mat_<double>(1, 3) << j, i, 1);Mat src_coordinate = dst_coordinate * T_inv;double v = src_coordinate.at<double>(0, 0);  //原图像的横坐标,列,宽double w = src_coordinate.at<double>(0, 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为坐标 列 的小数部分if (src.channels() == 1){//灰度图像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);}else{//彩色图像dst.at<Vec3b>(i, j)[0] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[0]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[0]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[0]+ pw * pv * src.at<Vec3b>(bottom, right)[0];dst.at<Vec3b>(i, j)[1] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[1]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[1]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[1]+ pw * pv * src.at<Vec3b>(bottom, right)[1];dst.at<Vec3b>(i, j)[2] = (1 - pw) * (1 - pv) * src.at<Vec3b>(top, left)[2]+ (1 - pw) * pv * src.at<Vec3b>(top, right)[2]+ pw * (1 - pv) * src.at<Vec3b>(bottom, left)[2]+ pw * pv * src.at<Vec3b>(bottom, right)[2];}}}}}
int main()
{Mat src = imread("cat.webp");//图像的缩放Mat dst1,dst2;//使用函数resizedst1 = Mat::zeros(812, 612, CV_8UC3);    //放大dst2 = Mat::zeros(200, 300, CV_8UC3);    //缩小resize(src, dst1, dst1.size());resize(src, dst2, dst2.size());//使用像素矩阵float sx0 = 3.0f, sy0 = 2.0f;    //放大的倍数float sx1 = 0.4f, sy1 = 0.6f;     //缩小的倍数//在 C 语言中,如果不指定数据类型,那么小数常量会被认为是 double 类型的//由于它是常量,所以编译器称为 const double。//double 是不能隐式转换为 float 的。//在后面加一个f或F,且需写成小数形式//最近邻插值放大缩小Mat dst3, dst4;nearest(src, dst3, sx0, sy0);nearest(src, dst4, sx1, sy1);//双线性插值Mat dst5, dst6;linear(src, dst5, sx0, sy0);linear(src, dst6, sx1, sy1);//旋转Mat dst7;img_rotate(src, dst7, 250);//平移Mat dst8;trans(src, dst8, 80.11, 100.33);imshow("原图", src);imshow("调用函数-放大", dst1);imshow("调用函数-缩小", dst2);imshow("最近邻插值-放大", dst3);imshow("最近邻插值-缩小", dst4);imshow("双线性插值-放大", dst5);imshow("双线性插值-缩小", dst6);imshow("旋转250", dst7);imshow("平移", dst8);cout << "size:" << src.size() << endl;cout << "row:" << src.rows << endl;cout << "col:" << src.cols << endl;waitKey(0);return 0;
}

参考文章:https://blog.csdn.net/weixin_40647819/article/details/87912122

图像几何变换--缩放、平移、镜像、旋转相关推荐

  1. C#图像处理-OpenCVSharp教程(十二) OpenCVSharp图像几何变换:平移、旋转、缩放、转置和镜像等

    本文作者Color Space,文章未经作者允许禁止转载! 本文将介绍OpenCVSharp图像几何变换:平移.旋转.缩放.转置和镜像等! 一.图像缩放 // 图像缩放-- - Resize Mat ...

  2. 图像算法二:【图像几何变换】平移、镜像、转置、缩放、旋转、插值

    作为一个强大的科学计算软件,MATLAB广泛运用于较多领域,以其简单的编程风格著称.这篇文章便通过matlab语言来讲述如何进行图像的各种几何变换. 图像几何变换又称为图像空间变换,它是将一幅图像中的 ...

  3. [Python从零到壹] 三十八.图像处理基础篇之图像几何变换(平移缩放旋转)

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  4. C语言实现bmp图像几何变换(移动,旋转,镜像,转置,缩放)

    C语言实现bmp图像几何变换(移动,旋转,镜像,转置,缩放) 移动 旋转 镜像 转置 缩放 自定义结构及函数如下: #define pi 3.1415926 typedef struct {unsig ...

  5. 计算机图形与OpenGL学习五(二维几何变换1.平移、旋转、缩放)

    二维几何变换(平移.旋转.缩放) 本章涉及数学变换比较多,代码是次要的,数学理论可自己推导一下. [二维平移] 通过将二维量加到一个点的坐标上来生成一个新的坐标位置,可以实现一次平移.将平移距离加到原 ...

  6. 图像几何变换C++实现--镜像,平移,旋转,错切,缩放

    一.图像几何变换介绍 图像的几何空间变换是图像处理中的最基础的算法,是指对原始图像按需要改变其大小.形状和位置的变化,原始图像与目标函数之间的坐标变换函数为线性函数.二维图像的基本几何变换主要包括镜像 ...

  7. 生成组合仿射变换矩阵,裁剪+缩放+平移+斜切+旋转

    前言 在翻以前oschina上写的博客的时候,看到这篇觉得还挺有趣的,就重新修改并添加一些新的内容发到再公号上. 一般对图像做 augmentation 都会用仿射变换去实现,如果是用OpenCV来实 ...

  8. OpenCV图像几何变换——转置,镜像,倒置

    图像几何变换方法之--remap使用. 源图像 一.图像转置 #include <iostream> #include <opencv2/opencv.hpp> using n ...

  9. Premiere模板插件预设 1300个摄像机缩放平移扭曲旋转图形干扰炫光水墨婚礼体育vlog视频转场

    vlog视频转场 包含光效.梦幻虚焦.水墨.平移.旋转.扭曲.摄像机冲击.信号损坏.图形切割等一共1300个转场效果,prproj格式,直接拖拽到两个视频中间上面的轨道即可,使用简单,包含的工程分辨率 ...

最新文章

  1. cmake find_package 中,include_directories,target_link_libraries 的值怎么知道?
  2. Java Stream ORM现在带有JOIN
  3. window10维护不了华为服务器,win10怎么打开云服务器异常
  4. 【UI/UX】浅谈Spin框
  5. 955. 删列造序 II
  6. 男子支付宝每天莫名进钱 吓得赶紧报警 最后真相哭笑不得...
  7. MySQL主从同步(四)——M-M架构配置实战
  8. 使用plsql管理oracle数据库连接,使用PLSQL连接远程oracle数据库
  9. 「旅游信息管理系统」 · Java Swing + MySQL 开发
  10. html5快手视频播放源码,快手视频解析源码
  11. java 读取网络js文件_JavaScript 读写文件
  12. socket函数send和recv函数
  13. 【Nodejs】使用http.request批量下载MP3,发现网络文件大于1000K时下载文件为0K
  14. 2020倒计时,大厂核心送给每一个脚踏实地努力着的Android程序员,逆风前行
  15. PWM方波的理解浅谈
  16. Programming In Scala笔记-第十五章、Case Classes和模式匹配
  17. WebP_支持:超乎你想象
  18. 能上qq但是打不开网页-详解DNS
  19. 网卡不支持承载网络(无法启动wifi热点)--解决办法
  20. 【搜索技巧】去广告、限定文件类型等提高资料查找效率的技巧

热门文章

  1. 大一基础会计期末考试试题
  2. word文件上传(前后端分离)
  3. 《计算机网络》(第8版)第五章 运输层 知识点总结
  4. php 密码错误就弹出框_PHP弹出对话框
  5. 怎么用计算机上摄像头拍照,win7电脑怎么用摄像头拍照?win7电脑用摄像头拍照的详细步骤...
  6. centos 网卡设置
  7. 绝缘子红外图像检测项目(TF2)
  8. 速看!PMP新考纲、PMBOK第七版解读(附新考纲资料+PMBOK第七版电子书)
  9. 一款集成度高的渗透工具:Railgun
  10. Hadoop安全之Kerberos