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

news2024/12/23 20:36:27

【测试环境】

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 rect
		double angle2 = fabs(atan(height * 1.0 / width)); //即角度 b
		double 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);
	else
		return 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; //存放检测结果的vector
	double 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_LINE
				line(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_LINE
	imshow(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.coloradmin.cn/o/2047922.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型应用开发基础知识

一、LangChain介绍 背景 一个应用程序通常需要多次对大语言模型写提示并对它输出的结果进行解析。因此&#xff0c;需要写很多胶水代码。而LangChain的目的使这个开发过程变得更容易。目的 LangChain是一个构建大语言模型应用的开源框架。当有人在开发LLM的复杂应用&#xff0…

ModuleNotFoundError: No module named ‘transformers_modules.chatglm-6b-v1‘

ModuleNotFoundError: No module named transformers_modules.chatglm-6b-v1 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;…

基于Java语言的充电桩平台 包含(充电桩系统 汽车充电桩云快充协议 电动自行车充电桩协议 中电联充电桩互联互通协议)

介绍 SpringBoot 框架&#xff0c;充电桩平台充电桩系统充电平台充电桩互联互通协议云快充协议1.5-1.6协议新能源汽车二轮车公交车二轮车充电-四轮车充电充电源代码充电平台源码Java源码 软件架构 软件架构说明 软件功能 小程序端&#xff1a;城市切换、附近电站、电桩详情…

【RAG实战】基于TextIn打造上市公司财务报表智能问答系统

今天介绍一个项目案例,利用大语言模型打造上市公司财务报表智能问答系统。 在当今竞争激烈的市场环境中,企业和投资者对财务信息的获取与分析要求越来越高。上市公司财务报表作为评估公司财务健康和未来发展的重要依据,提供了大量关键信息。 然而,传统的财务报表分析技术…

尚品汇-前端调用搜索实现(三十二)

目录&#xff1a; &#xff08;1&#xff09;修改web-all模块 &#xff08;2&#xff09;配置网关 &#xff08;3&#xff09;页面渲染 &#xff08;4&#xff09;面包屑处理 &#xff08;1&#xff09;修改web-all模块 修改pom.xml文件 <dependencies><depende…

QT串口通信

查看详情http://100bcw.com/qt6.htm 前言&#xff1a;如果用qt写程序作为上位机&#xff0c;然后通过和usb和下位机通信的时候&#xff0c;就需要用到qt中的串口通信了。 使用qt中的串口通信的时候需要用到的两个头文件分别为&#xff1a; #include <QtSerialPort/QSeria…

MYSQL----表的创建

1.创建表 create table 表名&#xff08; field1 datetype, field2 datetype, field3 datetype &#xff09; 1.field字段名&#xff0c;也就是属性&#xff0c;相当于java类里面的成员属性 2.datetype 数据类型 3.最后一个字段的定义&#xff0c;结束没有逗号 4.字段的定义在…

政策驱动,科技引领,漫途信息化监测方案守护农村饮水安全!

近日&#xff0c;山西省人民政府正式发布《山西省农村供水高质量发展规划》&#xff0c;明确到2030年&#xff0c;全面实现农村24小时供水&#xff0c;县域农村饮水安全标准化建设达标率力争达到80%&#xff0c;基本实现农村供水城市化、城乡供水均等化。 现阶段部分地区受…

基于VS2022+Qt5+C++的串口助手开发

目录 一、前言 二、环境准备 三、创建QT串口项目 ​编辑 四、串口项目实现 1.ui界面设计 2.添加QT串口模块 3.功能实现 ①串口扫描 ②波特率、停止位等设置 ③接收数据 ④发送数据 五、最终效果 六、总结 一、前言 如果有人之前看过我文章的话应该知道&#xf…

C# 窗体通过调用webapi往U9C系统创建请购单

C# 窗体通过调用webapi往U9C系统创建请购单 窗体图片 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System…

Java面试题———分布式篇

目录 1、什么是分布式事务 2、什么是CAP理论 3、为什么分布式系统中无法同时AC 4、什么是BASE理论 5、分布式事务的解决方案有哪些 6、Seata的架构是什么 7、XA模式的工作流程是什么 8、AT模型的工作原理是什么 9、TCC模型的工作原理是什么 1、什么是分布式事务 在分…

Aix 清理 iscsi 残留盘

目录 一、前提概要 二、解决方法 一、前提概要 Aix 环境下挂载 ISCSI 盘&#xff0c;需要结合 LVM 进行挂盘。这个过程比较复杂&#xff0c;在使用过程中极易出现残留。 本篇博客介绍的就是如何清理 Aix 下 ISCSI 的残留。 二、解决方法 主要步骤和需要执行的命令如下面的流…

ModuleNotFoundError: No module named ‘pywin32_bootstrap

ModuleNotFoundError: No module named ‘pywin32_bootstrap 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发者社…

Prompt Engineering 完整指南

Prompt Engineering 完整指南 Prompt Engineering 是与 AI 模型进行交互的关键技术&#xff0c;通过精心设计的指令&#xff08;prompt&#xff09;引导模型生成高质量的输出。本文将详细介绍 prompt 设计的最佳实践、基本原则以及高级技巧&#xff0c;帮助你在实际应用中最大化…

vscode添加宏定义

1 起因 在用vscode看项目代码时&#xff0c;如果源文件中的代码块被某个宏定义给包裹住了&#xff0c;则在vscode的默认配置下&#xff0c;不会高亮显示这块被包裹住的代码&#xff0c;如下图中229行开始的代码被STM32F40_41xxx所控制&#xff0c;没有高亮显示。 由于STM32F4…

SSH升级至9.8p1

此前写过一个有关升级ssh的帖子&#xff0c;当时的情况是ssh5.3p1 升级到 ssh8.0 p1 下面是链接&#xff1a;https://blog.csdn.net/zhurobert/article/details/103193205?spm1001.2014.3001.5501 此次升级的环境是CentOS-7.6.1810 ssh版本7.4p1 准备好升级包后上传至/home…

Ubuntu 下 NFS服务开启

NFS&#xff08;Network File System&#xff0c;网络文件系统&#xff09;是一种基于TCP/IP传输的网络文件系统协议&#xff0c;它允许网络中的计算机之间通过TCP/IP网络共享资源。简单来说NFS就是一个文件传输的协议&#xff0c;可以用来传输文件。 使用如下命令安装 NFS 服务…

怎样选择开放式耳机好?精选五大实力出众爆款安利!

开放式耳机通过空气传导技术传递声音&#xff0c;不需要插入耳道&#xff0c;这种设计不仅保护了耳朵&#xff0c;还确保了佩戴的稳定性和舒适性。与传统的入耳式耳机相比&#xff0c;开放式耳机有助于保持耳道清洁&#xff0c;并允许用户在享受音乐的同时保持对周围环境的警觉…

Ps:首选项 - 增效工具

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“增效工具” Plugins选项卡允许用户管理 Photoshop 中的插件和扩展功能&#xff0c;通过调整这些设置&#xff0c;用户可以根据自己的需求来扩展 Photoshop 的功能&#xff0c…

前端post传入拿到数据,后端报null,并且能够添加或者编辑成功

检查conterller层注解接到实体类的注解是不是没加&#xff08; RequestBody &#xff09; 后端&#xff1a; 前端&#xff1a; 那么就看注解&#xff0c;因为contrller层有个接值注解&#xff08; RequestBody &#xff09;