OpenCV 图像仿射变换之旋转

发布于:2025-06-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、知识点
1、void warpAffine(InputArray src, 
                  OutputArray dst, 
                  InputArray M, 
                  Size dsize, 
                  int flags = INTER_LINEAR, 
                  int borderMode = BORDER_CONSTANT, 
                  const Scalar & borderValue = Scalar());

(1)、对图像应用仿射变换(旋转、平移、缩放)。
(2)、参数说明:
    src: 输入图像。
    dst: 输出图像,大小为dsize,数据类型与src相同。
    M: 2 * 3转换矩阵(两行三列)。
    dsize: 输出图像的大小。
    flags: 插值方法,InterpolationFlags枚举值。可和WARP_INVERSE_MAP组合,意味着M是逆变换(dst-->src)。
    borderMode: 边界模式,像素外推方法,BorderTypes枚举值。
    borderValue: 边界值。 当borderMode为BORDER_CONSTANT时,borderValue为边界的像素颜色。
(3)、仿射变换公式:

dst(x,y) = src(M11x + M12y + M13, M21x + M22y + M23)

    | M11 M12 M13 |     | x |
    |             |  *  | y |
    | M21 M22 M23 |     | 1 |


2、在OpenCV中,旋转矩阵M为:

    | cosθ   sinθ   0 |
    |                 |
    | -sinθ  cosθ   0 |


3、在OpenCV中,平移矩阵M为:

    | 1   0   dx |
    |            |
    | 0   1   dy |


4、但是2、3的矩阵只相对于原点变换,实际工作中,经常是2、3的结合。 
  对于绕任意点的旋转矩阵,OpenCV中提供getRotationMatrix2D()获取。

5、Mat getRotationMatrix2D(Point2f center, double angle, double scale);
  (1)、计算2D旋转的仿射矩阵,可以用来对图像进行旋转。
  (2)、参数说明:
      center: 源图像的旋转中心点,即源图像要围绕此点旋转。
      angle: 旋转的角度,以度为单位,正值逆时针旋转,负值顺时针旋转。
      scale: 缩放因子。 1.0不改变大小,0.5缩小一半,2.0放大一倍。
  (3)、原点在源图像的左上角。
    
6、旋转后新的图像大小计算:

  nw = w * cosθ + h * sinθ;
  nh = h * cosθ + w * sinθ;

二、示例代码

#include <iostream>
#include <opencv2/opencv.hpp>


int main()
{
    //1.获取源图像
    cv::Mat src = cv::imread("../images/9.png");
    if (src.empty())
    {
        std::cout << "load src image error..." << std::endl;
        return -1;
    }
    cv::imshow("源图像", src);

    //2.获取源图像宽、高
    int w = src.cols;
    int h = src.rows;

    //3.获取围绕源图像中心点,逆时针旋转45度,不缩放的矩阵
    cv::Mat M = cv::getRotationMatrix2D(cv::Point2f(w / 2, h / 2), 45, 1.0);

    //4.仿射变换,但是旋转后图像四角被截断,并且有黑色背景
    cv::Mat dst1;
    cv::warpAffine(src, dst1, M, cv::Size(w, h));
    cv::imshow("绕中心点逆时针旋转45度", dst1);

    //5.改变旋转后图像大小,源图像四角不被截断
    double cosTheta = cv::abs(M.at<double>(0, 0));
    double sinTheta = cv::abs(M.at<double>(0, 1));
    int nw = w * cosTheta + h * sinTheta;
    int nh = h * cosTheta + w * sinTheta;
    M.at<double>(0, 2) += (nw / 2.0 - w / 2.0);
    M.at<double>(1, 2) += (nh / 2.0 - h / 2.0);
    cv::Mat dst2;
    cv::warpAffine(src, dst2, M, cv::Size(nw, nh));
    cv::imshow("改变大小,源图像四角不被截断", dst2);

    //6.填充背景
    cv::Scalar backColor(src.at<cv::Vec3b>(0, 0)[0], src.at<cv::Vec3b>(0, 0)[1], src.at<cv::Vec3b>(0, 0)[2]);
    cv::Mat dst3;
    cv::warpAffine(src, dst3, M, cv::Size(nw, nh), cv::INTER_LINEAR, 0, backColor);
    cv::imshow("填充背景", dst3);

    cv::waitKey(0);
    return 0;
}

输出结果:


网站公告

今日签到

点亮在社区的每一天
去签到