[C++][opencv]基于opencv实现photoshop算法图像旋转

【测试环境】

vs2019

opencv==4.8.0

【效果演示】

【核心实现代码】

//图像旋转: src为原图像, dst为新图像, angle为旋转角度, isClip表示是采取缩小图片的方式
int imageRotate4(InputArray src, OutputArray dst, double angle, bool isClip)
{Mat input = src.getMat();if (input.empty()) {return -1;}//得到图像大小int width = input.cols;int height = input.rows;//计算图像中心点Point2f center;center.x = width / 2.0;center.y = height / 2.0;//获得旋转变换矩阵double scale = 1.0;Mat trans_mat = getRotationMatrix2D(center, -angle, scale);//计算新图像大小double angle1 = angle * CV_PI / 180.;double a = sin(angle1) * scale;double b = cos(angle1) * scale;double out_width = height * fabs(a) + width * fabs(b); //外边框长度double out_height = width * fabs(a) + height * fabs(b);//外边框高度int new_width, new_height;if (!isClip) {new_width = cvRound(out_width);new_height = cvRound(out_height);}else {//calculate width and height of clip rectdouble angle2 = fabs(atan(height * 1.0 / width)); //即角度 bdouble len = width * fabs(b);double Y = len / (1 / fabs(tan(angle1)) + 1 / fabs(tan(angle2)));double X = Y * 1 / fabs(tan(angle2));new_width = cvRound(out_width - X * 2);new_height = cvRound(out_height - Y * 2);}//在旋转变换矩阵中加入平移量trans_mat.at<double>(0, 2) += cvRound((new_width - width) / 2);trans_mat.at<double>(1, 2) += cvRound((new_height - height) / 2);//仿射变换warpAffine(input, dst, trans_mat, Size(new_width, new_height));return 0;
}/*** 检测图像倾斜度* 返回值:返回0表示无检测结果,返回非0表示摆正图象需要旋转的角度(-10至10度)*/
double detectRotation(InputArray src)
{double max_angle = 6; //可旋转的最大角度Mat in = src.getMat();if (in.empty()) return 0;Mat input;//转为灰度图if (in.type() == CV_8UC1)input = in;else if (in.type() == CV_8UC3)cvtColor(in, input, cv::COLOR_BGR2GRAY);else if (in.type() == CV_8UC3)cvtColor(in, input, cv::COLOR_BGRA2GRAY);elsereturn 0;Mat dst, cdst;//执行Canny边缘检测(检测结果为dst, 为黑白图)double threshold1 = 90;Canny(src, dst, threshold1, threshold1 * 3, 3);//将Canny边缘检测结果转化为灰度图像(cdst)cvtColor(dst, cdst, cv::COLOR_GRAY2BGR);//执行霍夫线变换,检测直线vector<Vec4i> lines; //存放检测结果的vectordouble minLineLength = std::min(dst.cols, dst.rows) * 0.25; //最短线长度double maxLineGap = std::min(dst.cols, dst.rows) * 0.03; //最小线间距int threshold = 90;HoughLinesP(dst, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap);//分析所需变量int x1, y1, x2, y2; //直线的两个端点int x, y;  //直线的中点double angle, rotate_angle; //直线的角度,摆正直线需要旋转的角度double line_length; //直线长度double position_weighted; //直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小double main_lens[2]; //用于存放最长的二条直线长度的数组 (这两条直线即是主线条)double main_angles[2];//用于存放最长的二条直线的摆正需要旋转的角度main_lens[0] = main_lens[1] = 0;main_angles[0] = main_angles[1] = 0;//逐个分析各条直线,判断哪个是主线条for (size_t i = 0; i < lines.size(); i++) {//取得直线的两个端点座标x1 = lines[i][0]; y1 = lines[i][1]; x2 = lines[i][2]; y2 = lines[i][3];x = (x1 + x2) / 2; y = (y1 + y2) / 2;//计算直线的角度angle = (x1 == x2) ? 90 : (atan((y1 - y2) * 1.0 / (x2 - x1))) / CV_PI * 180;//摆正直线需要旋转的角度. 如果超出可旋转的最大角度,则忽略这个线。if (fabs(angle - 0) <= max_angle) {rotate_angle = angle - 0;}else if (fabs(angle - 90) <= max_angle) {rotate_angle = angle - 90;}else {continue;}//计算线的长度line_length = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));//计算直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小position_weighted = 1;if (x < dst.cols / 4 || x > dst.cols * 3 / 4) position_weighted *= 0.8;if (x < dst.cols / 6 || x > dst.cols * 5 / 6) position_weighted *= 0.5;if (x < dst.cols / 8 || x > dst.cols * 7 / 8) position_weighted *= 0.5;if (y < dst.rows / 4 || y > dst.rows * 3 / 4) position_weighted *= 0.8;if (y < dst.rows / 6 || y > dst.rows * 5 / 6) position_weighted *= 0.5;if (y < dst.rows / 8 || y > dst.rows * 7 / 8) position_weighted *= 0.5;//如果 直线长度 * 位置权重 < 最小长度, 则这条线无效line_length = line_length * position_weighted;if (line_length < minLineLength) continue;//如果长度为前两名,则存入数据if (line_length > main_lens[1]) {if (line_length > main_lens[0]) {main_lens[1] = main_lens[0];main_lens[0] = line_length;main_angles[1] = main_angles[0];main_angles[0] = rotate_angle;//如果定义了 SHOW_LINE, 则将该线条画出来
#ifdef SHOW_LINEline(cdst, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255), 3, 8);
#endif}else {main_lens[1] = line_length;main_angles[1] = rotate_angle;}}}//如果定义了 SHOW_LINE, 则在source_window中显示cdst
#ifdef SHOW_LINEimshow(source_window, cdst);
#endif//最后,分析最长的二条直线,得出结果if (main_lens[0] > 0) {//如果最长的线 与 次长的线 两者长度相近,则返回两者需要旋转的角度的平均值if (main_lens[1] > 0 && (main_lens[0] - main_lens[1] / main_lens[0] < 0.2)) {return (main_angles[0] + main_angles[1]) / 2;}else {return main_angles[0];   //否则,返回最长的线需要旋转的角度}}else {return 0;}
}

 【完整演示代码下载】

https://download.csdn.net/download/FL1623863129/89633167

【参考文献】

1 https://blog.csdn.net/c80486/article/details/51867128

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397983.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【c++】类和对象 (中) (类的默认成员函数)

类的默认成员函数 在C中&#xff0c;如果你定义了一个类但没有显式地提供特定的成员函数&#xff08;比如构造函数、析构函数、拷贝构造函数、拷贝赋值运算符等&#xff09;&#xff0c;编译器会为这些函数生成默认的实现。这些默认生成的成员函数称为类的默认成员函数。那么既…

C#学习笔记15:上位机助手_usercontrol窗体内嵌的应用

今日完善一下之前的上位机助手&#xff0c;做一个组合窗体内嵌的多功能助手软件应用, 与之前的上位机软件相比: 更注重控件能够随着窗体缩放而缩放变换&#xff0c;串口助手部分能自动后台检测串口设备&#xff0c;解决市面上大部分串口助手的打开初始化会卡顿的问题 ( 多线程后…

Linux服务管理-Nginx配置

静态解析主要解析html、css动态解析需要解析php 动态资源通过轮询分配到后端的Apache服务器处理 apache是同步阻塞&#xff0c;nginx是异步非阻塞

论文阅读笔记:Efficient Teacher: Semi-Supervised Object Detection for YOLOv5

Efficient Teacher: Semi-Supervised Object Detection for YOLOv5 1 背景1.1 动机1.2 问题 2 创新点3 方法4 模块4.1 伪标签分配4.2 Epoch Adapter 5 效果5.1 与SOTA方法对比5.2 消融实验 论文&#xff1a;https://arxiv.org/pdf/2302.07577v3.pdf 代码&#xff1a;https://g…

Python 常用内置函数

目录 1、enumerate函数 1.1、for循环中使用 1.2、enumerate指定索引的起始值 1.3、enumerate在线程中的作用 2、Map 函数 2.1、map()函数可以传多个迭代器对象 3、lambda表达式&#xff08;匿名函数&#xff09; 示例 4、sort函数和sorted函数 4.1、sort()函数 4.2、…

map和set的使用

关联式容器 在学习关联式容器之前&#xff0c;我们学习过的容器有vector、list、deque…这些容器称为序列式容器&#xff0c;单纯的存储数据存储的数据没有关联性。 即将学习的map 和set属于关联式容器&#xff0c;其里面存储的是<key, value>结构的键值对&#xff0c;…

制造知识普及(九)--企业内部物料编码(IPN)与制造商物料编码(MPN)

在日常的物料管理业务逻辑中&#xff0c;一物一码是物料管理的基本的业务规则&#xff0c;不管物料从产品开发还是仓库管理&#xff0c;甚至成本核算&#xff0c;都要遵循这个原则&#xff0c;才能保证产品数据的准确性&#xff0c;才具备唯一追溯的可行性。大部分企业都是这种…

某通电子文档安全管理系统 CDGAuthoriseTempletService1接口SQL注入漏洞复现 [附POC]

文章目录 某通电子文档安全管理系统 CDGAuthoriseTempletService1接口SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现0x06 修复建议某通电子文档安全管理系统 CDGAuthoriseTempletService1接口SQL注入漏…

C#数据类型转换

代码&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Test05 {class Program{static void Main(string[] args){double db 2008;//声明一个double类型变量db&#xff0c;并初始化为2008object obj db;//对db…

JAVA实现判断小程序用户是否关注公众号

本文主要描述了判断小程序用户是否关注公众号的逻辑实现及部分代码 首先阐述一下大致流程&#xff1a; 1、在将小程序和公众号绑定至同一个微信开发平台下&#xff1b; 2、后端拉取公众号已关注用户列表&#xff0c;并获取其中每一个用户的unionID&#xff0c; 建立已关注用户…

OCR调研

OCR调研 一、介绍 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;是一种将图像中的文字转换为计算机可处理格式的技术。OCR技术经历了从传统OCR到基于深度学习的OCR的转变。深度学习OCR技术通过模拟人脑神经元结构处理文本和图像数据&am…

MATLAB - 强化学习(Reinforcement Learning)

系列文章目录 前言 一、什么是强化学习&#xff1f; 强化学习是一种以目标为导向的计算方法&#xff0c;计算机通过与未知的动态环境交互来学习执行任务。这种学习方法能让计算机在没有人工干预和明确编程的情况下&#xff0c;做出一系列决策&#xff0c;使任务的累积奖励最大化…

cmake 编译教程

参考链接&#xff1a;cmake使用详细教程&#xff08;日常使用这一篇就足够了&#xff09;_cmake教程-CSDN博客 一、只有一个源文件的程序编译 首先在当前目录下创建两个文件 hello.cpp CMakeLists.txt &#xff08;注意CMakeLists大小写&#xff0c;不要写错了&#xff09; …

推荐一个优秀的 .NET MAUI 组件库

目录 前言 组件介绍 组件展示 布局 按钮 复选框 进度条 导航栏 组件地址 最后 前言 .NET MAUI 的发布&#xff0c;项目中可以使用这个新的跨平台 UI 框架来轻松搭建的移动和桌面应用。 为了帮助大家更快地构建美观且功能丰富的应用&#xff0c;本文将推荐一款优秀…

AcCode核心思路

文章目录 在线OJ项目核心思路1. 项目介绍2.预备知识理解多进程编程为啥采用多进程而不使用多线程?标准输入&标准输出&标准错误 3.项目实现题目API实现相关实体类定义新增/修改题目获取题目列表 编译运行编译运行流程 4.统一功能处理 在线OJ项目核心思路 1. 项目介绍 …

有序转化数组(LeetCode)

题目 给你一个已经 排好序 的整数数组 和整数 、 、 。对于数组中的每一个元素 &#xff0c;计算函数值 &#xff0c;请 按升序返回数组 。 解题 在时间复杂度为解决问题 def sortTransformedArray(nums, a, b, c):def f(x):return a * x * x b * x cn len(nums)result…

4个从阿里毕业的P7打工人,当起了包子铺的老板

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&chksmc0e47813f793f105017fb8551c9b996dc7782987e19efb166ab665f44ca6d900210e6c4c0281&scene21#wechat_redirect 《网安面试指南》h…

学生公寓电费信息管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;公寓管理员管理&#xff0c;学生管理&#xff0c;楼层信息管理&#xff0c;用电情况管理&#xff0c;缴费清单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;用电情况…

【数据结构】六、图:4.图的遍历(深度优先算法DFS、广度优先算法BFS)

三、基本操作 文章目录 三、基本操作1.图的遍历1.1 深度优先遍历DFS1.1.1 DFS算法1.1.2 DFS算法的性能分析1.1.3 深度优先的生成树和生成森林 1.2 广度优先遍历BFS1.2.1 BFS算法1.2.2 BFS算法性能分析1.2.3 广度优先的生成树和生成森林 1.3 图的遍历与图的连通性 1.图的遍历 图…

Nginx系列-Nginx Location匹配规则

文章目录 Nginx系列-Nginx Location匹配规则1. 语法基础2. 匹配规则2.1 精确匹配&#xff08;&#xff09;2.2. 最长前缀匹配&#xff08;^~&#xff09;2.3. 正则表达式匹配&#xff08;~和~*&#xff09;2.4. 普通前缀匹配&#xff08;无修饰符&#xff09;2.5. 默认匹配&…