Opencv-C++ notes (16): Geometric transformation (image flip (mirror), translation, rotation, affine, perspective transformation)

1. Image translation

insert image description here

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include<ctime>
#include<iostream>

using namespace cv;
using namespace std;


//平移操作,图像大小不变
Mat imageTranslation1(Mat& srcImage, int x0ffset, int y0ffset)
{
    
    
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	Mat resultImage(srcImage.size(), srcImage.type());
	//遍历图像
	for (int i = 0; i < nRows; i++)
	{
    
    
		for (int j = 0; j < nCols; j++)
		{
    
    

			int x = j - x0ffset;
			int y = i - y0ffset;
			//边界判断
			if (x >= 0 && y >= 0 && x < nCols && y < nRows)
			{
    
    
				resultImage.at<Vec3b>(i, j) = srcImage.ptr<Vec3b>(y)[x];
			}
		}
	}
	return resultImage;
}
//平移操作,图形大小改变
Mat imageTranslation2(Mat& srcImage, int x0ffset, int y0ffset)
{
    
    
	//设置平移尺寸
	int nRows = srcImage.rows + abs(y0ffset);
	int nCols = srcImage.cols + abs(x0ffset);
	Mat resultImage(nRows, nCols, srcImage.type());
	//图像遍历
	for (int i = 0; i < nRows; i++)
	{
    
    
		for (int j = 0; j < nCols; j++)
		{
    
    
			int x = j - x0ffset;
			int y = i - y0ffset;
			//边界判断
			if (x >= 0 && y >= 0 && x < nCols && y < nRows)
			{
    
    
				resultImage.at<Vec3b>(i, j) = srcImage.ptr<Vec3b>(y)[x];
			}
		}
	}
	return resultImage;
}
Mat img_shift(Mat img, int d)
{
    
    
	Mat tmp;

	if (d > 0)
	{
    
    
		//右移
		Mat q0(img, Rect(0, 0, img.cols - d, img.rows));
		Mat q1(img, Rect(img.cols - d, 0, d, img.rows));

		q0.copyTo(tmp);

		Mat q2(img, Rect(0, 0, d, img.rows));
		Mat q3(img, Rect(d, 0, img.cols - d, img.rows));

		q1.copyTo(q2);
		tmp.copyTo(q3);
	}
	else
	{
    
    
		//左移
		d = -d;

		Mat q0(img, Rect(0, 0, d, img.rows));
		Mat q1(img, Rect(d, 0, img.cols - d, img.rows));

		q0.copyTo(tmp);

		Mat q2(img, Rect(0, 0, img.cols - d, img.rows));
		Mat q3(img, Rect(img.cols - d, 0, d, img.rows));

		q1.copyTo(q2);
		tmp.copyTo(q3);

	}


	return img;

}

int main()
{
    
    
	//读取图像
	Mat srcImage = imread("E:\\Lena.jpg");
	if (srcImage.empty())
	{
    
    
		return -1;
	}

	//显示原图像
	imshow("原图像", srcImage);
	int x0ffset = 50;
	int y0ffset = 80;
	Mat resultImage1 = imageTranslation1(srcImage, x0ffset, y0ffset);
	imshow("resultImage1", resultImage1);
	Mat resultImage2 = imageTranslation2(srcImage, x0ffset, y0ffset);
	imshow("resultImage2", resultImage2);
	x0ffset = -50;
	y0ffset = -80;
	Mat resultImage3 = imageTranslation1(srcImage, x0ffset, y0ffset);
	cv::imshow("resultImage3", resultImage3);
	Mat resultImage4 = img_shift(srcImage, 60);
	imshow("resultImage4", resultImage4);
	cv::waitKey(0);
	return 0;
}
//第二种图像平移
//将图像扩展两倍的宽 并对其进行截取达到是图像平移
Mat img_shift1(Mat img, int d)
{
    
    

	Mat src(img.rows, img.cols * 2, img.type());

	//水平平移 则在水平方向上对其复制粘贴
	img.copyTo(src({
    
     0,0,img.cols,img.rows }));
	img.copyTo(src({
    
     img.cols,0,img.cols,img.rows }));
	imshow("src", src);

	if (d > 0)
	{
    
    
		Mat tmp(src, Rect(img.cols - d, 0, img.cols, img.rows));
		tmp.copyTo(img);
	}
	else
	{
    
    
		Mat tmp(src, Rect(-d, 0, img.cols, img.rows));
		tmp.copyTo(img);
	}

	return img;
}

insert image description here

uchar pixel_value = Mat.ptr<uchar>(row)[col]; //获取某个像素值(row行col列)
 Mat.ptr<uchar>(row);  //获取某行的首地址

2. Image rotation

Image rotation refers to the process of rotating an image at a certain angle according to a certain position, and the image still maintains its original size during rotation. After the image is rotated, the horizontal axis of symmetry, the vertical axis of symmetry, and the origin of the central coordinates of the image may all be transformed, so the coordinates in the image rotation need to be converted accordingly.

2.1 Find the rotation matrix

Suppose there is a point: P(x,y), which is transformed into P'(x', y') after rotating β around the origin O(0,0). In addition, the distance from point P to origin O is r :
insert image description here
insert image description here

Suppose the point P(x,y) forms an angle α with the X axis. Here, the formula is as follows:

x = r cos(α)
y = r sin(α)
Similarly, the point P'(x',y') will form an angle with the X axis, α + β. Therefore, the formula is as follows:

x' = r cos(α+β)
y' = r sin(α+β)
Next, the following trigonometric identity will be used:

cos(α+β) = cosαcosβ – sinαsinβ
insert image description here
insert image description here
Now that the previous equations have been obtained, any point can be transformed into a new point as long as it is rotated by a given angle. The same equation can be applied to each pixel in the image, resulting in a rotated image. However, even though the image is rotated, it's still within a rectangle. This means that the dimensions of the new image can change, while in translation the dimensions of the output and input images remain the same.

2.2 Find the size of the rotated image

Two cases will be considered here.

第一种情况是保持输出图像的尺寸与输入图像的尺寸相同。
第二种情况是修改输出图像的尺寸。
通过下面的图表来理解它们之间的区别。

将图像以逆时针方向围绕图像中心旋转一个角度 ϴ ——

左半部分显示的是即使是在旋转之后,图像的尺寸保持不变的情况
而在右半部分,缩放尺寸以覆盖整个旋转后的图像。
可以看到两种情况下得到的结果的差异。
下图中 L 和 H 为原始图像的尺寸,L' 和 H' 为旋转后的尺寸。

insert image description here
In the image above, the size of the rotated image depends on whether the size of the image remains the same or is modified when it is rotated. For cases where you want to keep the image size the same as the original image size, only extra regions need to be culled. If you don't want to keep the same size, you need to learn how to get the size of the rotated image.
insert image description here
insert image description here

2.3 Manual image rotation

Mat imgRotate(Mat matSrc, float angle, bool direction)
{
    
    
	float theta = angle * CV_PI / 180.0;
	int nRowsSrc = matSrc.rows;
	int nColsSrc = matSrc.cols;
	// 如果是顺时针旋转
	if (!direction)
		theta = 2 * CV_PI - theta;
	// 全部以逆时针旋转来计算
	// 逆时针旋转矩阵
	float matRotate[3][3]{
    
    
		{
    
    std::cos(theta), -std::sin(theta), 0},
		{
    
    std::sin(theta), std::cos(theta), 0 },
		{
    
    0, 0, 1}
	};
	float pt[3][2]{
    
    
		{
    
     0, nRowsSrc },
		{
    
    nColsSrc, nRowsSrc},
		{
    
    nColsSrc, 0}
	};
	for (int i = 0; i < 3; i++)
	{
    
    
		float x = pt[i][0] * matRotate[0][0] + pt[i][1] * matRotate[1][0];
		float y = pt[i][0] * matRotate[0][1] + pt[i][1] * matRotate[1][1];
		pt[i][0] = x;
		pt[i][1] = y;
	}
	// 计算出旋转后图像的极值点和尺寸
	float fMin_x = min(min(min(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);
	float fMin_y = min(min(min(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);
	float fMax_x = max(max(max(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);
	float fMax_y = max(max(max(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);
	int nRows = cvRound(fMax_y - fMin_y + 0.5) + 1;
	int nCols = cvRound(fMax_x - fMin_x + 0.5) + 1;
	int nMin_x = cvRound(fMin_x + 0.5);
	int nMin_y = cvRound(fMin_y + 0.5);
	// 拷贝输出图像
	Mat matRet(nRows, nCols, matSrc.type(), Scalar(0));
	for (int j = 0; j < nRows; j++)
	{
    
    
		for (int i = 0; i < nCols; i++)
		{
    
    
			// 计算出输出图像在原图像中的对应点的坐标,然后复制该坐标的灰度值
			// 因为是逆时针转换,所以这里映射到原图像的时候可以看成是,输出图像
			// 到顺时针旋转到原图像的,而顺时针旋转矩阵刚好是逆时针旋转矩阵的转置
			// 同时还要考虑到要把旋转后的图像的左上角移动到坐标原点。
			int x = (i + nMin_x) * matRotate[0][0] + (j + nMin_y) * matRotate[0][1];
			int y = (i + nMin_x) * matRotate[1][0] + (j + nMin_y) * matRotate[1][1];
			if (x >= 0 && x < nColsSrc && y >= 0 && y < nRowsSrc)
			{
    
    
				matRet.at<Vec3b>(j, i) = matSrc.at<Vec3b>(y, x);
			}
		}
	}
	return matRet;
}
int main()
{
    
    
	Mat matSrc = imread("E:\\Lena.jpg");
	if (matSrc.empty())
		return 1;
	float angle = 30;
	Mat matRet = imgRotate(matSrc, angle, true);
	imshow("src", matSrc);
	imshow("rotate", matRet);
	// 保存图像
	imwrite("rotate_panda.jpg", matRet);

	waitKey();
	return 0;
}

insert image description here

2.4 Opencv function realizes image rotation

// 图像旋转
void Rotate(const Mat& srcImage, Mat& destImage, double angle)//angle表示要旋转的角度
{
    
    
	Point2f center(srcImage.cols / 2, srcImage.rows / 2);//中心
	Mat M = getRotationMatrix2D(center, angle, 1);//计算旋转的仿射变换矩阵 
	warpAffine(srcImage, destImage, M, Size(srcImage.cols, srcImage.rows));//仿射变换  
	circle(destImage, center, 2, Scalar(255, 0, 0));
}

int main()
{
    
    
	//读入图像,并判断图像是否读入正确
	cv::Mat srcImage = imread("E:\\Lena.jpg");
	if (!srcImage.data)
	{
    
    
		puts("打开图像文件失败");
		return -1;
	}
	imshow("srcImage", srcImage);
	//将图片按比例缩放至宽为250像素的大小
	Mat destImage;
	double angle = 9.9;//角度
	Rotate(srcImage, destImage, angle);
	imshow("dst", destImage);
	waitKey(0);
	return 0;
}

insert image description here

旋转分为三步操作:
1. 首先,你需要得到旋转的中心。这通常是你要旋转的图像的中心。
2. 接下来,创建2d旋转矩阵。OpenCV提供了我们在上面讨论过的getRotationMatrix2D()函数。
3. 最后,使用在上一步中创建的旋转矩阵对图像应用仿射变换。OpenCV中的warpAffine()函数完成这项工作。

getRotationMatrix2D(center, angle, scale)
getRotationMatrix2D()函数接受以下参数:
	center:图像的旋转中心:
	angle: 旋转角度:
	scale :一个各向同性的比例因子,根据提供的值将图像向上或向下缩放
	如果angle是正的,图像将逆时针方向旋转。如果你想顺时针旋转图像相同的量,那么角度需要是负的。

warpAffine()函数的作用是:对图像应用一个仿射变换。在进行仿射变换后,原图像中所有的平行线在输出图像中也保持平行。
warpAffine(
    src, 
    M, 
    dsize[, 
    dst[, 
    flags[, 
    borderMode[, 
    borderValue]]]]
)
函数的参数:
src:原图
M:变换矩阵
dsize:输出图像的大小
dst:输出图像
flags: 插值方法的组合如INTER_LINEAR或INTER_NEAREST
borderMode:像素扩展方法
borderValue:在边界不变的情况下使用的值,默认值为0

3. Image flipping

insert image description here

3.1 Flip left and right

//图像翻转,图像大小不变
Mat imageTranslation1(Mat& srcImage)
{
    
    
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	Mat resultImage(srcImage.size(), srcImage.type());
	//遍历图像
	for (int i = 0; i < nRows; i++)
	{
    
    
		for (int j = 0; j < nCols; j++)
		{
    
    

				resultImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(i, nCols-j-1);
		
		}
	}
	return resultImage;
}


int main()
{
    
    
	//读取图像
	Mat srcImage = imread("E:\\Lena.jpg");
	if (srcImage.empty())
	{
    
    
		return -1;
	}

	//显示原图像
	imshow("原图像", srcImage);
	int x0ffset = 50;
	int y0ffset = 80;
	Mat resultImage1 = imageTranslation1(srcImage);
	imshow("resultImage1", resultImage1);
	
	cv::waitKey(0);
	return 0;
}

insert image description here

3.2 Flip up and down

//图像翻转,图像大小不变
Mat imageTranslation1(Mat& srcImage)
{
    
    
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	Mat resultImage(srcImage.size(), srcImage.type());
	//遍历图像
	for (int i = 0; i < nRows; i++)
	{
    
    
		for (int j = 0; j < nCols; j++)
		{
    
    

				resultImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(nRows-1-i, j);
		
		}
	}
	return resultImage;
}

3.3 upside down, left and right

//图像翻转,图像大小不变
Mat imageTranslation1(Mat& srcImage)
{
    
    
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	Mat resultImage(srcImage.size(), srcImage.type());
	//遍历图像
	for (int i = 0; i < nRows; i++)
	{
    
    
		for (int j = 0; j < nCols; j++)
		{
    
    

				resultImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(nRows-1-i, nCols-1-j);
		
		}
	}
	return resultImage;
}

insert image description here

4. Wrong cut transformation

Image staggering transformation, also known as oblique cutting, refers to the non-vertical projection of a plane scene on a projection plane, which causes the graphics in the image to be distorted in the horizontal or vertical direction.
Taking horizontal distortion as an example, the pixel point (x, y) is twisted in the horizontal direction to become a hypotenuse, while the side in the vertical direction remains unchanged, which can be described by the following formula:
insert image description here

4.1 Realize staggered transformation

//图像错切
Mat imageTranslation(Mat& srcImage, float a,float b)
{
    
    
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	Mat resultImage(srcImage.size(), srcImage.type());
	//遍历图像
	for (int i = 0; i < nRows; i++)
	{
    
    
		for (int j = 0; j < nCols; j++)
		{
    
    
			if (i + a * j > 0 && i + a * j < nRows && i * b + j >0 && i * b + j< nCols)
			{
    
    
				resultImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>( i + a * j, i * b + j);
			}



		}
	}
	return resultImage;
}

insert image description here

5. Affine transformation

Affine transformation can be understood as a transformation of matrix multiplication (linear transformation) and vector addition (translation). In essence, an affine transformation represents the relationship between two images, which can be expressed as:
1. Rotation (linear transformation)
2. Translation (vector addition)
3. Scaling operation (linear transformation)
Affine transformation usually uses 2 ×3 matrix representation
insert image description here
Multiplying M by a two-dimensional vector [x, y], such as image pixel coordinates, can finally be expressed as:
insert image description here

5.1 Solving Affine Transformation

An affine transformation is basically the relationship between two images. The information of this relationship can be obtained in two ways:
1. Knowing X and T, then our task is to find M;
2. Knowing M and X, apply T=M⋅X, and get T.
Pictured below: Points 1, 2 and 3 (which form a triangle in Figure 1) are mapped into Figure 2 and still form a triangle, but now they have changed. If we find the affine transformation of these 3 points, then we can apply the found relationship to all pixels in the image.
insert image description here
insert image description here

5.2 OpenCV implements affine transformation

//全局变量
String src_windowName = "原图像";
String warp_windowName = "仿射变换";
String warp_rotate_windowName = "仿射旋转变换";
String rotate_windowName = "图像旋转";

int main()
{
    
    
	Point2f srcTri[3];
	Point2f dstTri[3];

	Mat rot_mat(2, 3, CV_32FC1);
	Mat warp_mat(2, 3, CV_32FC1);
	Mat srcImage, warp_dstImage, warp_rotate_dstImage, rotate_dstImage;

	//加载图像
	srcImage = imread("E:\\Lena.jpg");

	//判断文件是否加载成功
	if (srcImage.empty())
	{
    
    
		cout << "图像加载失败!" << endl;
		return -1;
	}
	else
		cout << "图像加载成功!" << endl << endl;

	//创建仿射变换目标图像与原图像尺寸类型相同
	warp_dstImage = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());

	//设置三个点来计算仿射变换
	srcTri[0] = Point2f(0, 0);
	srcTri[1] = Point2f(srcImage.cols - 1, 0);
	srcTri[2] = Point2f(0, srcImage.rows - 1);

	dstTri[0] = Point2f(srcImage.cols * 0.0, srcImage.rows * 0.33);
	dstTri[1] = Point2f(srcImage.cols * 0.85, srcImage.rows * 0.25);
	dstTri[2] = Point2f(srcImage.cols * 0.15, srcImage.rows * 0.7);

	//计算仿射变换矩阵
	warp_mat = getAffineTransform(srcTri, dstTri);

	//对加载图形进行仿射变换操作
	warpAffine(srcImage, warp_dstImage, warp_mat, warp_dstImage.size());

	//计算图像中点顺时针旋转50度,缩放因子为0.6的旋转矩阵
	Point center = Point(warp_dstImage.cols / 2, warp_dstImage.rows / 2);
	double angle = -50.0;
	double scale = 0.6;

	//计算旋转矩阵
	rot_mat = getRotationMatrix2D(center, angle, scale);

	//旋转已扭曲图像
	warpAffine(warp_dstImage, warp_rotate_dstImage, rot_mat, warp_dstImage.size());

	//将原图像旋转
	warpAffine(srcImage, rotate_dstImage, rot_mat, srcImage.size());

	//显示变换结果
	namedWindow(src_windowName, WINDOW_AUTOSIZE);
	imshow(src_windowName, srcImage);

	namedWindow(warp_windowName, WINDOW_AUTOSIZE);
	imshow(warp_windowName, warp_dstImage);

	namedWindow(warp_rotate_windowName, WINDOW_AUTOSIZE);
	imshow(warp_rotate_windowName, warp_rotate_dstImage);

	namedWindow(rotate_windowName, WINDOW_AUTOSIZE);
	imshow(rotate_windowName, rotate_dstImage);

	waitKey(0);

	return 0;
}

insert image description here

5.3 Manual

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

using namespace std;
using namespace cv;

#define PI 3.1415927
#define MAX(a,b) (((a)>(b))?(a):(b))

// 单点双线性插值
// [输入]	ii--dst的行索引
//			jj--dst的列索引	
//			u_src--jj反向映射到src中对应的列索引
//			v_src--ii反向映射到src中对应的行索引
int Bilinear_interpolation_img(Mat src, Mat& dst, int ii, int jj, double u_src, double v_src)
{
    
    
	if (src.rows <= 0 || src.cols <= 0 ||
		(src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U)
	{
    
    
		printf("输入图像有误!\n");
		return 0;
	}

	if (u_src >= 0 && u_src <= src.cols - 1 && v_src >= 0 && v_src <= src.rows - 1)
	{
    
    
		int x1 = int(u_src), x2 = (int)(u_src + 0.5), y1 = (int)v_src, y2 = (int)(v_src + 0.5);
		double pu = fabs(u_src - x1), pv = fabs(v_src - y2);
		if (src.channels() == 1)
		{
    
    
			dst.at<uchar>(ii, jj) = (1 - pv) * (1 - pu) * src.at<uchar>(y2, x1) +
				(1 - pv) * pu * src.at<uchar>(y2, x2) +
				pv * (1 - pu) * src.at<uchar>(y1, x1) + pv * pu * src.at<uchar>(y1, x2);
		}
		else
		{
    
    
			dst.at<Vec3b>(ii, jj)[0] = (1 - pv) * (1 - pu) * src.at<Vec3b>(y2, x1)[0] +
				(1 - pv) * pu * src.at<Vec3b>(y2, x2)[0] +
				pv * (1 - pu) * src.at<Vec3b>(y1, x1)[0] +
				pv * pu * src.at<Vec3b>(y1, x2)[0];
			dst.at<Vec3b>(ii, jj)[1] = (1 - pv) * (1 - pu) * src.at<Vec3b>(y2, x1)[1] +
				(1 - pv) * pu * src.at<Vec3b>(y2, x2)[1] +
				pv * (1 - pu) * src.at<Vec3b>(y1, x1)[1] +
				pv * pu * src.at<Vec3b>(y1, x2)[1];
			dst.at<Vec3b>(ii, jj)[2] = (1 - pv) * (1 - pu) * src.at<Vec3b>(y2, x1)[2] +
				(1 - pv) * pu * src.at<Vec3b>(y2, x2)[2] +
				pv * (1 - pu) * src.at<Vec3b>(y1, x1)[2] +
				pv * pu * src.at<Vec3b>(y1, x2)[2];
		}

	}
	return 1;
}

//水平镜像、垂直镜像变换
// [输入]	way_mirror镜像方法:0水平镜像 1垂直镜像
int affine_mirrorImg(Mat src, Mat& dst, int way_mirror)
{
    
    
	if (src.rows <= 0 || src.cols <= 0 ||
		(src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U) {
    
    
		printf("输入图像有误!\n");
		return 0;
	}

	if (way_mirror != 0 && way_mirror != 1) {
    
    
		printf("输入镜像方法不为1或0,way_mirror: %d!\n", way_mirror);
		return 0;
	}

	int dst_h = src.rows, dst_w = src.cols;//目标图像宽高 初始化为原图宽高
	int ii = 0, jj = 0;
	double u_src = 0, v_src = 0;

	Mat M_mirr = (Mat_<double>(3, 3) << -1, 0, 0, 0, 1, 0, 0, 0, 1);
	if (way_mirror) {
    
    
		M_mirr.at<double>(0, 0) = 1;
		M_mirr.at<double>(1, 1) = -1;
	}
	Mat M_corrToSrc = (Mat_<double>(3, 3) << 1, 0, src.cols, 0, 1, 0, 0, 0, 1);
	if (way_mirror) {
    
    
		M_corrToSrc.at<double>(0, 2) = 0;
		M_corrToSrc.at<double>(1, 2) = src.rows;
	}
	Mat M_trans = M_corrToSrc * M_mirr;

	Mat M_trans_inv = M_trans.inv();
	Mat dst_uv(3, 1, CV_64F);
	dst_uv.at<double>(2, 0) = 1;
	Mat src_uv(dst_uv);

	if (src.channels() == 3)
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB图初始
	else
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);

	//反向映射
	for (ii = 0; ii < dst_h; ++ii)
	{
    
    
		for (jj = 0; jj < dst_w; ++jj)
		{
    
    
			dst_uv.at<double>(0, 0) = jj;
			dst_uv.at<double>(1, 0) = ii;
			src_uv = M_trans_inv * dst_uv;
			u_src = src_uv.at<double>(0, 0);
			v_src = src_uv.at<double>(1, 0);

			// 边界问题
			if (u_src < 0) u_src = 0;
			if (v_src < 0) v_src = 0;
			if (u_src > src.cols - 1) u_src = src.cols - 1;
			if (v_src > src.rows - 1) v_src = src.rows - 1;

			//双线性插值
			Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);
		}
	}
	return 1;
}

// 图像旋转(绕图像中心) 逆时针旋转为正
// 可处理8位单通道或三通道图像
int affine_rotateImg(Mat src, Mat& dst, double Angle)
{
    
    
	if (src.rows <= 0 || src.cols <= 0 ||
		(src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U)
	{
    
    
		printf("输入图像有误!\n");
		return 0;
	}

	double angle = 0, cos_a = 0, sin_a = 0;//旋转角度
	int dst_h = src.rows, dst_w = src.cols;//目标图像宽高 初始化为原图宽高
	int ii = 0, jj = 0;
	double u_src = 0, v_src = 0;

	angle = Angle / 180 * CV_PI;
	cos_a = cos(angle);
	sin_a = sin(angle);
	dst_h = (int)(fabs(src.rows * cos_a) + fabs(src.cols * sin_a) + 0.5);
	dst_w = (int)(fabs(src.rows * sin_a) + fabs(src.cols * cos_a) + 0.5);

	if (src.channels() == 3)
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB图初始
	}
	else
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);
	}
	Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, -0.5 * src.cols, 0, -1, 0.5 * src.rows, 0, 0, 1);
	Mat M_rotate = (Mat_<double>(3, 3) << cos_a, -sin_a, 0, sin_a, cos_a, 0, 0, 0, 1);
	Mat M_toPixel = (Mat_<double>(3, 3) << 1, 0, 0.5 * dst.cols, 0, -1, 0.5 * dst.rows, 0, 0, 1);
	Mat M_trans = M_toPixel * M_rotate * M_toPhysics;
	Mat M_trans_inv = M_trans.inv();
	Mat dst_uv(3, 1, CV_64F);
	dst_uv.at<double>(2, 0) = 1;
	Mat src_uv(dst_uv);

	//反向映射
	for (ii = 0; ii < dst_h; ++ii)
	{
    
    
		for (jj = 0; jj < dst_w; ++jj)
		{
    
    
			dst_uv.at<double>(0, 0) = jj;
			dst_uv.at<double>(1, 0) = ii;
			src_uv = M_trans_inv * dst_uv;
			u_src = src_uv.at<double>(0, 0);
			v_src = src_uv.at<double>(1, 0);

			//处理边界问题
			if (int(Angle) % 90 == 0)
			{
    
    
				if (u_src < 0) u_src = 0;
				if (v_src < 0) v_src = 0;
				if (u_src > src.cols - 1) u_src = src.cols - 1;
				if (v_src > src.rows - 1) v_src = src.rows - 1;
			}
			//双线性插值
			Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);
		}
	}

	return 1;
}

// 图像平移 在像素坐标系下进行 图像左顶点为原点,x轴为图像列,y轴为图像行
// tx: x方向(图像列)平移量,向右平移为正
// ty: y方向(图像行)平移量,向下平移为正
int affine_moveImg(Mat src, Mat& dst, double tx, double ty)
{
    
    
	if (src.rows <= 0 || src.cols <= 0 ||
		(src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U)
	{
    
    
		printf("输入图像有误!\n");
		return 0;
	}

	int dst_h = src.rows, dst_w = src.cols;
	int ii = 0, jj = 0;
	double u_src = 0, v_src = 0;

	if (src.channels() == 3)
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB图初始
	}
	else
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);
	}

	Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, tx, 0, 1, ty, 0, 0, 1);
	Mat M_trans_inv = M_toPhysics.inv();
	Mat dst_uv(3, 1, CV_64F);
	dst_uv.at<double>(2, 0) = 1;
	Mat src_uv(dst_uv);

	//反向映射
	for (ii = 0; ii < dst_h; ++ii)
	{
    
    
		for (jj = 0; jj < dst_w; ++jj)
		{
    
    
			dst_uv.at<double>(0, 0) = jj;
			dst_uv.at<double>(1, 0) = ii;
			src_uv = M_trans_inv * dst_uv;
			u_src = src_uv.at<double>(0, 0);
			v_src = src_uv.at<double>(1, 0);

			//双线性插值
			Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);

		}
	}

	return 1;
}

// 缩放 以图像左顶点为原点
// cx: 水平缩放尺度
// cy: 垂直缩放尺度
int affine_scalingImg(Mat src, Mat& dst, double cx, double cy)
{
    
    
	if (src.rows <= 0 || src.cols <= 0 ||
		(src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U)
	{
    
    
		printf("输入图像有误!\n");
		return 0;
	}

	int dst_h = (int)(cy * src.rows + 0.5), dst_w = (int)(cx * src.cols + 0.5);
	int ii = 0, jj = 0;
	double u_src = 0, v_src = 0;

	if (src.channels() == 3)
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB图初始
	}
	else
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);
	}

	Mat M_scale = (Mat_<double>(3, 3) << cx, 0, 0, 0, cy, 0, 0, 0, 1);
	Mat M_trans_inv = M_scale.inv();
	Mat dst_uv(3, 1, CV_64F);
	dst_uv.at<double>(2, 0) = 1;
	Mat src_uv(dst_uv);

	//反向映射
	for (ii = 0; ii < dst_h; ++ii)
	{
    
    
		for (jj = 0; jj < dst_w; ++jj)
		{
    
    
			dst_uv.at<double>(0, 0) = jj;
			dst_uv.at<double>(1, 0) = ii;
			src_uv = M_trans_inv * dst_uv;
			u_src = src_uv.at<double>(0, 0);
			v_src = src_uv.at<double>(1, 0);

			// 边界问题
			if (u_src < 0) u_src = 0;
			if (v_src < 0) v_src = 0;
			if (u_src > src.cols - 1) u_src = src.cols - 1;
			if (v_src > src.rows - 1) v_src = src.rows - 1;

			//双线性插值
			Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);
		}
	}
	return 1;
}

// 错切变换 以图像中心为偏移中心
// [输入]	sx--水平错切系数
//			sy--垂直错切系数		
int affine_miscut(Mat src, Mat& dst, double sx, double sy)
{
    
    
	if (src.rows <= 0 || src.cols <= 0 ||
		(src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U)
	{
    
    
		printf("输入图像有误!\n");
		return 0;
	}

	int dst_h = fabs(sy) * src.cols + src.rows, dst_w = fabs(sx) * src.rows + src.cols;
	int ii = 0, jj = 0;
	double u_src = 0, v_src = 0;

	if (src.channels() == 3)
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB图初始
	}
	else
	{
    
    
		dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);
	}

	Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, -0.5 * src.cols, 0, -1, 0.5 * src.rows, 0, 0, 1);
	Mat M_rotate = (Mat_<double>(3, 3) << 1, sx, 0, sy, 1, 0, 0, 0, 1);
	Mat M_toPixel = (Mat_<double>(3, 3) << 1, 0, 0.5 * dst.cols, 0, -1, 0.5 * dst.rows, 0, 0, 1);
	Mat M_trans = M_toPixel * M_rotate * M_toPhysics;
	Mat M_trans_inv = M_trans.inv();
	Mat dst_uv(3, 1, CV_64F);
	dst_uv.at<double>(2, 0) = 1;
	Mat src_uv(dst_uv);

	//反向映射
	for (ii = 0; ii < dst_h; ++ii)
	{
    
    
		for (jj = 0; jj < dst_w; ++jj)
		{
    
    
			dst_uv.at<double>(0, 0) = jj;
			dst_uv.at<double>(1, 0) = ii;
			src_uv = M_trans_inv * dst_uv;
			u_src = src_uv.at<double>(0, 0);
			v_src = src_uv.at<double>(1, 0);

			//双线性插值
			Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);
		}
	}

	return 1;
}

// 组合变换示例
// 缩放->旋转->错切(即偏移)
// [输入]	
int affine_srm_combImg(Mat src, Mat& dst, double cx, double cy, double Angle, double sx, double sy)
{
    
    
	if (src.rows <= 0 || src.cols <= 0 ||
		(src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U)
	{
    
    
		printf("输入图像有误!\n");
		return 0;
	}

	double angle, cos_a, sin_a;
	int dst_s_h, dst_s_w, dst_sr_h, dst_sr_w, dst_srm_h, dst_srm_w;

	angle = Angle / 180 * CV_PI;
	cos_a = cos(angle);
	sin_a = sin(angle);
	dst_s_h = (int)(cy * src.rows + 0.5);
	dst_s_w = (int)(cx * src.cols + 0.5);
	dst_sr_h = (int)(fabs(dst_s_h * cos_a) + fabs(dst_s_w * sin_a) + 0.5);
	dst_sr_w = (int)(fabs(dst_s_h * sin_a) + fabs(dst_s_w * cos_a) + 0.5);
	dst_srm_h = fabs(sy) * dst_sr_w + dst_sr_h;
	dst_srm_w = fabs(sx) * dst_sr_h + dst_sr_w;

	int ii = 0, jj = 0;
	double u_src = 0, v_src = 0;

	if (src.channels() == 3)
	{
    
    
		dst = cv::Mat::zeros(dst_srm_h, dst_srm_w, CV_8UC3); //RGB图初始
	}
	else
	{
    
    
		dst = cv::Mat::zeros(dst_srm_h, dst_srm_w, CV_8UC1);
	}

	Mat M_scale = (Mat_<double>(3, 3) << cx, 0, 0, 0, cy, 0, 0, 0, 1);

	Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, -0.5 * dst_s_w, 0, -1, 0.5 * dst_s_h, 0, 0, 1);
	Mat M_rotate = (Mat_<double>(3, 3) << cos_a, -sin_a, 0, sin_a, cos_a, 0, 0, 0, 1);
	Mat M2 = M_rotate * M_toPhysics;

	Mat M_mis = (Mat_<double>(3, 3) << 1, sx, 0, sy, 1, 0, 0, 0, 1);
	Mat M_toPixel = (Mat_<double>(3, 3) << 1, 0, 0.5 * dst.cols, 0, -1, 0.5 * dst.rows, 0, 0, 1);
	Mat M3 = M_toPixel * M_mis;

	Mat M_trans = M3 * M2 * M_scale;
	Mat M_trans_inv = M_trans.inv();

	Mat dst_uv(3, 1, CV_64F);
	dst_uv.at<double>(2, 0) = 1;
	Mat src_uv(dst_uv);

	//反向映射
	for (ii = 0; ii < dst_srm_h; ++ii)
	{
    
    
		for (jj = 0; jj < dst_srm_w; ++jj)
		{
    
    
			dst_uv.at<double>(0, 0) = jj;
			dst_uv.at<double>(1, 0) = ii;
			src_uv = M_trans_inv * dst_uv;
			u_src = src_uv.at<double>(0, 0);
			v_src = src_uv.at<double>(1, 0);

			//处理边界问题
			if (int(Angle) % 90 == 0)
			{
    
    
				if (u_src < 0) u_src = 0;
				if (v_src < 0) v_src = 0;
				if (u_src > src.cols - 1) u_src = src.cols - 1;
				if (v_src > src.rows - 1) v_src = src.rows - 1;
			}

			//双线性插值
			Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);

		}
	}
	return 1;
}

int main()
{
    
    
	Mat src = imread("E:\\Lena.jpg", 1), dst;

	//水平、垂直镜像
	int way_mirror = 1;
	//affine_mirrorImg(src, dst, way_mirror);

	//旋转
	double angle_r = 250;
	//int flag = affine_rotateImg(src, dst, angle_r);
	//if (flag == 0)
	//{
    
    
	//	return;
	//}

	//平移
	double tx = 50, ty = -50;
	//	affine_moveImg(src, dst, tx, ty);

		//尺度变换(缩放)
	double cx = 1.5, cy = 1.5;
	affine_scalingImg(src, dst, cx, cy);

	//错切(偏移)
	double sx = 0.2, sy = 0.2;
	//affine_trans_deviation(src, dst, sx, sy);
	affine_miscut(src, dst, sx, sy);

		//组合变换 缩放->旋转->错切(即偏移)
	//affine_srm_combImg(src, dst, cx, cy, angle_r, sx, sy);

		// 显示 
	Mat src_resize, dst_resize;
	//affine_scalingImg(src, src_resize, 0.4, 0.3);
	//affine_scalingImg(dst, dst_resize, 0.4, 0.3);

	namedWindow("src", 0);
	namedWindow("dst", 0);
	imshow("src", src);
	imshow("dst", dst);

	waitKey(0);
	system("pause");
	return 0;
}

6. Image scaling

Images can be resized in two ways:

Suppose the initial size of the image is W×H, where W and H represent width and height, respectively. If you want to double the size (dimensions) of the image, you can resize or scale the image to 2W×2H. Similarly, if one wanted to reduce the size (dimensions) of an image by half, one would resize or scale the image to W/2×H/2. Because you just want to scale the image, you can pass scaling factors (length and width) when resizing, and the image output size can be calculated based on these scaling factors.

At the same time, you may also want to resize the image to a fixed size, such as 420×360 pixels. In this case, scaling will not work because it cannot be determined that the initial dimension is a multiple (or factor) of the fixed dimension. This requires passing the new dimensions of the image directly when resizing.

insert image description here
The image above shows the image and pixel values ​​that you want to resize. Currently, its dimensions are 5×5. Let's say we want to double. This will result in the following output. However, we want to pad the pixel values.
insert image description here
Let's see what different options we have. Pixels can be copied. This will give us the result shown in the image below:
insert image description here
If we remove the pixel values ​​(numbers inside the squares) from the previous image, we will get the image shown in the image below. Compare it with the original image. Notice how similar it looks to the original image.
insert image description here
Similarly, if you want to shrink the image in half, you can reduce some pixels. You'll notice that when resizing, pixels are duplicated. There are a few other tricks you can use as well. For example: You can use interpolation, that is, find new pixel values ​​​​based on the pixel values ​​​​of adjacent pixels instead of copying them directly. This gives the colors a nice smooth transition. The image below shows how the result changes if we use different interpolation. From the image below, you can see that the newly created pixel values ​​are calculated differently when executed from left to right. In the first three images, the pixel is copied directly from the neighboring pixels, while in the latter image, the pixel value depends on all the neighboring pixels (left, right, top, bottom) and also on the diagonal phase Neighboring pixels:
insert image description here

6.1 Implement image scaling

//图像缩小
Mat imageTranslation(Mat& srcImage,int n)
{
    
    
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	Mat resultImage(srcImage.size() / n, srcImage.type());
	//遍历图像
	for (int i = 0; i < nRows; i++)
	{
    
    
		for (int j = 0; j < nCols; j++)
		{
    
    
			if (n * i < nRows && n * j < nCols)
			{
    
    
				resultImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(n * i, n * j);
			}

			

		}
	}
	return resultImage;
}
//图像放大
Mat imageTranslation1(Mat& srcImage, int n)
{
    
    
	int nRows = srcImage.rows;
	int nCols = srcImage.cols;
	Mat resultImage(srcImage.size() * n, srcImage.type());
	//遍历图像
	for (int i = 0; i < nRows * n; i++)
	{
    
    
		for (int j = 0; j < nCols * n ; j++)
		{
    
    

			
			resultImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>( i / n , j / n);
			
				
			



		}
	}
	return resultImage;
}


int main()
{
    
    
	//读取图像
	Mat srcImage = imread("E:\\Lena.jpg");
	if (srcImage.empty())
	{
    
    
		return -1;
	}

	//显示原图像
	imshow("原图像", srcImage);
	int x0ffset = 50;
	int y0ffset = 80;
	int n = 2;
	Mat resultImage1 = imageTranslation(srcImage,n);
	imshow("缩小图片", resultImage1);
	Mat resultImage2 = imageTranslation1(srcImage, n);
	imshow("放大图片", resultImage2);

	cv::waitKey(0);
	return 0;
}

insert image description here

Mat imgDown_1(Mat& srcimg, float kx, float ky)
{
    
    
	//提取图像的分辨率
		int nrows = cvRound(srcimg.rows * kx);
	int ncols = cvRound(srcimg.cols * ky);
	Mat resimg(nrows, ncols, srcimg.type());
	for (int i = 0; i < nrows; i++)
	{
    
    
		for (int j = 0; j < ncols; j++)
		{
    
    
			//根据水平因子计算坐标
			int x = static_cast<int>((i + 1) / kx + 0.5) - 1;
			//根据垂直因子计算坐标
			int y = static_cast<int>((j + 1) / ky + 0.5) - 1;
			resimg.at<Vec3b>(i, j) = srcimg.at<Vec3b>(x, y);
		}
	}
	return resimg;
}
//对图像进行放大
Mat imgUp_1(Mat& srcimg, float kx, float ky)
{
    
    
	int nrows = srcimg.rows * kx;
	int ncols = srcimg.cols * ky;
	Mat resimg(nrows, ncols, srcimg.type());
	for (int i = 0; i < nrows; i++)
	{
    
    
		//int x = i / kx;
		int x = static_cast<int>((i + 1) / kx + 0.7) - 1;
		for (int j = 0; j < ncols; j++)
		{
    
    

			//int y = j / ky;
			int y = static_cast<int>((j + 1) / ky + 0.7) - 1;
			resimg.at<Vec3b>(i, j) = srcimg.at<Vec3b>(x, y);
		}
	}
	return resimg;
}

Vec3b areaAverage(const Mat& srcimg, Point_<int> leftPoint, Point_<int> rightPoint)
{
    
    
	int tmp1 = 0, tmp2 = 0, tmp3 = 0;
	//计算区域字块像素点个数
		int nPix = (rightPoint.x - leftPoint.x + 1) * (rightPoint.y - leftPoint.y + 1);
	//对区域字块各个通道对像素值求和
	for (int i = leftPoint.x; i <= rightPoint.x; i++)
	{
    
    
		for (int j = leftPoint.y; j <= rightPoint.y; j++)
		{
    
    
			tmp1 += srcimg.at<Vec3b>(i, j)[0];
			tmp2 += srcimg.at<Vec3b>(i, j)[1];
			tmp3 += srcimg.at<Vec3b>(i, j)[2];
		}
	}
	//对每个通道求均值
	Vec3b vecTmp;
	vecTmp[0] = tmp1 / nPix;
	vecTmp[1] = tmp2 / nPix;
	vecTmp[2] = tmp3 / nPix;
	return vecTmp;
}

Mat imgDown_2(const Mat& srcimg, double kx, double ky)
{
    
    
	int nrows = srcimg.rows * kx;
	int ncols = srcimg.cols * ky;
	/*
	int nrows = cvRound(srcimg.rows * kx);
	int ncols = cvRound(srcimg.cols * ky);
	*/
	Mat resimg(nrows, ncols, srcimg.type());
	//区域子块的左上角行列坐标
	int leftRowCoordinate = 0;
	int leftColCoordinate = 0;
	for (int i = 0; i < nrows; i++)
	{
    
    
		//根据水平因子计算坐标
		int x = static_cast<int>((i + 1) / kx + 0.5) - 1;
		for (int j = 0; j < ncols; j++)
		{
    
    
			//根据垂直因子计算坐标
			int y = static_cast<int>((j + 1) / ky + 0.5) - 1;
			//求解区域子块的均值
			resimg.at<Vec3b>(i, j) = areaAverage(srcimg, Point_<int>(leftRowCoordinate, leftColCoordinate), Point_<int>(x, y));
			//resimg.at<Vec3b>(i, j) = srcimg.at<Vec3b>(x, y);
			//更新下子块左上角的列坐标,行坐标不变
			leftColCoordinate = y + 1;
		}
		leftColCoordinate = 0;
		//更新下子块左上角的行坐标
		leftRowCoordinate = x + 1;
	}
	return resimg;
}
//对图像进行放大
Mat imgUp_2(const Mat& srcimg, double kx, double ky)
{
    
    
	int nrows = srcimg.rows * kx;
	int ncols = srcimg.cols * ky;
	Mat resimg(nrows, ncols, srcimg.type());
	int leftRowCoordinate = 0;
	int leftColCoordinate = 0;
	for (int i = 0; i < nrows; i++)
	{
    
    
		int x = i / kx;
		for (int j = 0; j < ncols; j++)
		{
    
    
			int y = j / ky;
			//resimg.at<Vec3b>(i, j) = areaAverage(srcimg, Point_<int>(leftRowCoordinate, leftColCoordinate), Point_<int>(x, y));
			resimg.at<Vec3b>(i, j) = srcimg.at<Vec3b>(x, y);

			leftColCoordinate = y + 1;
		}
		leftColCoordinate = 0;
		leftRowCoordinate = x + 1;
	}
	return resimg;
}

int main()
{
    
    
	//Mat srcimg = imread("C:\\Users\\H\\Desktop\\1.png");
	Mat srcimg = imread("E:\\Lena.jpg");

	if (srcimg.empty())
	{
    
    
		return -1;
	}
	imshow("srcimg", srcimg);

	//自定义图像缩放模式
	Mat upimg1 = imgUp_1(srcimg, 2, 2);
	imshow("upimg1", upimg1);

	Mat resimg1 = imgDown_1(srcimg, 0.5, 0.5);
	imshow("resimg1", resimg1);

	Mat resimg2 = imgDown_2(srcimg, 0.5, 0.5);
	imshow("resimg2", resimg2);

	Mat upimg2 = imgUp_2(srcimg, 2, 2);
	imshow("upimg2", upimg2);

	//图像金子塔实现图像的缩放
	Mat pyrDownimg;
	pyrDown(srcimg, pyrDownimg);
	imshow("pyrDownimg", pyrDownimg);
	Mat pyrUpimg;
	pyrUp(srcimg, pyrUpimg);
	imshow("pyrUpimg", pyrUpimg);

	//resize方式实现图像的缩放
	Mat dstimg;
	const double scaleVal = 2;
	//resize(srcimg, dstimg, Size(srcimg.cols*0.5, srcimg.rows*0.5));
	resize(srcimg, dstimg, Size(srcimg.cols * 2, srcimg.rows * 2));
	imshow("dstimg", dstimg);

	waitKey(0);
	return 0;
}

insert image description here

7. Perspective transformation

After affine transformation, it is still a parallelogram and cannot be transformed arbitrarily.
insert image description here
#7.1 Principle of perspective transformation
Perspective transformation (Perspective Transformation) is to project a two-dimensional picture onto a three-dimensional viewing plane, and then convert it to two-dimensional coordinates, so it is also called Projective Mapping. Simply put, it is a process of two-dimensional → three-dimensional → two-dimensional.
Perspective transformation formula:

insert image description here

The perspective transformation matrix represents:
insert image description here

Affine transformations are a subset of perspective transformations. Next, convert to two-dimensional coordinates by dividing by the Z axis:
insert image description here

3D -> 2D in perspective transformation

Perspective transformation is more flexible than affine transformation. After transformation, a new quadrilateral will be generated, but it is not necessarily a parallelogram. Therefore, four non-collinear points are required to be uniquely determined. The straight line in the original image is still a straight line after transformation. Because quadrilaterals include all parallelograms, perspective transformations include all affine transformations.

7.2 Implementing Perspective Transformation

int main() {
    
    

	string path = "E:\\Lena.jpg";
	Mat img = imread(path);
	float w = 150, h = 250;
	Mat matrix, imgWarp;
	Point2f src[4] = {
    
     {
    
    96,94},{
    
    212,94},{
    
    96,209},{
    
    212,209} };
	Point2f dst[4] = {
    
     {
    
    0.0f,0.0f},{
    
    w,0.0f},{
    
    0.0f,h},{
    
    w,h} };
	matrix = getPerspectiveTransform(src, dst);
	warpPerspective(img, imgWarp, matrix, Point(w, h));
	for (int i = 0; i < 4; i++)
	{
    
    
		circle(img, src[i], 10, Scalar(0, 0, 255), FILLED);
	}

	imshow("Image", img);
	imshow("Image Warp", imgWarp);
	waitKey(0);
	return 0;
}

insert image description here

1)Mat getPerspectiveTransform(const Point2f* src, const Point2f* dst)
	//参数const Point2f* src:原图的四个固定顶点
	//参数const Point2f* dst:目标图像的四个固定顶点
	//返回值:Mat型变换矩阵,可直接用于warpAffine()函数
	//注意,顶点数组长度超4个,则会自动以前4个为变换顶点;数组可用Point2f[]或Point2f*表示
    //注意:透视变换的点选取变为4个2)C++ void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
	//参数InputArray src:输入变换前图像
	//参数OutputArray dst:输出变换后图像,需要初始化一个空矩阵用来保存结果,不用设定矩阵尺寸
	//参数InputArray M:变换矩阵,用另一个函数getAffineTransform()计算
	//参数Size dsize:设置输出图像大小
	//参数int flags = INTER_LINEAR:设置插值方式,默认方式为线性插值(另一种WARP_FILL_OUTLIERS)

Guess you like

Origin blog.csdn.net/jiyanghao19/article/details/132105405