opencv校正图像
目录
- 1、前言
- 2、例程
- 2.1、代码
- 2.2、效果
- 口罩
- 说明书
- 网页
- 3、按步骤分析
- 转灰度图
- 降噪 + Canny边缘检测
- 膨胀(可视具体情况省略)
- 轮廓检索
- 选取角度
1、前言
我们用相机拍照时,会因为角度问题造成拍歪,会影响图像的识别,这时就需要对图像进行校正,下面介绍校正图像的一种方式,可以用来校正简单的图像,如文字信息、工件等。
校正的过程可以分为以下几步:
1、转灰度图。
2、降噪。
3、Canny边缘检测。
4、膨胀。
5、轮廓检索。
6、从各个轮廓中选取合适的旋转角度并校正图像。
总体的思路是获取图像中各个特征的轮廓旋转角度,从中选取合适的角度让原图像进行逆旋转,达到校准目的。
方法参考:https://blog.csdn.net/DU_YULIN/article/details/120504660
2、例程
2.1、代码
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {Mat src = imread("./test5.jpg");imshow("src", src);/* 转灰度图 */Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);imshow("gray", gray);/* 高斯模糊降噪,避免环境中的花纹影响边缘检测 */Mat blur;GaussianBlur(gray, blur, Size(5, 5), 1.0);imshow("gaussianBlur", blur);/* Canny边缘检测 */Mat canny;Canny(blur, canny, 20, 100);imshow("canny", canny);/* 膨胀两次,膨胀是为了让文字连到一块,轮廓数,提高效率,可以按需求调整膨胀的大小 */Mat kernel = getStructuringElement(MORPH_RECT, Size(4, 2));Mat expand;dilate(canny, expand, kernel, Point(-1, -1), 2);imshow("dialate", expand);/* 检索轮廓 */vector<vector<Point>> contours;findContours(expand, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);/* 对各个轮廓的旋转角度进行排序 */std::vector<float> vecAngles;for (int i = 0; i < contours.size(); i++) {RotatedRect rr = minAreaRect(contours[i]);vecAngles.push_back(rr.angle);}std::sort(vecAngles.begin(), vecAngles.end());/* 以中间值为基准,取相差20%以内的角度的平均值作为结果 */float midIndex = int(vecAngles.size() / 2) - 1;float midAngle = vecAngles[midIndex];float maxAngleThreshold = midAngle > 0 ? midAngle - 15 : midAngle + 15;float minAngleThreshold = midAngle > 0 ? midAngle + 15 : midAngle - 15;float angleSum = 0;int angleCounter = 0;cout << "maxAngleThreshold:" << maxAngleThreshold << endl;cout << "minAngleThreshold:" << minAngleThreshold << endl;for (auto angle : vecAngles) {cout << angle << endl;if (angle > minAngleThreshold && angle < maxAngleThreshold) {angleSum += angle;angleCounter++;}}float averageAngle = angleSum / angleCounter;cout << "averageAngle:" << averageAngle << endl;cout << "midAngle:" << midAngle << endl;/* 旋转图像 */Mat result;Mat rotateM = getRotationMatrix2D(Point2f(gray.cols / 2.0, gray.rows / 2.0), averageAngle, 1.0);warpAffine(src, result, rotateM, gray.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(255, 255, 255));imshow("result", result);waitKey(0);
}
2.2、效果
口罩
说明书
网页
3、按步骤分析
转灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("gray", gray);
我们平时看的图片都是由RGB来描述颜色的,RGB有三个值,而灰度图只有一个灰度值,转换为灰度图可以减少计算量。
降噪 + Canny边缘检测
/* 高斯模糊降噪,避免环境中的花纹影响边缘检测 */
Mat blur;
GaussianBlur(gray, blur, Size(5, 5), 1.0);
imshow("gaussianBlur", blur);/* Canny边缘检测 */
Mat canny;
Canny(blur, canny, 20, 100);
imshow("canny", canny);
降噪是为Canny边缘检测做准备,相机拍出来的照片会有很多多余的特征,这些会影响到边缘检测的结果,通过降噪可以把不明显的特征去掉。
比如这张图片,我们需要校正的只有中间的文字部分。
如果不进行降噪,Canny边缘检测的结果会是这样,存在多余的特征,可能会影响到最后的结果。
降噪后把最明显特征留了下来,提高准确度。
膨胀(可视具体情况省略)
/* 膨胀两次,膨胀是为了让文字连到一块,轮廓数,提高效率,可以按需求调整膨胀的大小 */
Mat kernel = getStructuringElement(MORPH_RECT, Size(4, 2));
Mat expand;
dilate(canny, expand, kernel, Point(-1, -1), 2);
imshow("dialate", expand);
如上图所示,Canny算法查找到了很多组轮廓,但有时候我们其实不需要太多细节上的轮廓,只需要一个能描述整体的轮廓,这时候用膨胀就可以把这些细节的轮廓组合到一起,这样做的好处是可以减少计算量,而且整体的轮廓比细节轮廓更有代表性。
轮廓检索
/* 检索轮廓 */
vector<vector<Point>> contours;
findContours(expand, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
opencv提供了findContours可以获取图像中的轮廓位置及其旋转角度。
选取角度
/* 对各个轮廓的旋转角度进行排序 */
vector<float> vecAngles;
for (int i = 0; i < contours.size(); i++) {RotatedRect r = minAreaRect(contours[i]);vecAngles.push_back(r.angle);
}
sort(vecAngles.begin(), vecAngles.end());/* 以中间值为基准,取相差20%以内的角度的平均值作为结果 */
float midIndex = int(vecAngles.size() / 2) - 1;
float midAngle = vecAngles[midIndex];
float maxAngleThreshold = midAngle > 0 ? midAngle - 15 : midAngle + 15;
float minAngleThreshold = midAngle > 0 ? midAngle + 15 : midAngle - 15;
float angleSum = 0;
int angleCounter = 0;
for (auto angle : vecAngles) {if (angle > minAngleThreshold && angle < maxAngleThreshold) {angleSum += angle;angleCounter++;}
}
float averageAngle = angleSum / angleCounter;
因为我们要做的是图像整体的校准,所以先排序,取中间值,避开一些过大或过小的角度。
直接使用中间值会存在一些特殊情况,比如角度序列:0、31、31、36、90、90。中间值的选取,取决于过大、或过小角度的数量,从序列选中可以看到,偏移角度显然是倾向于31方向的,而结果确是36,所以这里加了一个取平均值的操作:取中间值前后15度的所有角度作为有效角度,通过有效角度的平均值来确定最终的校准结果。
相关文章:

opencv校正图像
目录1、前言2、例程2.1、代码2.2、效果口罩说明书网页3、按步骤分析转灰度图降噪 Canny边缘检测膨胀(可视具体情况省略)轮廓检索选取角度1、前言 我们用相机拍照时,会因为角度问题造成拍歪,会影响图像的识别,这时就需…...
JavaScript:函数与箭头函数的区别
ref 1. 定义 函数 function getName() {}箭头函数 const getName () > {}2. 命名 函数分为匿名、具名。 function getName() {} let getName function () {}箭头函数只有匿名。 const getName () > {}3. 构造函数 箭头函数都是匿名函数,所以不能作为构造…...
八股文(四)
目录 一、 Vue2的双向数据绑定原理 二、 vue2数据绑定缺点是什么?vue3是怎么解决的? (1)因为vue2.0 object.defineProperty只能劫持对象属性 (2)Proxy是直接代理对象 (3)proxy不…...

XSS挑战赛(xsslabs)1~10关通关解析
简介 XSS挑战赛,里面包含了各种XSS的防御方式和绕过方式,好好掌握里面的绕过细节,有助于我们更好的去发现XSS漏洞以及XSS的防御。本文更多的是分享解析的细节,不是一个标准的答案,希望大家在渗透的时候有更多的思维。…...

什么是以太网供电POE
POE指的是以太网供电,就是一根网线在传输网络的同时还传输设备所需的电源。我们最常见的就是通过POE交换机连接网络摄像头,网络摄像头无需的电源适配器,仅靠一根网线就能实现电源和网络的传输。POE供电一般可以到100米。POE包含两个部分&…...

【JUC2022】第七章 AQS、ReentrantReadWriteLock 和 StampedLock
【JUC2022】第七章 AQS 文章目录【JUC2022】第七章 AQS一、AQS1.概述2.同步器3.抽象的4.队列式二、ReentrantReadWriteLock1.概述2.案例3.存在的问题三、StampedLock1.概述2.案例3.存在的问题一、AQS 1.概述 AQS(AbstractQueueSynchronizer,抽象的队列式同步器)&am…...

Spark 磁盘作用
Spark 磁盘作用磁盘作用性能价值失败重试ReuseExchangeSpark 导航 磁盘作用 临时文件、中间文件、缓存数据,都会存储到 spark.local.dir 中 在 Shuffle Map 时, 当内存空间不足,就会溢出临时文件存储到磁盘上溢出的临时文件一起做归并计算…...

三、Spark 内存管理
文章目录Spark 内存管理堆内和堆外内存堆内内存堆外内存堆外与堆内的平衡内存空间分配静态内存管理(早期版本)统一内存管理Spark 内存管理 堆内和堆外内存 Spark 引入了堆外(Off-heap)内存,使之可以直接在工作节点的…...

Java 面试常见项目问题回答
之前整理了好几期,我面试时遇到的面试候选人,我是如何我去筛选的,这一期,我们来看下一些 面试常问的业务性的问题 你们公司权限认证是如何实现的? 这其实是个通用性的问题,大部分公司 小型公司,或者中型公…...

文件上传和下载(原生JS + SpringBoot实现)
目录 概述 前端编写-上传表单和图片回显 HTML表单代码 发送请求逻辑 CSS代码 后端编写-文件上传接口 后端编写-文件下载接口 概述 在现代Web应用程序中,文件上传和下载是常见的功能。本博客将介绍如何使用原生JS和Spring Boot实现文件上传和下载的功能。 在其…...
【C语言学习笔记】:安全性
用const修饰变量或方法,从而告诉编译器这些都是不可变的,有助于编译器优化代码,并帮助开发人员了解函数是否有副作用。此外,使用const &可以防止编译器复制不必要的数据。John Carmack对const的评论[2]值得一读。 // Bad Ide…...

Linux - 磁盘存储管理 磁盘引入
# 我们要介绍下 磁盘管理, 那不妨先来看一张图来简单 引入 :这张图呢,是我们 Windows 上的磁盘管理的显示 。根据这幅图呢,提出一个问题 :>>> 这幅图磁盘管理所显示的内容,你能判断出 该电脑 有几…...
分割std::string成多个string
文章目录问题描述前置知识解决代码问题描述 假设我们有一个http服务器,此服务器接收客户端发来的http请求,假设请求如下 GET / HTTP/1.1我们怎么将这个Http请求分割成三份,分别存入不同的string中分别处理? 前置知识 首先std::string的本…...
3月多国更新进出口产品规定
【3月多国更新进出口产品规定】2023年3月多项外贸新规实施,涉及欧盟,伊拉克,泰国,孟加拉国,埃及等多国进出口产品限制及海关税则。1. 3月1日起给予埃塞俄比亚等三国98%税目产品零关税待遇中国国务院关税税则委员会17日…...
nacos相关面试题
Nacos是阿里巴巴开源的一款注册中心和配置中心,它能够实现服务的注册、发现和配置管理等功能。Nacos的实现原理主要分为以下几个部分:注册中心:Nacos作为注册中心,通过提供RESTful API的方式对外提供注册和发现服务。它使用基于Ra…...

Linux基础命令-groupmems管理组群的成员
Linux-usermod修改用户 Linux-useradd创建用户 Linux-userdel删除用户 Linux基础命令-chown修改文件属主 Linux基础命令-chmod修改文件权限 groupmems 命令介绍 先来看看这个命令的帮助信息是什么概念 NAME groupmems - administer members of a user’s primary group group…...

css系统化学习
元素的语义化 SEO:搜索引擎优化 根据搜索引擎展示的规律,语义化的元素更容易被展示获得更多浏览量 字符编码 css历史 内联样式(inline) style"内容全写在等号后面,双引号里面,多个之间用;隔开" 内部样式(internal) style写在head里面,在title下面,不是在body内, …...

AI的简单介绍
什么是AI? AI 是 Artificial Intelligent 的缩写,是我们通常意义上说的人工智能。 简单来说就是让机器能够模拟人类的思维能力,让它能够像人一样感知、思考甚至决策。 为什么要开发AI? 因为在过去,都是我们学习机器…...

【Linux】-- 进程间通讯
目录 进程间通讯概念的引入 意义(手段) 思维构建 进程间通信方式 管道 站在用户角度-浅度理解管道 匿名管道 pipe函数 站在文件描述符角度-深度理解管道 管道的特点总结 管道的拓展 单机版的负载均衡 匿名管道读写规则 命名管道 前言 原理…...

STM32模拟SPI时序控制双路16位数模转换(16bit DAC)芯片DAC8552电压输出
STM32模拟SPI时序控制双路16位数模转换(16bit DAC)芯片DAC8552电压输出 STM32部分芯片具有12位DAC输出能力,要实现16位及以上DAC输出需要外挂DAC转换ASIC。 DAC8552是双路16位DAC输出芯片,通过SPI三线总线进行配置控制输出。这里…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...