Opencv学习记录(五)——图像仿射变换

一、opencv仿射与旋转原理

  1. 仿射变换矩阵求取
    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;
}

需要说明的是,仿射矩阵的各个参数对应,参考仿射矩阵学习很详细
平移变换的值为像素值

平移变换的值为像素值!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值