opencv—图像旋转函数,rotate与warpAffine运用(不裁剪crop方法)
引言Oo
图像旋转是图像几何变换中具有代表性的操作,直接调用opencv库函数很简单,但是叫你讲解一下,可能能多人会直接放弃。这里面包含了插值、背景处理、三角函数等一些知识,我也今早看了一篇推文,自己写了一下,发现还是有很多基础知识,于是作一次记录。
图像旋转基本原理
图像旋转之后大小会有变化,就会产生背景,背景一般默认填充为黑色,即0值。同时,原本的和新产生的像素会发生位置迁移,新的位置的像素需要进行插值进行处理,插值处理包括最近邻、线性插值和立方插值等方法(默认为二插值)。
这是旋转后的高度映射,
//h和w为原图像的高与宽,CV_PI/180等于弧度制中角度1度对应的值
int bound_w = (h * fabs(sin(angle * CV_PI / 180)) + w * fabs(cos(angle * CV_PI / 180))) ;
int bound_h = (h * fabs(cos(angle * CV_PI / 180)) + w * fabs(sin(angle * CV_PI / 180)));
这是简写的旋转矩阵,2*3的矩阵:
原本图像的左上角是 原点,要实现中心旋转,旋转 矩阵需要重新计算,
其中scale是表示支持旋转与放缩,第三列就是易错点,图像旋转之后中心位置的平移量。
函数讲解
我们在opencv中找到两个函数支持旋转,其中一个是Rotate,另一个是WarpAffine。
Rotate函数原型:
//函数原型,可以看出它只支持90、180、270这样的特殊角度旋转
CV_EXPORTS_W void rotate(InputArray src, OutputArray dst, int rotateCode);
//下面为源码解释
/** @brief Fills the output array with repeated copies of the input array.
The function cv::repeat duplicates the input array one or more times along each of the two axes:
\f[\texttt{dst} _{ij}= \texttt{src} _{i\mod src.rows, \; j\mod src.cols }\f]
The second variant of the function is more convenient to use with @ref MatrixExpressions.
@param src input array to replicate.
@param ny Flag to specify how many times the `src` is repeated along the
vertical axis.
@param nx Flag to specify how many times the `src` is repeated along the
horizontal axis.
@param dst output array of the same type as `src`.
@sa cv::reduce
*/
//第三个参数可选
ROTATE_180,
ROTATE_90_CLOCKWISE,
ROTATE_90_COUNTERCLOCKWISE
可以看出它只支持90、180、270这样的特殊角度旋转。
函数warpAffine支持任意角度的旋转,但通过定义M矩阵实现,
函数原型:
CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,//输入与输出图像
InputArray M,//旋转矩阵
Size dsize,//输出大小
int flags = INTER_LINEAR,插值方式
int borderMode = BORDER_CONSTANT,背景填充默认为常量
const Scalar& borderValue = Scalar());//填充默认为黑色
到此我们可以看到,我们需要生成一个旋转矩阵M,opencv也提供了函数getRotationMatrix2D,
函数原型:
Mat getRotationMatrix2D(Point2f center,
double angle,
double scale);
代码演示
演示平台win10,vs2019,opencv4.4
//对图像进行旋转,不crop的方法,对旋转矩阵的参数修改,以及输出Size大小的确认
#include"opencv.hpp"
#include<iostream>
using namespace std;
using namespace cv;
int main() {
Mat src = imread("timg.jpg");
Mat des,m;
//des = src;
//rotate(src, des, ROTATE_90_COUNTERCLOCKWISE);
Point2f center = Point(src.cols / 2, src.rows / 2);
double angle = 50,scale=0.5;
int h = src.cols,w=src.rows;
//目标图像的大小,避免crop,fabs(sin(angle * CV_PI / 180)
int bound_w = (h * fabs(sin(angle * CV_PI / 180)) + w * fabs(cos(angle * CV_PI / 180))) * scale;
int bound_h = (h * fabs(cos(angle * CV_PI / 180)) + w * fabs(sin(angle * CV_PI / 180))) * scale;
m = getRotationMatrix2D(center, angle, scale);
m.at<double>(0, 2) += (bound_w - src.cols) / 2;
m.at<double>(1, 2) += (bound_h - src.rows) / 2;
warpAffine(src,des,m,Size2i(bound_h,bound_w));
imshow("image",des);
waitKey();
return 0;
}
原图
效果图
不足点
图像的四周还是有裁剪到,说明这个代码还是有bug的,但问题不大,主要就是在旋转矩阵中心点的平移上,还有就是不同类型的数值转换造成的数值偏差。暂时做一下记录,以后有空继续看看。