文章目录
cpp
**仿射变换相关函数**
cv::transform():对一组点进行仿射变换
cv::warpAffine():对整幅图像进行仿射变换
cv::getAffineTransform():从一组点计算仿射变换矩阵
cv::getRotationMatrix2D():计算旋转矩阵
**投影变换相关的函数**
cv::perspectiveTransform():对一组点进行透射变换/投影变换
cv::warpPerspective():对整幅图像进行透视变换/投影变换
cv::getPerspectiveTransform():获取透视变换/投影变换矩阵
cv::findHomography():计算单应性矩阵
引言
图像的几何变换通常包括拉伸、缩放、扭曲和旋转等操作。
对于平面区域来说,分为两类几何转换:
- 仿射变换 (affine transform),基于2x3矩阵进行变换。指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指图像在完成仿射变换后,平行线仍然是平行线。
- 透视变换 (perspective transform),基于3x3矩阵进行变换。透视变换将视锥体转换为长方体形状,视锥体的近端比远端小,具有扩大相机附近物体的效果。透视变换可以改变平行关系,将矩形映射为任意四边形。
图像仿射变换 warpAffine()
图像的旋转
getRotationMatrix2D()函数通过输入旋转中心、旋转角度、旋转过程两轴的比例因子,获得一个Mat类型的旋转矩阵对象:
cpp
Mat cv::getRotationMatrix2D(Point2f center,
double angle,
double scale //两轴的比例因子,输入1则不缩放
)
旋转变换是仿射变换的一个特殊情况,将此矩阵用于仿射变换函数即可仅作图像旋转
仿射变换
仿射变换又称为三点变换。如果知道目标变换前后的三个像素点坐标之间的关系,即可求出仿射变换矩阵M。
opencv提供了getAffineTransform函数用于计算仿射变换矩阵:
cpp
//只需要提供原图像和目标图像的三个点的坐标
Mat cv::getAffineTransform(const Point2f src[],
const Point2f dst[] //输入点类数组
)
cpp
void cv::warpAffine(InputArray src,
OutputArray dst,
InputArray M, //仿射变换矩阵M
Size dsize,
int flags = INTER_LINEAR, //插值方法
int borderMode = BORDER_CONSTANT, //像素边界外推方法
const Scalar & nprderValue = Scalar()
)
注:仿射变换的矩阵M是2×3的矩阵
透视变换 warpPerspective()
透视变换:按照物体的成像投影规律将图像重新投影。常见用例是修正镜头与拍摄目标存在斜角时产生的图像畸变。
使用getPerspectiveTransform()函数获取变换矩阵
cpp
//只需要提供原图像和目标图像的四个点的坐标
Mat cv::getPerspectiveTransform(const Point2f src[],
const Point2f dst[],
int solveMethod = DECOMP_LU //求解方法
)
在不知道四个坐标点的情况下,可以使用原图和目标图的匹配点计算变换矩阵:
cpp
Mat cv::findHomography ( InputArray srcPoints,
InputArray dstPoints,
int method = 0,
double ransacReprojThreshold = 3,
OutputArray mask = noArray(),
const int maxIters = 2000,
const double confidence = 0.995
)
method参数详解:
cpp
method 计算单应矩阵所使用的方法。不同的方法对应不同的参数,具体如下:
0 - 利用所有点的常规方法
RANSAC - RANSAC-基于RANSAC的鲁棒算法
LMEDS - 最小中值鲁棒算法
RHO - PROSAC-基于PROSAC的鲁棒算法
使用warpPerspective()函数应用透视变换
cpp
void cv::warpPerspective(InputArray src,
OutputArray dst,
InputArray M, //变换矩阵M
Size dsize,
int flags = INTER_LINEAR, //插值方法
int borderMode = BORDER_CONSTANT, //像素边界外推方法
const Scalar & nprderValue = Scalar()
)
注:透视变换的矩阵M是3×3的矩阵
透视变换例子
cpp
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
struct callbackP
{
Mat src;
int clickTimes = 0; //在图像上单击次数
vector<Point2f> srcTri;
};
void onMouse(int event, int x, int y, int flags, void *utsc)
{
callbackP cp = *(callbackP*)utsc; // 先转换类型,再取数据
if (event == EVENT_LBUTTONUP) //响应鼠标左键事件
{
circle((*(callbackP*)utsc).src, Point(x, y), 2, Scalar(0, 0, 255), 2); //标记选中点
imshow("wait ", (*(callbackP*)utsc).src);
(*(callbackP*)utsc).srcTri.push_back(Point2f(x, y));
cout << "x:" << x << " " << "y:" << y << endl;
(*(callbackP*)utsc).clickTimes++;
if ((*(callbackP*)utsc).clickTimes == 4)
{
cout << "按任意键继续!" << endl;
}
}
}
int main(int argc, char *argv[])
{
vector<Point2f> dstTri(4);
Mat dst;
callbackP utsc;
utsc.src = imread("tt.jpg");
namedWindow("src", WINDOW_AUTOSIZE);
imshow("src", utsc.src);
cout << "从需要透视变换区域的左上角开始,顺时针依次点矩形的四个角!" << endl;
setMouseCallback("src", onMouse, (void*)&utsc); //类型转换
waitKey();
if (utsc.clickTimes == 4)
{
dstTri[0].x = 0;
dstTri[0].y = 0;
dstTri[1].x = utsc.srcTri[1].x - utsc.srcTri[0].x;
dstTri[1].y = 0;
dstTri[2].x = utsc.srcTri[1].x - utsc.srcTri[0].x;
dstTri[2].y = utsc.srcTri[2].y - utsc.srcTri[1].y;
dstTri[3].x = 0;
dstTri[3].y = utsc.srcTri[2].y - utsc.srcTri[1].y;
//计算透视矩阵
Mat M = findHomography(utsc.srcTri, dstTri, RANSAC);
//图像透视变换
warpPerspective(utsc.src, dst, M, Size((utsc.srcTri[1].x - utsc.srcTri[0].x), (utsc.srcTri[2].y - utsc.srcTri[1].y)));
imshow("output", dst);
imwrite("3p.jpg", dst);
cout << "透视变换矩阵:" << M << endl;
waitKey();
}
else
{
cout << "需要从左上角开始,顺时针依次点矩形的四个角!" << endl;
cout << "现在点击了" << utsc.clickTimes << "次" << endl;
}在这里插入图片描述
cv::destroyAllWindows();
return 0;
}
参考文献
图像几何变换(仿射变换和透视变换...)及python-opencv实现
OpenCV学习笔记 02
opencv的单应性矩阵