0.引言
纯代码.
1.透视变换
- 1.计算旋转矩形。
- 2.基于矩形的四个顶点和想要抠出的正矩形的四个顶点得到一个变换矩阵。
- 3.通过透视变换,将四个点组成的平面转换成另四个点组成的一个平面,以此抠出正矩形。
#include <cmath>
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat img = imread("../test.png");
// 定义旋转矩形区域的中心点、宽度、高度和旋转角度
Point2f center(150, 215);
float width = 190;
float height = 150;
float angle = 30;
// 计算旋转矩形的四个顶点坐标
float radians = angle * CV_PI / 180.0;
float cos_angle = cos(radians);
float sin_angle = sin(radians);
vector<Point2f> src_pts;
src_pts.push_back(Point2f(-width / 2, -height / 2));
src_pts.push_back(Point2f(width / 2, -height / 2));
src_pts.push_back(Point2f(width / 2, height / 2));
src_pts.push_back(Point2f(-width / 2, height / 2));
for (int i = 0; i < 4; i++) {
float x = src_pts[i].x * cos_angle - src_pts[i].y * sin_angle + center.x;
float y = src_pts[i].x * sin_angle + src_pts[i].y * cos_angle + center.y;
if (x < 0 || y < 0 || x >= img.cols || y >= img.rows) {
std::cout << "vertex point out of range.";
return;
}
src_pts[i] = Point2f(x, y);
}
// 定义裁剪后输出的图像大小
int output_width = 200;
int output_height = 150;
// 定义目标图像的四个点
vector<Point2f> dst_pts;
dst_pts.push_back(Point2f(0, 0));
dst_pts.push_back(Point2f(output_width - 1, 0));
dst_pts.push_back(Point2f(output_width - 1, output_height - 1));
dst_pts.push_back(Point2f(0, output_height - 1));
// 计算透视变换矩阵
Mat M = getPerspectiveTransform(src_pts, dst_pts);
// 应用透视变换
Mat output_img;
warpPerspective(img, output_img, M, Size(output_width, output_height));
// 显示结果
imshow("Original Image", img);
imshow("Output Image", output_img);
waitKey(0);
return 0;
}
2.仿射变换
- 1.计算旋转矩形。
- 2.基于旋转矩形的中心和角度计算得到一个变换矩阵。
- 3.以旋转矩形的中心为基准点,对整张图进行旋转,这里旋转的实现是基于仿射变换实现的。
由于旋转之后矩形的中点坐标是不变的,以中心为基础,通过width和height抠出正矩形。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
// 读取输入图像
Mat img = imread("../test.png");
// 定义旋转矩形区域
RotatedRect rect(Point2f(300, 300), Size2f(200, 100), 45);
// 计算旋转矩形的仿射变换矩阵
Mat M = getRotationMatrix2D(rect.center, rect.angle, 1.0);
// 旋转图像
Mat rotated;
warpAffine(img, rotated, M, img.size(), INTER_CUBIC);
// 裁剪旋转矩形区域
Mat cropped;
getRectSubPix(rotated, rect.size, rect.center, cropped);
// 显示结果
imshow("Input", img);
imshow("Cropped and Rotated", cropped);
waitKey(0);
return 0;
}
3.总结
- 仿射变换方法需要预先对整张图进行旋转,通过观察旋转后的图像可以发现,有一部分图像被旋转出了图像边界,如果你要抠取的目标正好在图像边缘附近,那么很容易出界导致图像抠取的缺失。同时需要对宽、高和角度做出动态的调整;
- 透视变换的方法直接对抠取区域进行了映射,这种方法可以省略旋转的步骤,并且不会出现抠取内容的缺失。同时只需要对4个顶点之间的映射关系做好定义即可,不需要考虑角度的问题。
- 相对的,透视变换相对于仿射变换计算量更大一些,不过这在c++的底层实现上带来的时延差距小于ms。