QT 6.6.0 基于OpenCV对图像进行旋转,缩放和裁剪等操作

目录

1.旋转操作(难点)

2.缩放和裁剪


GitHub源码icon-default.png?t=N7T8https://github.com/KeepTryingTo/QT_Learning/tree/main/QtOpenCV/QtOpenCV_Translation_Resize_Crop

QT初学案例教程
QT6.6.0实现打开电脑摄像头或其他的外接摄像头(实现拍照功能)
QT6.6.0实现简单的录音器
QT6.6.0实现一个简单的视频和音频播放器
QT6.6.0实现简单的视频录制(包含语音录制)器
QT6.6.0关于QMediaMetaData功能以及列子说明使用方法
QT6.6.0实现QtCamera功能(拍照,录像以及图像和视频的相关配置)
T6.6.0实现更好的视频播放器(前期改进)
QT6.6.0实现图表如饼图,柱状图,散点图以及盒图等的基本使用
QT6.6.0实现客户端Socket编程以及socket测试软件通信
QT6.6.0 实现服务端Server和多个客户端Client通信以及socket测试软件通信
QT6.6.0 实现QNetworkAccessManager的基本应用以及其拓展使用
QT6.6.0 实现服务端Server和多个客户端Client互相发送文件信息
QT6.6.0和QT6.0以下的版本实现FTP文件上传和下载(过程详解)
QT6.6.0实现QNetworkInterface网络接口,QHostAddress网络地址IP以及域名解析等相关信息查询。
QT6.6.0基于QTcpSocket和QTcpServer实现多个客户端群聊
QT6.6.0实现基于UDP协议的简单通信(过程详解)
QT 6.6.0基于UDP协议实现广播和多播机制(过程详解)
QT 6.6.0基于UDP协议实现群聊功能(过程详解)
QT 6.6.0中OpenCV两种环境的配置方法以及基本使用例子
QT 6.6.0 中基于OpenCV的图像变换以及基于鼠标点击移动事件的图像绘制

本文主要是基于前面两篇文章来的,建议读者先去看关于QT中OpenCV环境的配置以及基本使用博文,然后来看本篇博文。

参考文档
cv.warpAffine参考文档
QT参考文档

1.旋转操作(难点)

第一步:创建旋转矩阵:

cv::Mat RotatedImage = cv::getRotationMatrix2D(Center, Angle, 1.0);

- Center:指定旋转的中心点,即图像围绕这个中心点旋转(注意:在本文中每一次的旋转都是以原图的高宽为中心)。
- Angle:设定旋转的角度,单位是度,正值表示逆时针旋转。

 其中RotatedImage.shape:[2,3]  => [a, b, tx]  => [M11,M12,M13]
                                  [c, d, ty]     [M21,M22,M23]
a 和 b   :定义了旋转和可能的缩放(如果 scale 不为 1.0)在 x 轴方向上的影响。
c 和 d   :定义了旋转和可能的缩放(如果 scale 不为 1.0)在 y 轴方向上的影响。
[tx, ty] :是平移向量,用于将旋转后的图像中心移回原始的中心位置
         (或者,如果旋转中心不是图像中心,则将其移至指定的中心位置)。

 第二步:计算旋转矩形的最小包围矩形:旋转过程,最小包围矩形在不断的变化。也就是可以通过原图大小以及要旋转的角度得到最小包围矩形,如下图所示:

cv::Rect2f BoundingBox = cv::RotatedRect(cv::Point2f(), 
                                        this->Image.size(),
                                        Angle).boundingRect2f();

原点 cv::Point2f()、图像的大小this->Image.size()和旋转角度Angle。
boundingRect2f():此方法返回一个浮点数类型的矩形,作为包含旋转矩形的最小包围矩形,用于确定旋转后图像的新尺寸和位置。

第三步:调整旋转矩阵的平移部分:

RotatedImage.at<double>(0, 2) += BoundingBox.width / 2.0 - this -> Image.cols / 2.0;
RotatedImage.at<double>(1, 2) += BoundingBox.height / 2.0 - this -> Image.rows / 2.0;
如果不进行这个调整也是可以,只不过在旋转过程中看起来会有一点怪怪的。
如果使用了该调整之后,整个图像的旋转看起来就是围绕原图中心在旋转。

RotatedImage.at<double>(0, 2):访问旋转矩阵中平移x轴的值(第三列第一行)
RotatedImage.at<double>(1, 2):访问旋转矩阵中平移y轴的值(第三列第二行)

调整新包围矩形的宽度和高度的一半,减去原图像尺寸的一半,确保旋转后的图像真正居中。

也就是在旋转的过程中,最小包围矩形大小发生变化;如下图所示BoundingBox.width / 2.0 - this->Image.cols / 2.0表示最小包围矩形宽度和原图矩形宽度之差的二分之一大小,同理高度也是一样。同时RotatedImage.at<double>(0, 2)和RotatedImage.at<double>(1, 2)表示tx,ty,含义是“用于将旋转后的图像中心移回原始的中心位置”,因此,由于最下包围矩形的不断变换,需要对其tx和ty的值也需要根据最小包围矩形和原矩形之间的差距来重新来计算,让整个图像在旋转过程中看起来是按照指定原始图像中心来旋转的。

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

            = src(ax + cy + tx',bx + dy + ty')

            = src(ax + cy + tx + 最小包围矩形.width / 2 - 原图矩形.cols / 2,bx + dy + ty + 最小包围矩形.height / 2 - 原图矩形.rows / 2)

            = src(ax + cy + tx + 最小包围矩形和原图矩形宽度之差二分之一,bx + dy + ty + 最小包围矩形和原图矩形高度之差二分之一)

           = src(ax + cy + 实际的tx,bx + dy + 实际的ty)

tx' = tx + 最小包围矩形.width / 2 - 原图.cols / 2 = tx + BoundingBox.width / 2.0 - this->Image.cols / 2.0;
ty' = ty + 最小包围矩形.height / 2 - 原图.rows / 2 = ty + BoundingBox.height / 2.0 - this->Image.rows / 2.0;

 提示:旋转过程中仔细看以下语句输出的结果,直到旋转到90度,观察输出的数据[a,b,c,d,tx,ty]即可更加的明白【实际图像中心x和实际图像中心y】两个等式的含义。


qDebug()<<"before tx = "<<RotatedImage.at<double>(0,2)<<" ty = "<<RotatedImage.at<double>(1,2);
RotatedImage.at<double>(0,2) += BoundingBox.width / 2.0 - this -> Image.cols / 2.0;
RotatedImage.at<double>(1,2) += BoundingBox.height / 2.0 - this -> Image.rows / 2.0;

qDebug()<<"a = "<<RotatedImage.at<double>(0,0)<<" c = "<<RotatedImage.at<double>(1,0);
qDebug()<<"b = "<<RotatedImage.at<double>(0,1)<<" d = "<<RotatedImage.at<double>(1,1);
qDebug()<<"after tx = "<<RotatedImage.at<double>(0,2)<<" ty = "<<RotatedImage.at<double>(1,2);
qDebug()<<"BoundingBox.width / 2 = "<<BoundingBox.width / 2.0<<" BoundingBox.height / 2.0 = "<<BoundingBox.height / 2.0;

那么关于参数a,b,c,d怎么计算呢,看以下示例图,本文是以5度作为步长进行旋转,也就是0度,5度,10度,……,360度。

void cv::warpAffine(InputArray src, 
                    OutputArray dst, 
                    InputArray M, 
                    Size dsize, 
                    int flags=INTER_LINEAR, 
                    int borderMode=BORDER_CONSTANT, 
                    const Scalar& borderValue=Scalar())

src:         [输入图像]
dst:         [输出图像,与输入图像具有相同的数据类型,但其大小由 dsize 指定]
M: 2x3       [的变换矩阵]
dsize:       [输出图像的大小。当该参数为零时(Size(0,0)),
             函数会自动根据变换矩阵 M 和输入图像 src 的大小计算输出图像的大小]
flags:       [插值方法的标志。默认值为 INTER_LINEAR(双线性插值)]
borderMode:  [边界像素的外推方法。默认值为 BORDER_CONSTANT,
             表示使用恒定的颜色值进行填充,该颜色值由 borderValue 参数指定]
borderValue: [当 borderMode 为 BORDER_CONSTANT 时,这个值指定了用于填充边界的常量颜色值。
             默认值是 Scalar(),即黑色]

2.缩放和裁剪

Qt 6.6.0中基于OpenCV对图像缩放,裁剪,旋转等

OpenCV的拓展应用
OpenCV下的图形交互和媒体接口HighGUI
OpenCV中Mat类模板的一些基本操作(实例)
OpenCV下的Mat类模板基础(实例)
Opencv实现信用卡识别
Opencv实现停车位识别
Dlib+Opencv库实现疲劳检测
Dlib库实现人脸关键点检测(Opencv实现)
Opencv+YOLO-V3实现目标跟踪
YOLO-V3实时检测实现(opencv+python实现)——改进——>更加的易懂
YOLO-V3实时检测实现(opencv+python实现)
Opencv中使用Tracker实现物体跟踪
Opencv实现颜色检测
Opencv实现目标检测
Opencv实现对象跟踪
Opencv中的Canny边缘检测
Opencv级联分类器训练自己的.xml文件
visual studo 2022中使用Opencv人脸检测
Opencv进行人脸检测(第三版改进)
Opencv进行人脸检测(改进)
OpenCV进行人脸识别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值