19.图像仿射变换
在OpenCV 4中并没有专门用于图像旋转的函数,而是通过图像的仿射变换实现图像的旋转。实现图像的旋转首先需要确定旋转角度和旋转中心,之后确定旋转矩阵,最终通过仿射变换实现图像旋转。针对这个流程,OpenCV 4提供了getRotationMatrix2D()函数用于计算旋转矩阵和warpAffine()函数用于实现图像的仿射变换。首先介绍计算旋转矩阵getRotationMatrix2D()函数,该函数的函数原型在代码清单3-31中给出。
代码清单3-31 getRotationMatrix2D()函数原型 1. Mat cv::getRotationMatrix2D (Point2f center, double angle, double scale)
-
center:图像旋转的中心位置。
-
angle:图像旋转的角度,单位为度,正值为逆时针旋转。
-
scale:两个轴的比例因子,可以实现旋转过程中的图像缩放,不缩放输入1。
该函数输入旋转角度和旋转中心,返回图像旋转矩阵,该返回值得数据类型为Mat类,是一个2×3的矩阵。如果我们已知图像旋转矩阵,可以自己生成旋转矩阵而不调用该函数。该函数生成的旋转矩阵与旋转角度和旋转中心的关系如式(3.11)所示。
确定旋转矩阵后,通过warpAffine()函数进行仿射变换,就可以实现图像的旋转,在代码清单3-32中给出了warpAffine()函数的函数原型。
代码清单3-32 warpAffine()函数原型 1. void cv::warpAffine(InputArray src, 2. OutputArray dst, 3. InputArray M, 4. Size dsize, 5. int flags = INTER_LINEAR, 6. int borderMode = BORDER_CONSTANT, 7. const Scalar& borderValue = Scalar() 8. )
-
src:输入图像。
-
dst:仿射变换后输出图像,与src数据类型相同,但是尺寸与dsize相同。
-
M:2×3的变换矩阵。
-
dsize:输出图像的尺寸。
-
flags:插值方法标志,可选参数及含义在表3-3和表3-4中给出。
-
borderMode:像素边界外推方法的标志。
-
borderValue:填充边界使用的数值,默认情况下为0。
该函数拥有多个参数,但是多数都与前面介绍的图像尺寸变换具有相同的含义。函数中第三个参数为前面求取的图像旋转矩阵,第四个参数是输出图像的尺寸。函数第五个参数是仿射变换插值方法的标志,这里相比于图像尺寸变换多增加了两个类型,可以与其他插值方法一起使用,这两种类型在表3-4中给出。函数第六个参数为像素边界外推方法的标志,其可以的标志和对应的方法在表3-5中给出。第七个参数是外推标志选择BORDER_CONSTANT时的定值,默认情况下为0。
表3-4 图像仿射变换中的补充插值方法
标志参数 |
简记 |
作用 |
WARP_FILL_OUTLIERS |
8 |
填充所有输出图像的像素,如果部分像素落在输入图像的边界外,那么他们的值设定为fillval |
WARP_INVERSE_MAP |
16 |
表示M为输出图像到输入图像的反变换。 |
表3-5 边界填充方法和对应标志
标志参数 |
简记 |
作用 |
BORDER_CONSTANT |
0 |
用特定值填充,如iiiiii|abcdefgh|iiiiiii |
BORDER_REPLICATE |
1 |
两端复制填充,如aaaaaa|abcdefgh|hhhhhhh |
BORDER_REFLECT |
2 |
倒叙填充,如fedcba|abcdefgh|hgfedcb |
BORDER_WRAP |
3 |
正序填充,如cdefgh|abcdefgh|abcdefg |
BORDER_REFLECT_101 |
4 |
不包含边界值倒叙填充,如gfedcb|abcdefgh|gfedcba |
BORDER_TRANSPARENT |
5 |
随机填充,uvwxyz|abcdefgh|ijklmno |
BORDER_REFLECT101 |
4 |
与BORDER_REFLECT_101相同 |
BORDER_DEFAULT |
4 |
与BORDER_REFLECT_101相同 |
BORDER_ISOLATED |
16 |
不关心感兴趣区域之外的部分 |
仿射变换又称为三点变换,如果知道变换前后两张图像中三个像素点坐标的对应关系,就可以求得仿射变换中的变换矩阵,OpenCV 4提供了利用三个对应像素点来确定矩阵的函数getAffineTransform(),该函数的函数原型在代码清单3-33中给出。
代码清单3-33 getAffineTransform()函数原型 1. Mat cv::getAffineTransform(const Point2f src[], 2. const Point2f dst[] 3. )
-
src[]:原图像中的三个像素坐标。
-
dst[]:目标图像中的三个像素坐标。
代码清单3-34 myWarpAffine.cpp图像旋转与仿射变换 1. #include <opencv2\opencv.hpp> 2. #include <iostream> 3. #include <vector> 4. 5. using namespace std; 6. using namespace cv; 7. 8. int main() 9. { 10. Mat img = imread("lena.png"); 11. if (img.empty()) 12. { 13. cout << "请确认图像文件名称是否正确" << endl; 14. return -1; 15. } 16. 17. Mat rotation0, rotation1, img_warp0, img_warp1; 18. double angle = 30; //设置图像旋转的角度 19. Size dst_size(img.rows, img.cols); //设置输出图像的尺寸 20. Point2f center(img.rows / 2.0, img.cols / 2.0); //设置图像的旋转中心 21. rotation0 = getRotationMatrix2D(center, angle, 1); //计算放射变换矩阵 22. warpAffine(img, img_warp0, rotation0, dst_size); //进行仿射变换 23. imshow("img_warp0", img_warp0); 24. //根据定义的三个点进行仿射变换 25. Point2f src_points[3]; 26. Point2f dst_points[3]; 27. src_points[0] = Point2f(0, 0); //原始图像中的三个点 28. src_points[1] = Point2f(0, (float)(img.cols - 1)); 29. src_points[2] = Point2f((float)(img.rows - 1), (float)(img.cols - 1)); 30. //放射变换后图像中的三个点 31. dst_points[0] = Point2f((float)(img.rows)*0.11, (float)(img.cols)*0.20); 32. dst_points[1] = Point2f((float)(img.rows)*0.15, (float)(img.cols)*0.70); 33. dst_points[2] = Point2f((float)(img.rows)*0.81, (float)(img.cols)*0.85); 34. rotation1 = getAffineTransform(src_points, dst_points); //根据对应点求取仿射变换矩阵 35. warpAffine(img, img_warp1, rotation1, dst_size); //进行仿射变换 36. imshow("img_warp1", img_warp1); 37. waitKey(0); 38. return 0; 39. }