OpenCV图像旋转原理及示例

发布于:2025-05-16 ⋅ 阅读:(11) ⋅ 点赞:(0)

OpenCV计算机视觉开发实践:基于Qt C++ - 商品搜索 - 京东

图像旋转是数字图像处理的一个非常重要的环节,是图像的几何变换手法之一。图像旋转算法是图像处理的基础算法。在数字图像处理过程中,经常要用到旋转,例如在进行图像扫描时,需要运用旋转实现图像的倾斜校正;在进行多幅图像的比较、模式识别及对图像进行剪裁和拼接前,都需要进行图像的旋转处理。

图像旋转是指图像以某一点为中心旋转一定的角度,形成一幅新的图像的过程。图像旋转通常可以分为两种情况,一种是以坐标原点为中心进行旋转;另外一种是以任意图形中的某点为坐标原点进行旋转。图像的旋转变换是图像的位置变换,旋转后图像的大小一般会改变。在图像旋转变换中,既可以把转出显示区域的图像截去,也可以扩大图像范围以显示所有的图像。

1. 以坐标原点为中心进行旋转的原理

P0绕坐标原点逆时针旋转θ角度得到点P1,如图7-2所示。

图7-2

2. 以任意图形中的某点为坐标原点进行旋转的原理

以任意图形中的某点为坐标原点进行旋转的原理如图7-3所示。

图7-3

由图7-3可以看出,以任意图形中心点为坐标原点进行旋转需要如下3步:

   将坐标系Ⅰ变成坐标系Ⅱ。

由Figure1得到Figure2可知,变换矩阵为:

   在坐标系Ⅱ中旋转θ角度。参考以坐标原点为中心进行旋转的原理。

   将坐标系Ⅱ变成坐标系Ⅰ。

由图7-3中的Figure3得到Figure4可知,变换矩阵为(其实就是步骤01中变换矩阵的逆变换):

在OpenCV中,图像旋转首先根据旋转角度和旋转中心获取旋转矩阵,然后根据旋转矩阵进行变换,即可实现任意角度和任意中心的旋转效果。

下面我们实现图像旋转算法,有两种方式:一种是根据算法手动实现,另一种是根据OpenCV提供的现成函数自动实现。现在先来介绍根据算法手动实现的方式。

【例7.2】手动实现图像旋转

   打开Qt Creator,新建一个控制台工程,工程名是test。

   在工程中打开main.cpp,输入如下代码:

#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <string>
#include <cmath>

using namespace cv;
Mat imgRotate(Mat matSrc, float angle, bool direction)
{
    float theta = angle * CV_PI / 180.0;
    const int nRowsSrc = matSrc.rows;
    const int nColsSrc = matSrc.cols;
    // 如果是顺时针旋转
    if (!direction)
        theta = 2 * CV_PI - theta;
    // 全部以逆时针旋转来计算
    // 逆时针旋转矩阵
    float matRotate[3][3]{
        {std::cos(theta), -std::sin(theta), 0},
        {std::sin(theta), std::cos(theta), 0 },
        {0, 0, 1}
    };
    float pt[3][2]{		{ 0, (float)nRowsSrc},		{(float)nColsSrc, (float)nRowsSrc},		{(float)nColsSrc, 0}
    };
    for (int i = 0; i < 3; i++)
    {
        float x = pt[i][0] * matRotate[0][0] + pt[i][1] * matRotate[1][0];
        float y = pt[i][0] * matRotate[0][1] + pt[i][1] * matRotate[1][1];
        pt[i][0] = x;
        pt[i][1] = y;
    }
    // 计算出旋转后图像的极值点和尺寸
    float fMin_x = min(min(min(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);
    float fMin_y = min(min(min(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);
    float fMax_x = max(max(max(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);
    float fMax_y = max(max(max(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);
    int nRows = cvRound(fMax_y - fMin_y + 0.5) + 1;
    int nCols = cvRound(fMax_x - fMin_x + 0.5) + 1;
    int nMin_x = cvRound(fMin_x + 0.5);
    int nMin_y = cvRound(fMin_y + 0.5);
    // 拷贝输出图像
    Mat matRet(nRows, nCols, matSrc.type(), Scalar(0));
    for (int j = 0; j < nRows; j++)
    {
        for (int i = 0; i < nCols; i++)
        {
            // 计算出输出图像在原图像中对应点的坐标,然后复制该坐标的灰度值
            // 因为是逆时针转换,所以这里映射到原图像时可以看作输出图像顺时针旋转到原图像
            // 而顺时针旋转矩阵刚好是逆时针旋转矩阵的转置
            // 同时还要考虑把旋转后的图像的左上角移动到坐标原点
            int x = (i + nMin_x) * matRotate[0][0] + (j + nMin_y) * matRotate[0][1];
            int y = (i + nMin_x) * matRotate[1][0] + (j + nMin_y) * matRotate[1][1];
            if (x >= 0 && x < nColsSrc && y >= 0 && y < nRowsSrc)
            {
                matRet.at<Vec3b>(j, i) = matSrc.at<Vec3b>(y, x);
            }
        }
    }
    return matRet;
}
 int main()
 {
    Mat matSrc = imread("cat.png");
    if (matSrc.empty())
        return 1;
    float angle = 30;
    Mat matRet = imgRotate(matSrc, angle, true);
    imshow("src", matSrc);
    imshow("rotate", matRet);
    // 保存图像
    imwrite("rotate_panda.jpg", matRet);

    waitKey();
    return 0;
 }

以上代码完全是根据前面的算法原理公式来实现的。图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持原始尺寸。图像旋转后,图像的水平对称轴、垂直对称轴及中心坐标原点都可能发生变换,因此需要对图像旋转中的坐标进行相应转换。

   保存工程并运行,结果如图7-4所示。

如果不想通过算法公式手动实现图像旋转,也可以利用OpenCV提供的库函数getRotationMatrix2D来实现图像旋转。该函数用来计算旋转矩阵,其声明如下:

Mat getRotationMatrix2D(Point2f center, double angle, double scale);

其中参数center表示旋转的中心点;angle表示旋转的角度;scale表示图像缩放因子。该函数的返回值为一个2×3的矩阵,其中矩阵前两列代表旋转,最后一列代表平移。

​​​​​​​图7-4

计算出旋转矩阵后,还需要把旋转应用到仿射变换的输出,仿射变换函数是warpAffine,该函数在下一节介绍。

【例7.3】使用函数实现图像的旋转

   打开Qt Creator,新建一个控制台工程,工程名是test。

   在工程中打开main.cpp,输入如下代码:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;

// 图像旋转,angle表示要旋转的角度
void Rotate(const Mat &srcImage, Mat &destImage, double angle)
{
	Point2f center(srcImage.cols / 2, srcImage.rows / 2);// 中心
	Mat M = getRotationMatrix2D(center, angle, 1);// 计算旋转的仿射变换矩阵 
	// 现在把旋转应用到仿射变换的输出
	warpAffine(srcImage, destImage, M, Size(srcImage.cols, srcImage.rows));
	// 仿射变换  
	circle(destImage, center, 2, Scalar(255, 0, 0));
}

int main()
{
	// 读入图像,并判断图像是否读入正确
	cv::Mat srcImage = imread("lena.png");
	if (!srcImage.data)
	{
		puts("Fail to open file.");
		return -1;
	}
	imshow("srcImage", srcImage); // 原图像也绘制出来以作参照
	
	Mat destImage;
	double angle = 9.9;// 角度
	Rotate(srcImage, destImage, angle);
	// 最后把仿射变换和旋转的结果绘制在窗体中
	imshow("dst", destImage);
	waitKey(0);
	return 0;
}

保存工程并运行,结果如图7-5所示。

​​​​​​​图7-5


网站公告

今日签到

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