一、opencv仿射与旋转原理
- 仿射变换矩阵求取
warp_x = getAffineTransform( srcTri, dstX )
// 设置源图像和目标图像上的三组点以计算仿射变换
srcTri[0] = cv::Point2f( 0.0,0.0 );
srcTri[1] = cv::Point2f( src.cols - 1, 0.0 );
srcTri[2] = cv::Point2f( 0.0, src.rows - 1 );
//以y轴(中轴)旋转,h不变,w缩小
float err_x = 0.5*src.cols*(1.0 - cos(angley));
cout << "err_x = "<< err_x <<endl;
dstY[0] = cv::Point2f( err_x,0.0 );
dstY[1] = cv::Point2f( src.cols - 1 - err_x, 0.0);
dstY[2] = cv::Point2f( err_x, src.rows - 1 );
//求取仿射矩阵
warp_y = getAffineTransform( srcTri, dstY );
原理:参考
图像旋转算法原理
3D中绕坐标轴的旋转
- 绕点旋转(即在该二维平面内,认为绕z轴)
- 绕y轴旋转(图像宽度cols改变,y不变)
- 绕x轴旋转(图像高度rows改变,x不变)
2.仿射变换应用
warpAffine( src, src_x, warp_x, src_x.size());
参考仿射变换详解 warpAffine
// 求得仿射变换
warp_x = getAffineTransform( srcTri, dstX );
// 对源图像应用上面求得的仿射变换
warpAffine( src, src_x, warp_x, src_x.size());
cv::imshow("x",src_x);
3、旋转
求旋转矩阵(原理同仿射矩阵绕z轴旋转)
实现函数:rot = cv::getRotationMatrix2D(center, angle, 1);
应用仿射变换。
二、代码实现
结果如图:
- 因为图像旋转输入参数为点和角度,所以我想让绕x、y轴的旋转后图像(伪3D)的函数也以角度为入口。
通过投影关系,得出图像仿射后的坐标变换关系(以绕Y轴为例):
err_y = 0.5rows(1 – cosθ)
同理 err_x = 0.5cols(1 – cosθ)
所以可以得到仿射变换后原来三点的坐标。
//用投影方法 3d旋转转为2d仿射变换
/*
*
* ^ y
* |
* |
* |
* --------> x
* */
cv::Mat warp_change(cv::Mat src,float anglex,float angley)
{
anglex = anglex/180*3.1415;
angley = angley/180*3.1415;
cv::Point2f srcTri[3];
cv::Point2f dstX[3];
cv::Point2f dstY[3];
cv::Point2f dstXY[3];
cv::Mat warp_x( 2, 3, CV_32FC1 ),warp_y( 2, 3, CV_32FC1 ),warp_xy( 2, 3, CV_32FC1 );
cv::Mat src_x,src_y,warp_dst;
// 设置源图像和目标图像上的三组点以计算仿射变换
srcTri[0] = cv::Point2f( 0.0,0.0 );
srcTri[1] = cv::Point2f( src.cols - 1, 0.0 );
srcTri[2] = cv::Point2f( 0.0, src.rows - 1 );
//以x轴(中轴)旋转,w不变,h缩小
float err_y = 0.5*src.rows*(1.0 - cos(anglex));
cout << "err_y = "<< err_y <<endl;
dstX[0] = cv::Point2f(0.0,err_y);
dstX[1] = cv::Point2f(src.cols - 1,err_y);
dstX[2] = cv::Point2f(0.0,src.rows - 1 - err_y);
//以y轴(中轴)旋转,h不变,w缩小
float err_x = 0.5*src.cols*(1.0 - cos(angley));
cout << "err_x = "<< err_x <<endl;
dstY[0] = cv::Point2f( err_x,0.0 );
dstY[1] = cv::Point2f( src.cols - 1 - err_x, 0.0);
dstY[2] = cv::Point2f( err_x, src.rows - 1 );
//以y轴(中轴)旋转,h不变,w缩小
dstXY[0] = cv::Point2f( dstY[0].x,dstX[0].y );
dstXY[1] = cv::Point2f( dstY[1].x,dstX[1].y );
dstXY[2] = cv::Point2f( dstY[2].x,dstX[2].y );
// 求得仿射变换
warp_x = getAffineTransform( srcTri, dstX );
// 对源图像应用上面求得的仿射变换
warpAffine( src, src_x, warp_x, src_x.size());
cv::imshow("x",src_x);
warp_y = getAffineTransform( srcTri, dstY );
warpAffine( src, src_y, warp_y, src_y.size() );
cv::imshow("y",src_y);
warp_xy = getAffineTransform( srcTri, dstXY );
warpAffine(src,warp_dst, warp_xy, warp_dst.size());
//cv::imshow("Xy",warp_dst);
return warp_dst;
}
- 2d旋转并确定新的图像大小
cv::Mat rotate2D_change(cv::Mat image,cv::Point center,double angle,float scale)
{
// cv::Mat rot_mat( 2, 3, CV_32FC1 );
// cv::Mat result1;
// rot_mat = cv::getRotationMatrix2D( center, angle, scale );
// warpAffine( image, result1, rot_mat, image.size() );
// cv::imshow("result1",result1);
cv::Mat rot( 2, 3, CV_32FC1 );
cv::Mat result;
rot = cv::getRotationMatrix2D(center, angle, 1); //求旋转矩阵
cv::Rect bbox = cv::RotatedRect(center, image.size(), angle).boundingRect(); //求新的外切矩形
rot.at<double>(0, 2) += bbox.width / 2.0 - center.x;
rot.at<double>(1, 2) += bbox.height / 2.0 - center.y;
cv::warpAffine(image, result,rot,bbox.size(),1,0,cv::Scalar(255,255,255)); //原图像旋转
return result;
}
更正 (2018.12.13):
以上没有正确使用仿射变换,更正如下:
因此,不用opencv三点法,直接矩阵赋值。函数输入 绕x、y的角度,输出仿射变换后的图片。
/* 1 0(x) 0
* (y)0 1 0
*
*/
void warp_init(cv::Mat m)
{
m.at<float>(0,0) = 1; //(y,x)
m.at<float>(1,0) = 0; // x
m.at<float>(0,1) = 0;
m.at<float>(1,1) = 1;
m.at<float>(0,2) = 0;
m.at<float>(1,2) = 0;
m.at<float>(2,0) = 0;
}
cv::Mat warp_change(cv::Mat src,float anglex,float angley)
{
anglex = anglex/180*3.1415;
angley = angley/180*3.1415;
cv::Mat warp_mat( 2, 3, CV_32FC1 );
cv::Mat warp_dst;
warp_init(warp_mat);
float resize_h = src.rows/(tan(anglex)*src.cols+ src.rows);
float resize_w = src.cols/(tan(angley)*src.rows+ src.cols);
//int Translation_x = 0.5*(1-resize_x)*src.cols;
//int Translation_y = 0.5*(1-resize_y)*src.rows;
warp_mat.at<float>(1,0) = sin(anglex); //绕y旋转.对应高缩小
warp_mat.at<float>(0,1) = sin(angley); //绕x旋转.对应宽缩小
warp_mat.at<float>(0,0) = resize_w; //宽缩小(原点左上角) 这里是比例
warp_mat.at<float>(1,1) = resize_h; //高缩小
//warp_mat.at<float>(0,2) = Translation_x; //+向右平移 这里是真实坐标(左上角从0,0开始)
//warp_mat.at<float>(1,2) = Translation_y; //+向下平移
warpAffine(src, warp_dst, warp_mat, warp_dst.size());
return warp_dst;
}
需要说明的是,仿射矩阵的各个参数对应,参考仿射矩阵学习很详细。
平移变换的值为像素值!