上一节我们学习了如何对一幅图像进行放大与缩小(resize函数)以及对插值算的原理进行了分析,相信大家对图像如何进行放大与缩小已经有了清晰的理解,那么我们这节学习OpenCV中比较重要的内容,即仿射变换(warpAffine),仿射变换可以实现对稍微倾斜的图片进行矫正哦!!那么接下来我们就正式进入仿射变换的学习吧!
一、原理
仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,翻转,旋转和剪切。
这类变换可以用一个3*3的矩阵M来表示,其最后一行为(0,0,1)。该变换矩阵将原坐标为(x,y)变换为新坐标(x',y')
![3c8e218575ac2b034b5a5ca7f5f3cd81.png](https://i-blog.csdnimg.cn/blog_migrate/35d7c32daea22a89eb49d5a85a77200f.jpeg)
仿射变换
典型的仿射变换
1、平移
将每一点移到到(x+t , y+t),变换矩阵为
![38211a7efd765ffa35bf20925d4a8d57.png](https://i-blog.csdnimg.cn/blog_migrate/3bdb160939297647c55c13ca57da5927.jpeg)
平移
2、缩放变换
将每一点的横坐标放大或缩小s_x倍,纵坐标放大(缩小)到s_y倍,变换矩阵为
![2f8940cc23c0ec4d845df9d0cd4991b3.png](https://i-blog.csdnimg.cn/blog_migrate/4c2a50a0321c4a244711a540abafc78e.jpeg)
缩放变换
3、旋转变换原点
目标图形围绕原点顺时针旋转Θ 弧度,变换矩阵为
![9a04417f5b5b6e5485f6a2708e4ba5d6.png](https://i-blog.csdnimg.cn/blog_migrate/a1fd02dd54f1434855445b977577a0b9.jpeg)
旋转变换原点
4、旋转变换
目标图形以(x , y )为轴心顺时针旋转θ弧度,变换矩阵为
![30340f77ef34270d731651b3b22584f6.png](https://i-blog.csdnimg.cn/blog_migrate/8c355c79ec4b45589465bfd585077d0a.jpeg)
旋转变换
二、OpenCV中warpAffine()相关函数详解
(一)warpAffine()函数
1、函数原型
void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar());
2、函数功能
对图像进行仿射变换,当函数的参数flags设置为 WARP_INVERSE_MAP时,函数使用下面的矩阵进行仿射变换:
![2418dbb02fba1e619c70a48f9eca8445.png](https://i-blog.csdnimg.cn/blog_migrate/d90733b39f279592657aed09b4a0b5b7.jpeg)
仿射变换
否则,函数将首先使用逆仿射变换的转置,然后用上面的公式代替M,并且该函数不支持就地操作。
3、参数详解
- 第一个参数,InputArray src,原图像;
- 第二个参数,OutputArray dst,目标图像,并且该图像与原图像的尺寸和类型都是一样的;
- 第三个参数,InputArray M,2 x 3的变换矩阵;
- 第四个参数,Size dsize,目标图像的大小;
- 第五个参数,int flags = INTER_LINEAR,插值算法的标识符,默认为线性插值(INTER_LINEAR),如果插值算法为WARP_INVERSE_MAP,则函数使用下面的上面的公式[放射变换]进行图像转换;
- 第六个参数,int borderMode = BORDER_CONSTANT,边界像素的模式,默认值为BORDER_CONSTANT;
- 第七个参数,const Scalar& borderValue = Scalar(),边界取值,默认值为Scalar()。
4、实例
#include #include #include #include using namespace std;using namespace cv;//全局变量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("lena.png"); //判断文件是否加载成功 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()); //计算图像中点顺时针旋转90度,缩放因子为0.6的旋转矩阵 Point center = Point(warp_dstImage.cols / 2, warp_dstImage.rows / 2); double angle = -90.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;}
实验结果
![ef5c42625de8f6cd539b1f7877827d64.png](https://i-blog.csdnimg.cn/blog_migrate/d3707f7c33fecbaa3479cacb28ae9e2d.jpeg)
原图
![a7060f748056385cfa06e58d75ac3070.png](https://i-blog.csdnimg.cn/blog_migrate/bdbbbe5af345ed5f9b5ddd079b6ca467.jpeg)
仿射变换、仿射旋转变换、图像旋转90度
(二)getAffineTransform()函数
1、函数原型
Mat getAffineTransform(const Point2f src[], const Point2f dst[]);
2、函数功能
从三对相对应的点计算图像的仿射变换。
3、参数详解
- 第一个参数,const Point2f src[],坐标系中,原图像中三角形的坐标;
- 第二个参数,const Point2f dst[],坐标系中,目标图像中相应的(相对于原图像)三角型的坐标;
- 返回值为Mat类型的图像。
(三)getRotationMatrix2D()函数
1、函数原型
Mat getRotationMatrix2D(Point2f center, double angle, double scale);
2、函数功能
计算二维图像旋转的放射变换。
3、参数详解
- 第一个参数,Point2f center,原图像的旋转中心坐标;
- 第二个参数,double angle,旋转的角度,假定坐标原点为左上角,角度为正值的话,按逆时针的方向进行旋转,角度为负值的话,按顺时针的角度进行旋转;
- 第三个参数,double scale,图像的缩放因子;
- 返回值为Mat类型的图像。
三、综合实例
使用findTransformECC()函数实现图像对齐ECC算法
![437f93674c014e170d7214d383703981.png](https://i-blog.csdnimg.cn/blog_migrate/41cd29229b5a670956e4daef081817d1.jpeg)
![cdb6cb74a5412dd470fa9044d877ad55.png](https://i-blog.csdnimg.cn/blog_migrate/ffcce85e07466c9d38134887def00750.jpeg)
![fd5ef62097af133de1bc96435cfcc7de.png](https://i-blog.csdnimg.cn/blog_migrate/646004e157895ba31010899ea2b915d9.jpeg)
![78be9c701e459b3c6781e7e2280cec39.png](https://i-blog.csdnimg.cn/blog_migrate/5f041bef48a51091940bfa86729b9eb6.jpeg)
![faee0064474e3651ddcf0959b928accf.png](https://i-blog.csdnimg.cn/blog_migrate/2ea5d55add33e1812ccbfe83cf642f85.jpeg)
![84577b1fe58d763e07720cf61848c546.png](https://i-blog.csdnimg.cn/blog_migrate/eca05fe3f5f4aa36df5f00348f6c8733.jpeg)
![938d0dd5b02997a0ebc0e24ab9cf5b15.png](https://i-blog.csdnimg.cn/blog_migrate/1ff9e781121ad5430e838ded05c72886.jpeg)
![2ad0de43d1131fdc2199182994053809.png](https://i-blog.csdnimg.cn/blog_migrate/baaa3415c909406622ca090a25288388.jpeg)
![1446df1e6d3e647728baf2bd135d31a8.png](https://i-blog.csdnimg.cn/blog_migrate/97a1e3da74f3870b26fea1507c5302ce.jpeg)
![29dd479808c9c33badfbe15c040d11ff.png](https://i-blog.csdnimg.cn/blog_migrate/cde77d71bafc615aae7b3ffffdfe0be7.jpeg)
![39aab420ae41bdbf56845aa327d76f19.png](https://i-blog.csdnimg.cn/blog_migrate/f2fb96f15158e10e4c2625c990a175f7.jpeg)
![3db7853ca9a10a088443ac43b3456379.png](https://i-blog.csdnimg.cn/blog_migrate/8ac3e5d31791a2855d64a7a251a58c3a.jpeg)
![2c0edc6197e3fc6ad3712240a9462962.png](https://i-blog.csdnimg.cn/blog_migrate/97a26107babee126168a5235c3be5351.jpeg)
![9730e652b392b36ff30350e766455b03.png](https://i-blog.csdnimg.cn/blog_migrate/5b9677094487494b8c9fd0f079366c81.jpeg)
![4f2837773ddef09231c0ab70e3df6203.png](https://i-blog.csdnimg.cn/blog_migrate/541ba2bf2063a6b69cd9b32b03e4e88b.jpeg)
![87bebeb6acbd6386305358b329c9b3d8.png](https://i-blog.csdnimg.cn/blog_migrate/6badd8c865cbb792bff1474dc496f1c6.jpeg)
实验结果
![2586f19221ee753cdcdf1a0f947c16f7.png](https://i-blog.csdnimg.cn/blog_migrate/cb271a1ed887bb63bba286623adf1e86.jpeg)
实验结果
好了,今天的学习到这里就结束了,希望大家可以认真学习学习每一节知识,加油,相信大家可以的!喜欢的朋友也可以给我点个赞哦!!