当前位置: 首页 > article >正文

OpenCV连续数字识别—可运行验证

前言

​ 文章开始,瞎说一点其他的东西,真的是很离谱,找了至少两三个小时,就一个简单的需求:

1、利用OpenCV 在Windows进行抓图

2、利用OpenCV 进行连续数字的检测。

3、使用C++,Qt

3、将检测的结果显示出来

​ 就这么简单的需求,结果网上找了各种版本硬是找不到,要是代码可能没啥问题,但是运行不了,你这运行不了,我怎么知道你到底能不能用,我代码调半天能用了,结果你跟我说最后效果不好,为啥呢?

​ 因为图像识别这种东西,很取决于你的外部环境的,一定你的外部环境变量,你的数字的背景啥的变了,那么你的代码肯定就要做相应的调整,这种不像深度学习能够自己学习的,实际只能靠你自己一步一步的去调试验证效果怎么样,最终得到适合你的。

​ 所以,我下面会给出我这个程序的打包的可直接验证效果的版本,你如果不是一个想调代码的人,或是你不是一个有耐心的人,或者你跟我的识别环境不一致, 那么我估计我的代码你也用不了,也不必去下载了。可继续找下一个了。

​ 但如果你说,只要我代码能让你运行起来,那么你就能够花精力把它调出来,实在不行,你让AI 帮你把它调出来,这都是没问题的,因为目前的运行方式很简单,只要你确保环境跟我一致,基本就没啥问题。

环境:

Windows 10

Qt 12.8 MSVC2015

OpenCV 4.5.5(我带的这个opencv 是用VS2015编译出来的,如果没有MSVC2015 ,那么就只能靠你自己去下载一个MinGW 之类的,或是你自己对应版本的OpenCV了)

运行现象:
在这里插入图片描述

因为这个是采用那个SVM首先进行模型训练的,我的模型,每个数字只放了一张或是两张,训练量太小了,出来的效果就比较不好,而且,若要进行这个识别,肯定要注意以下几点:

1、摄像头与数字的距离一定是固定的,然后外部光源也是固定的,不能说一会亮一会不亮的,这是不合理的。

2、需要拍摄更多组的照片以及数字来进行训练,甚至该模型可以采用自训练的方式,来进行优化,但我这个版本就没有做到这个点了,这个点有需要的可以来进行优化。后面对这个方面如果我有进行优化,我会来跟贴的。

3、可以对捕抓到的数字再进行一些处理,增大SVM训练的量,这样可能效果就会稳定很多了,我上面这个摄像头是手拿着的,所以会一直飘,我觉得应该也是比较正常的,毕竟只用了一天时间,搞出了这个demo,那效果肯定会有差强人意的地方。

可运行程序

通过网盘分享的文件:NumberRecognitionTool.zip
链接: https://pan.baidu.com/s/1hr8VqU2x17pIQ561hy8nQw?pwd=1111 提取码: 1111

我有试了一下,是可以运行的,如果不能运行可以留言下,我看下是什么原因。

如下,我会把我的核心代码给贴上去,如果有环境的,直接改一改运行就可以了。如果还觉得有点懒的话,可以直接下载我上传的资源文件,那里面我会把dll,啥的,都给你打包好,直接运行即可。不过要花费点积分就是了,如果又没有积分的话,可以加我qq,或者私信我,我可以直接发你。qq在主页有。

https://download.csdn.net/download/qq_43211060/90468759?spm=1001.2014.3001.5501

我也下载了好一些往上的资源,我也不知道有没有用,反正我没用上,如果有需要的话,也可以一起发给你们。希望能对你们有帮助。

正文

一、代码

处理的核心代码:

void CDataRecognitionMgr::InitSVM()
{srand((unsigned)time(0)); // 设置随机数种子// 定义数字图像尺寸:30x50digitWidth = 30;digitHeight = 50;hog = cv::HOGDescriptor(cv::Size(digitWidth, digitHeight), // winSizecv::Size(10, 10),                  // blockSizecv::Size(5, 5),                    // blockStridecv::Size(5, 5),                    // cellSize9                              // nbins);descriptorSize = (int)hog.getDescriptorSize();// ==========================// 1. 从外部加载模板图像,并生成数据增强后的训练样本// ==========================vector<Mat> trainImages;vector<int> trainLabels;const int numAugmentations = 100; // 每个数字至少生成 100 个训练样本for (int digit = 0; digit < 10; digit++) {// 模板图像存放在指定目录下(根据需要调整路径与图片格式)string folderPattern = "./img/Mod/" + to_string(digit) + "/*.png";vector<String> files;glob(folderPattern, files, false);if (files.empty()) {cout << "未找到数字 " << digit << " 的模板图片,请检查文件夹: " << folderPattern << endl;continue;}// 生成数据增强样本for (int i = 0; i < numAugmentations; i++) {// 随机选择一个模板图片int idx = rand() % files.size();Mat img = imread(files[idx], IMREAD_GRAYSCALE);if (img.empty()) {cout << "加载图片失败: " << files[idx] << endl;continue;}// 对模板图像进行增强处理Mat augImg = augmentImage(img, digitWidth, digitHeight);trainImages.push_back(augImg);trainLabels.push_back(digit);}}int totalSamples = (int)trainImages.size();if (totalSamples == 0) {cout << "未生成任何训练样本,请检查模板图像路径与数据增强处理!" << endl;return;}cout << "生成的训练样本总数: " << totalSamples << endl;// ==========================// 2. 构造训练数据矩阵// ==========================Mat trainingFeatures(totalSamples, descriptorSize, CV_32F);Mat trainingLabelsMat(totalSamples, 1, CV_32S);for (int i = 0; i < totalSamples; i++) {vector<float> descriptors;hog.compute(trainImages[i], descriptors);for (int j = 0; j < descriptorSize; j++) {trainingFeatures.at<float>(i, j) = descriptors[j];}trainingLabelsMat.at<int>(i, 0) = trainLabels[i];}// ==========================// 3. 使用 SVM(RBF 核)训练分类器// ==========================svm = SVM::create();svm->setType(SVM::C_SVC);svm->setKernel(SVM::RBF);svm->setC(2.0);svm->setGamma(0.005);svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, 1e-6));cout << "开始训练 SVM..." << endl;svm->train(trainingFeatures, ml::ROW_SAMPLE, trainingLabelsMat);cout << "SVM 训练完成。" << endl;
}void CDataRecognitionMgr::HandlerImage(const QImage &_oImg)
{
#if 1Mat mat = _ImageToMat(_oImg);Mat matGray;cvtColor(mat, matGray, COLOR_BGR2GRAY);Mat testImgThresh;threshold(matGray, testImgThresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
//    imshow("testImgThresh",testImgThresh);Mat struct1;struct1=getStructuringElement(0,Size(2,2));//矩形结构元素Mat erodeSrc;//存放腐蚀后的图像erode(testImgThresh, erodeSrc,struct1);Mat morphKernel = getStructuringElement(MORPH_RECT, Size(3, 3));morphologyEx(erodeSrc, testImgThresh, MORPH_OPEN, morphKernel);morphologyEx(erodeSrc, testImgThresh, MORPH_CLOSE, morphKernel);vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(testImgThresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);qDebug() << "---> contours:"<<contours.size();if (contours.size() < 10){return;}vector<Rect> digitROIs;for (const auto& contour : contours) {Rect bbox = boundingRect(contour);// 根据尺寸过滤噪声与无效区域qDebug() << "---> bbox.width:"<<bbox.width<<";bbox.height:"<<bbox.height;if (bbox.width > 20 && bbox.height > 20 && bbox.width < 200 && bbox.height < 200) {digitROIs.push_back(bbox);}}// 3. 分割粘连区域int avgWidth = 90; // 假设单个数字的平均宽度,可根据实际情况调整for (size_t i = 0; i < digitROIs.size(); i++) {if (digitROIs[i].width > 1.5 * avgWidth) { // 判断是否为粘连区域// 提取粘连区域的二值图像Mat roiImg = testImgThresh(digitROIs[i]);// 计算垂直投影Mat projection(1, roiImg.cols, CV_32F);reduce(roiImg, projection, 0, REDUCE_SUM, CV_32F);// 寻找分割点(局部最小值)int splitPos = -1;float minVal = numeric_limits<float>::max();for (int j = 1; j < projection.cols - 1; j++) {float val = projection.at<float>(0, j);if (val < projection.at<float>(0, j - 1) && val < projection.at<float>(0, j + 1) && val < minVal) {minVal = val;splitPos = j;}}// 根据分割点分割边界框if (splitPos > 0) {Rect leftROI(digitROIs[i].x, digitROIs[i].y, splitPos, digitROIs[i].height);Rect rightROI(digitROIs[i].x + splitPos, digitROIs[i].y, digitROIs[i].width - splitPos, digitROIs[i].height);// 替换原始粘连区域digitROIs.erase(digitROIs.begin() + i);digitROIs.insert(digitROIs.begin() + i, leftROI);digitROIs.insert(digitROIs.begin() + i + 1, rightROI);i--; // 重新检查新插入的区域}}}// 按 x 坐标排序(从左到右)sort(digitROIs.begin(), digitROIs.end(), [](const Rect& a, const Rect& b) {return a.x < b.x;});cout << "检测到的轮廓数量: " << digitROIs.size() << endl;for (const auto& roi : digitROIs) {cout << "边界框: " << roi << endl;}string recognized = "";for (const auto& roi : digitROIs) {Mat digitROI = testImgThresh(roi);Mat digitResized;resize(digitROI, digitResized, Size(digitWidth, digitHeight));vector<float> descriptors;hog.compute(digitResized, descriptors);Mat sample(1, descriptorSize, CV_32F);for (int j = 0; j < descriptorSize; j++) {sample.at<float>(0, j) = descriptors[j];}int predicted = (int)svm->predict(sample);recognized.push_back('0' + predicted);}QString str = QString::fromStdString(recognized);emit SIGNAL_DATA_NUM(str);cout << "识别结果1: " << recognized << endl;
#endif
}

InitSVM基本就是训练的标准流程了,那么比较核心的还是下面这个函数,这个函数HandlerImage可能就需要你进行一些调整:
首先先进行基本的图像处理,由于某些打印的会出现说数字粘在一起的情况,那么就得采用这个分割粘连区域进行局部处理,才能分割出来,我这份代码试了两种情况,都还可以,一个是会粘着的,一个是不会粘着的。

其他你需要更详细的,可以将这两个函数放到AI中帮忙解释一下就可以了。

接下来,就到了我们的经典环节:
在这里插入图片描述

在这里插入图片描述

参考

1、opencv 数字识别 数码管

相关文章:

OpenCV连续数字识别—可运行验证

前言 ​ 文章开始&#xff0c;瞎说一点其他的东西&#xff0c;真的是很离谱&#xff0c;找了至少两三个小时&#xff0c;就一个简单的需求&#xff1a; 1、利用OpenCV 在Windows进行抓图 2、利用OpenCV 进行连续数字的检测。 3、使用C&#xff0c;Qt 3、将检测的结果显示出来 …...

LiveGBS流媒体平台GB/T28181功能-海康大华宇视华为像头GB28181国标语音对讲语音喊话需要的摄像头设备及服务HTTPS准备

LiveGBS海康大华宇视华为像头GB28181国标语音对讲语音喊话需要的摄像头设备及服务HTTPS准备 1、背景2、准备2.1、服务端必备条件&#xff08;注意&#xff09;2.2、准备语音对讲设备2.2.1、 大华摄像机2.2.1.1、 配置接入示例2.2.1.2、 配置音频通道编号 2.2.2、 海康摄像机2.2…...

第十五章:go package 包的管理

import f "fmt"   // 注意 这里 f 是包的别名 init初始化函数 在每一个Go源文件中&#xff0c;都可以定义任意个如下格式的特殊函数&#xff1a; func init(){// ... } package&#xff1a;声明包的关键字 packagename&#xff1a;包名&#xff0c;可以不与文…...

面试基础---高并发/高可用架构下的分库分表实战:基于 ShardingSphere 的深度解析

高并发/高可用架构下的分库分表实战&#xff1a;基于 ShardingSphere 的深度解析 引言 在高并发、高可用的分布式系统中&#xff0c;单库单表的性能瓶颈日益凸显。分库分表&#xff08;Sharding&#xff09;作为一种常见的数据库水平拆分方案&#xff0c;能够有效提升系统的扩…...

deepseek的regflow安装mac版本

deepseek的ragflow部署安装 一:ollama安装,自行完成,我本地已安装 二:查看大模型情况oll::命令ollama list,我本地无ragflow 三:docker安装:命令docker version ,自行完成,我本地已安装 四:安装知识库软件ragflow: 简单科普下Ragflow 是一个基于深度学习模型的问答生成工具&…...

Facebook 的框架及技术栈

一、前端框架与技术 React.js 及其生态系统 核心原理与特点 React.js 是 Facebook 开源的用于构建用户界面的 JavaScript 库。它的核心概念是组件化&#xff0c;将用户界面拆分成一个个独立的、可复用的组件。每个组件都有自己的状态&#xff08;state&#xff09;和属性&#…...

文献分享: 对ColBERT段落多向量的剪枝——基于学习的方法

原论文 1. 导论 & \textbf{\&} &方法 1️⃣要干啥&#xff1a;在 ColBERT \text{ColBERT} ColBERT方法中&#xff0c;限制每个段落要保留的 Token \text{Token} Token的数量&#xff0c;或者说对段落 Token \text{Token} Token进行剪枝 2️⃣怎么干&#xff1a;注…...

社交软件频繁更新,UI 设计在其中扮演什么角色?

在当今数字化时代&#xff0c;社交软件已成为人们日常生活中不可或缺的一部分。随着科技的飞速发展和用户需求的不断变化&#xff0c;社交软件更新频率日益加快。在这频繁更新的背后&#xff0c;UI 设计扮演着至关重要的角色&#xff0c;它如同社交软件的 “门面担当” 与 “交…...

Gemini Robotics:Google DeepMind 让 AI 机器人真正“动”起来!

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

概率论的基本知识

逆概率还不懂&#xff0c;改天再想想。 联合概率 联合概率&#xff08;Joint Probability&#xff09; 是概率论中的一个重要概念&#xff0c;用于描述多个随机变量同时取某些值的概率。联合概率可以帮助我们理解多个变量之间的关系。...

AI大数据挖掘的威力

通过AI挖掘大数据可以发现很多世界上用传统方法无法发现的潜在规律。 人类群体可以被精准的操控&#xff0c;这在AI发达的未来会越来越现实&#xff0c;甚至可以在社会动荡前夕精准清理权威节点。 基于AI与大数据的人类群体潜在规律发现 随着AI和大数据技术的深度结合&#xf…...

限流及熔断的场景?

限流&#xff08;Rate Limiting&#xff09;和熔断&#xff08;Circuit Breaker&#xff09;是高并发系统中常见的保护机制&#xff0c;用于防止系统过载和提高稳定性。它们适用于不同的场景&#xff1a; 限流&#xff08;Rate Limiting&#xff09;适用场景 限流主要用于控制…...

使用服务器如何DNS呢

莱卡云服务器 DNS 配置指南 一、配置云服务器本地 DNS ‌修改网络配置文件‌ ‌Ubuntu/Debian‌&#xff1a; bashCopy Code sudo nano /etc/network/interfaces # 添加或修改 DNS 配置 dns-nameservers 8.8.8.8 8.8.4.4 *&#xff08;保存后重启网络服务&#xf…...

【SpringBoot】实现登录功能

在上一篇博客中&#xff0c;我们讲解了注册页面的实现。在此基础上会跳转到登录页面&#xff0c;今天给大家带来的是使用 SpringBoot&#xff0c;MyBatis&#xff0c;Html&#xff0c;CSS&#xff0c;JavaScript&#xff0c;前后端交互实现一个登录功能。 目录 一、效果 二、…...

图论part3|101.孤岛的总面积、沉没孤岛、417. 太平洋大西洋水流问题

101. 孤岛的总面积 &#x1f517;&#xff1a;101. 孤岛的总面积思路&#xff1a;和昨天的岛的区别是&#xff1a;是否有挨着边的岛屿 所以可以先遍历四条边挨着的岛屿&#xff0c;把他们标记为非孤岛再计算其他岛屿当中的最大面积 代码&#xff1a;&#xff08;深度搜索&…...

选型消息队列(MQ):ActiveMQ、RabbitMQ、RocketMQ、Kafka对比

选型消息队列&#xff08;MQ&#xff09;&#xff1a;ActiveMQ、RabbitMQ、RocketMQ、Kafka对比 选型消息队列&#xff08;MQ&#xff09;1. 引言2. 消息队列核心指标3. MQ 技术对比分析4. 详细分析及案例4.1 ActiveMQ&#xff1a;传统企业级 MQ 方案4.2 RabbitMQ&#xff1a;高…...

常见FUZZ姿势与工具实战:从未知目录到备份文件漏洞挖掘

本文仅供学习交流使用&#xff0c;严禁用于非法用途。未经授权&#xff0c;禁止对任何网站或系统进行未授权的测试或攻击。因使用本文所述技术造成的任何后果&#xff0c;由使用者自行承担。请严格遵守《网络安全法》及相关法律法规&#xff01; 目录 本文仅供学习交流使用&am…...

基于异构特征融合与轻量级集成学习的软件漏洞挖掘方案设计与Python实现

标题:基于异构特征融合与轻量级集成学习的软件漏洞挖掘方案设计与Python实现 一、方案设计原理 异构特征工程 静态特征:基于AST的代码属性图(CPG)解析(使用Joern+NetworkX)动态特征:内存访问模式分析(通过QEMU模拟执行)上下文特征:CWE漏洞模式匹配(集成Semgrep规则引…...

监控快手关注列表更新以及去视频水印视频

def printData(self):if len(self.UpdateDataList) > 0:self.UpdateDataList sorted(self.UpdateDataList, keylambda x: x[minutes]) # 先更新的在前sucess 0for index, video in enumerate(self.UpdateDataList):minutes video[minutes]if minutes > self.updateIn…...

【从零开始学习计算机科学】数据库系统(十一)云数据库、NoSQL 与 NewSQL

【从零开始学习计算机科学】数据库系统(十一)云数据库、NoSQL 与 NewSQL 云数据库云服务器的服务云数据库和传统的分布式数据库的异同NoSQLNoSQL数据库的特点CAP定理NoSQL的特性NoSQL数据库的分类NoSQL的适用场景Nosql数据库实例-RedisRedis的优势MongoDBMongoDB的特点NewSQL…...

江科大51单片机笔记【12】AT24C02(I2C总线)

写在前言 此为博主自学江科大51单片机&#xff08;B站&#xff09;的笔记&#xff0c;方便后续重温知识 在后面的章节中&#xff0c;为了防止篇幅过长和易于查找&#xff0c;我把一个小节分成两部分来发&#xff0c;上章节主要是关于本节课的硬件介绍、电路图、原理图等理论知识…...

【附JS、Python、C++题解】Leetcode面试150题(9)——三数之和

一、题目​​​​​ 15. 三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足&#xff1a; i!j、i!k 且 j! k &#xff0c;同时还满足&#xff1a;nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意…...

网络安全防护架构有哪些 网络安全防护措施包括

网络安全预防措施 网安措施 计算机网络安全措施主要包括保护网络安全、保护应用服务安全和保护系统安全三个方面&#xff0c;各个方面都要结合考虑安全防护的物理安全、防火墙、信息安全、Web安全、媒体安全等等。 (一)保护网络安全。 网络安全是为保护商务各方网络端系统之…...

多线程实现批量保存数据

首先注入 private final SqlSessionFactory sqlSessionFactory;private final static int BATCH_SIZE 200; //保存数据条数private final static int THREAD_POOL_SIZE 15; // 线程池大小然后把保存的数据根据BATCH_SIZE 切割成多个批次封装起来&#xff1a; /*** 将数据分成…...

ActiveMQ监听器在MQ重启后不再监听问题

应用的监听器注解 JmsListener(destination "TopicName",containerFactory "FactoryName")工厂代码 BeanJmsListenerContainerFactory<?> FactoryName(ConnectionFactory connectionFactory){SimpleJmsListenerContainerFactory factory new S…...

大模型架构记录5-向量数据库

一 倒排索引、KNN、PQ 1.1 基础版本 query -> requery 对问题做处理&#xff0c;处理上下文 对query 做 refined query 1.2 向量数据库 二 搜索逻辑 2.1 knn 2.2 近似KNN 先和N个空间的均值比较再和空间内部的所有点比较&#xff0c;计算最近值。 优化一&#xff1a; …...

Linux:基本指令与内涵理解

1.文件操作指令 1.1 ls ls指令用于查看指定层级文件夹下的文件或文件夹 基本格式&#xff1a;ls (选项) (查看层级&#xff09; 其中选项处不写就默认是显示文件名&#xff0c;查看层级默认是当前层级 选项1&#xff1a; -l 作用&#xff1a;将查找文件的详细信息显示出来 我们…...

Android实现简易计算器

<?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android" android:layout_width"match_parent" android:layout_height"match_parent" and…...

PHP 在 if 判断时由于运算符优先级导致 false 的问题

首先来看一段代码&#xff1a; $price 187.50;if (!is_numeric($price) || $price < 0 || ($price * 100 > 1000000)) {echo "价格错误&#xff1a;$price\n"; } else {echo "价格正确&#xff1a;$price\n"; }乍一看是不是认为并没有什么问题&…...

【分布式】如何使用RocketMQ实现下单-库存-支付这个场景的分布式事务问题

在 下单-库存-支付 场景中&#xff0c;通过消息队列实现最终一致性&#xff0c;需保证三个微服务的操作最终一致&#xff0c;且在支付失败或库存不足时触发回滚补偿。以下是具体实现方案&#xff1a; 1. 整体流程设计 正常流程&#xff08;成功场景&#xff09; 订单服务 创建…...