实验原理
在OpenCV中,图像旋转也是一种常见的几何变换,它可以用来调整图像的方向。图像旋转通常涉及绕着图像中心点旋转一定角度的操作。与图像平移类似,旋转也可以通过仿射变换来实现,但是旋转需要使用到旋转矩阵来定义旋转的角度和旋转中心。
图像旋转的原理
图像旋转通常需要两步完成:
1. 构建旋转矩阵:
根据旋转中心点和旋转角度构建一个2x3的仿射变换矩阵。
为了旋转图像,你需要创建一个旋转矩阵,该矩阵描述了旋转的角度和旋转中心。在OpenCV中,旋转矩阵是一个2x3的矩阵,可以使用 cv::getRotationMatrix2D 函数来创建。该函数接收三个参数:旋转中心、旋转角度和缩放因子。
2. 应用旋转变换:
使用cv::warpAffine函数将这个矩阵应用于图像。这与平移操作非常相似,只是使用的变换矩阵不同。
函数原型
cv::getRotationMatrix2D 是 OpenCV 库中的一个函数,用于构建一个 2x3 的仿射变换矩阵,该矩阵可以用于绕给定点(通常是图像的中心点)旋转图像。此函数非常适合于实现图像的旋转操作。
cv::Mat getRotationMatrix2D(
Point2f center, // 旋转中心点
double angle, // 旋转角度,顺时针为负,逆时针为正
double scale // 缩放因子,默认为1(无缩放)
);
参数说明
center: cv::Point2f 类型,指定了旋转的中心点。通常设置为图像的中心点 (width / 2.0, height / 2.0)。
angle: double 类型,指定了旋转的角度,单位是度。正值表示逆时针旋转,负值表示顺时针旋转。
scale: double 类型,指定了旋转后的图像相对于原图像的缩放比例。默认值为 1.0,表示不进行缩放。
如果设置为小于 1 的值,则图像会缩小;如果设置为大于 1 的值,则图像会放大。
如何使用
使用 cv::getRotationMatrix2D 获取到旋转矩阵之后,你可以通过 cv::warpAffine 函数将该矩阵应用于图像,从而实现图像的旋转。
示例代码1
下面是一个详细的示例代码,展示了如何使用 cv::getRotationMatrix2D 和 cv::warpAffine 来旋转图像:
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
// 加载图像
cv::Mat img = cv::imread("D1.png");
if (img.empty())
{
std::cout << "Error: Image not found." << std::endl;
return -1;
}
// 获取图像的尺寸
int width = img.cols;
int height = img.rows;
// 定义旋转中心点(图像中心)
cv::Point2f center(width / 2.0, height / 2.0);
// 定义旋转角度和缩放因子
double angle = 60; // 旋转60度
double scale = 1.0; // 不缩放
// 创建旋转矩阵
cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, angle, scale);
// 计算旋转后的图像大小(可选,如果需要保持原图大小可以省略)
cv::Size dsize = cv::Size(img.cols, img.rows);
// 应用旋转变换
cv::Mat rotatedImg;
cv::warpAffine(img, rotatedImg, rotationMatrix, dsize);
// 显示原图和旋转后的图像
cv::namedWindow("Original Image", cv::WINDOW_NORMAL);
cv::imshow("Original Image", img);
cv::namedWindow("Rotated Image", cv::WINDOW_NORMAL);
cv::imshow("Rotated Image", rotatedImg);
cv::waitKey(0);
return 0;
}
在这个示例中,我们首先加载了一张图像,并计算了图像的中心点。接着,我们定义了旋转的角度(60度)和缩放因子(1.0)。使用 cv::getRotationMatrix2D 构建了旋转矩阵,并将其应用于图像,最终得到了旋转后的图像。
请注意,旋转可能会导致图像的一部分超出边界,此时可以调整 dsize 或者使用不同的 borderMode 来处理边界情况。
运行结果1
总结
图像旋转是通过仿射变换来实现的,它涉及到将图像绕着一个点旋转一定角度。在OpenCV中,可以通过cv::getRotationMatrix2D 构建一个2x3的仿射变换矩阵,并使用cv::warpAffine函数来实现图像的旋转。通过调整旋转角度和旋转中心点,可以控制图像的旋转效果。此外,还可以通过指定不同的边界处理方式来处理旋转后超出原图像范围的情况。
示例代码2
示例代码下面是一个使用OpenCV和C++实现图像旋转的示例代码:
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char** argv)
{
// 读取图像
cv::Mat src = cv::imread("D3.png", cv::IMREAD_COLOR);
if (src.empty())
{
std::cout << "Error opening image" << std::endl;
return -1;
}
// 创建输出图像
cv::Mat dst;
// 定义旋转参数
double angleInDegrees = 45; // 旋转角度(度)
cv::Point center(src.cols / 2.0, src.rows / 2.0); // 旋转中心点
// 计算旋转矩阵
double angleInRadians = angleInDegrees * (CV_PI / 180.0); // 将角度转换为弧度
double cosTheta = std::cos(angleInRadians);
double sinTheta = std::sin(angleInRadians);
cv::Mat rotationMatrix = (cv::Mat_<double>(2, 3) <<
cosTheta, -sinTheta, 0,
sinTheta, cosTheta, 0);
// 更新平移部分
rotationMatrix.at<double>(0, 2) = center.x - (center.x * cosTheta - center.y * sinTheta);
rotationMatrix.at<double>(1, 2) = center.y - (center.x * sinTheta + center.y * cosTheta);
// 应用仿射变换
cv::warpAffine(src, dst, rotationMatrix, src.size());
// 显示结果
cv::namedWindow("Original Image", cv::WINDOW_NORMAL);
cv::imshow("Original Image", src);
cv::namedWindow("Rotated Image", cv::WINDOW_NORMAL);
cv::imshow("Rotated Image", dst);
cv::waitKey(0);
return 0;
}
代码解释
1. 读取图像:使用cv::imread读取输入图像,并确保它是彩色图像。
2. 创建输出图像:创建一个新的cv::Mat对象来存储旋转后的结果。
3. 定义旋转参数:定义旋转的角度和旋转中心点。
4. 计算旋转矩阵:根据旋转角度和旋转中心点计算旋转矩阵。首先将角度转换为弧度,然后根据旋转矩阵的定义构建矩阵。
5. 更新平移部分:为了使图像绕着旋转中心点旋转,需要更新旋转矩阵中的平移部分。
6. 应用仿射变换:使用cv::warpAffine函数对图像进行旋转变换。
7. 显示结果:使用cv::imshow函数显示原始图像和旋转后的图像,并等待用户按键退出。
运行结果2
示例代码3
7.3函数实现图像旋转
// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
//#pragma comment(lib, "opencv_world450d.lib") //引用引入库
// 图像旋转
void Rotate(const Mat &srcImage, Mat &destImage, double angle)//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("02.jpeg");
if (!srcImage.data)
{
puts("打开图像文件失败");
return -1;
}
namedWindow("原图", WINDOW_NORMAL);
imshow("原图", srcImage);
//将图片按比例缩放至宽为250像素的大小
Mat destImage;
double angle = 9.9;//角度
Rotate(srcImage, destImage, angle);
namedWindow("旋转图", WINDOW_NORMAL);
imshow("旋转图", destImage);
waitKey(0);
return 0;
}
运行结果3
示例代码4
7.2手工实现旋转
// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#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;
//#pragma comment(lib, "opencv_world450d.lib") //引用引入库
Mat imgRotate(Mat matSrc, float angle, bool direction)
{
float theta = angle * CV_PI / 180.0;
int nRowsSrc = matSrc.rows;
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, nRowsSrc },
{nColsSrc, nRowsSrc},
{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("2.jpeg");
if (matSrc.empty())
return 1;
float angle = 30;
Mat matRet = imgRotate(matSrc, angle, true);
namedWindow("原图", WINDOW_NORMAL);
imshow("原图", matSrc);
namedWindow("旋转图", WINDOW_NORMAL);
imshow("旋转图", matRet);
// 保存图像
imwrite("rotate.jpg", matRet);
waitKey();
return 0;
}
手工实现旋转