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三线总线进行配置控制输出。这里…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...

【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...