PYTHON+YOLOV5+OPENCV,实现数字仪表自动读数,并将读数结果进行输出显示和保存

news2025/1/11 4:08:37

最近完成了一个项目,利用python+yolov5实现数字仪表的自动读数,并将读数结果进行输出和保存,现在完成的7788了,写个文档记录一下,若需要数据集和源代码可以私信。

最后实现的结果如下:

项目过程

首先查阅文献和文档,好家伙,不看不知道,做相似项目的很多资料都是硕士研究生的毕业项目,看来还是有一定难度的,做完以后可以自己给自己颁发一个硕士研究生水平证明。

先归纳一下我总体的思路:

        第一步是利用各种目标检测技术(包括SSD,YOLO等,我用的是YOLOV5)从图中识别并提取仪表,这样做可以将仪表从复杂的背景图中提取出来,去掉无关紧要的背景信息,在后续训练的时候可以让网络更集中的学习需要学习的特征,从而降低训练所需要的时间,优化训练的结果。(这里亲测带着背景直接训练和提取表盘再训练两个结果差很多,在precision和recall上都可以有一个0.3左右的差距,并且在速度上明显提取后收敛的更快,读数表现更好)

原始图像,除了需要读取的数据外还有大量无关背景信息
进行图像提取后,只留下关键信息和少量背景

这部分的实现很简单,制作一个仪表盘的数据集,只对表盘进行标注,随后训练网络识别仪表,在输出结果的时候写一个裁切方法把识别的图像裁切出来就可以了,比较简单,不多赘述。这里分享一个算法,由于网上保存的图像乱七八糟的,如果直接使用可能会遇到很多格式报错,但是往往格式导致的报错都很难排查,所以通过下面这个算法,可以将下载的图像里面乱七八糟的格式去掉,只保留jpg文件需要的rgb元素,这样可以保证格式不出错:

import os  
from PIL import Image


class convert2RGB():
    def __init__(self, path):
        # 图片文件夹路径
        self.path = "../"  

    def convert(self):
        filelist = os.listdir(self.path)
        for item in filelist:
            if item.endswith('.jpg') or item.endswith('.png'):
                print(item)
                file = self.path + '/' + item
                im = Image.open(file)
                length = len(im.split())
                if length == 4:
                    r, g, b, a = im.split()
                    # im = img.convert('RGB')
                    im = Image.merge("RGB", (r, g, b))
                    os.remove(file)
                    im.save(file[:-4] + ".jpg")


if __name__ == '__main__':
    # 输入自己的需要处理的文件夹路径
    imgPath = '../'
    demo = convert2RGB(imgPath)
    demo.convert()

          第二步是对提取后的表盘图像进行处理,由于各种各样的原因,我们实际上获得的表盘图像不一定都是很清晰,角度很正,光线很好的情况。肯定会存在拍摄角度倾斜,或者光线过亮或过暗等情况,所以需要进行一个图像预处理,将图像都转换为我们比较喜欢的亮度均匀,角度正对着我们的图像。

角度不好,过暗
画面倾斜,过暗
我们想要的图

这里主要是进行一些图像增强,利用算法对提取的表盘图像进行矫正,清晰化,比如一个倾斜的或者不是从正面角度拍摄的仪表,可以通过算法进行空间变换矫正,将表盘从歪歪斜斜的角度调整为“面朝我们”,同时一些表盘图像由于拍摄时光线不好,或者相机太烂,图像不清晰,存在过亮或者过暗的情况,需要用算法对图像的明暗分布进行一个平衡化处理,同时利用滤波算法去除掉图像的噪点,让图像更清晰和平滑,方便我们读数。

处理后
亮度均衡化处理前

可以看到图像处理过后有明显的改善,有利于后续的训练和读数,所以这一步是非常有必要的,在这一步针对不同的图像问题编写了多个算法,这里分享一个比较实用的,可以根据图像的亮暗来自动调用不同的算法进行亮度均匀化处理:

#  这个算法是用来进行自动化图像处理的,可以对一个文件夹中的图像进行处理
#  具体效果是可以自动判断图像的亮暗,然后调用不同的算法对图像进行处理,让图像更清晰,方便后续的读数
import os
import cv2
path = ""  # 读取图片的路径
save_file = ""  # 保存图片的路径

for i in os.listdir("/VOCdevkit/VOC2007/JPEGImages/"):  # 批量读取 批量保存
    img = cv2.imread(path+"/"+i,0) # 读取顺便灰度化
    r, c = img.shape[:2]
    dark_sum = 0  # 偏暗的像素 初始化为0个
    dark_prop = 0  # 偏暗像素所占比例初始化为0
    piexs_sum = r * c  # 整个弧度图的像素个数为r*c
    for row in img:
        for colum in row:
            if colum < 40:  # 人为设置的超参数,表示0~39的灰度值为暗
                dark_sum += 1
    dark_prop = dark_sum / piexs_sum
    if dark_prop >= 0.30:  # 人为设置的超参数:表示若偏暗像素所占比例超过0.30,调整这个参数 来调整判断图像亮暗的尺度
        print(" is dark!")  # 暗的图像
        equa = cv2.equalizeHist(img)
        result1 = cv2.GaussianBlur(equa, (3, 3), 3)
        cv2.imwrite(save_file+"/"+"%s" %i, result1)
        #  对于比较暗的图像,调用图像均衡化算法进行图片增强和高斯滤波去噪
    else:
        print(" is bright!")
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))  # 创建CLAHE对象
        dst = clahe.apply(img)  # 限制对比度的自适应阈值均衡化
        result = cv2.GaussianBlur(dst, (3, 3), 3)
        cv2.imwrite(save_file+"/"+"%s" %i, result)
        # 对于比较亮的图像 调用CLAHE 限制对比度的直方图均衡化进行图像处理,并采用高斯滤波去噪

        第三步将图像都处理好后,就开始进行标注和训练了,使用的是labelimg进行标注,同时由于常见的数据集,如coco等没有仪表图像,所以所有的数据集只可以手动获取,这里也是花了很多时间进行数据集的收集制作,主要的方式有直接搜索存图(这里推荐使用以图搜图的方式进行查找,比直接搜索可以得到更多更相关的图像),购买别人仪表数据集,最后整理了一份几百张的数据集。但是对于一个深度学习任务来说,几百张的数据集是远远不够的,所以还需要进行数据增强,也就是数据集扩充。本来想着调用opencv直接遍历文件夹对图像进行各种变换就可以了,但是一想到后面生成的数以千计的图像还要再标注,心情是崩溃的,本着能偷懒就偷懒的原则,我就想能否在进行数据增强的时候,带着标记好的文件一起变换,思路一打开动力就来了,果然前人早就遇到过这个问题,并且想到了解决方案,不得不感叹我们真的是站在巨人的肩膀上前进。我借鉴了一些前人的算法,改良出了一个数据增强算法,可以扩充数据集,并且可以带着标签一起变换,不需要再做其他标注了。最后在进行数据增强以后也是把数据集的图像扩充到了4位数,开始喂给网络训练。

算法跑完后得到了2k+的图像数据,不需要再重复标注

 在训练的过程中要关注各个参数的值,不能在一旁打lol就不管了,yolov5提供了很棒的可视化工具tensorboard,一定要用起来,通过分析tensorboard里面的各大性能指标曲线,不仅可以实时了解训练的情况,还可以在训练跑完后帮助我们找到可能存在的问题,从而进行对应的调整。

tensorboard --logdir=runs/train  # 可视化命令

最后一次训练完我的参数跑的已经相当不错了,但是都是一路摸索过来的,一路上经历了precision很高但是recall很低,recall很高但是precision左右横跳,误判率高的吓人等等问题,但是都一步一步调整了过来,在tensorboard里面把所有曲线都打开,可以看到每一次的对比,没有什么成功是一蹴而就的,都需要一个过程,努力吧,骚年!

最后一次的参数,都还不错
足足跑了十多次,发现问题,尝试修改,再跑,再改,才跑出最后一次比较不错的成绩

训练完成之后,就是喜闻乐见的detect环节,虽然网络已经提供了自带的训练结果展示,但是我还是留了一小部分数据,没有当测试集也没有当数据集,就是留着检验模型的成果,结果如下,效果还是很不错的。

 没有显示置信度是因为我手动设置了不显示置信度,同时还修改了框的粗细,因为都是数字,比较密集,如果显示置信度以及用原始的框的粗细的话会导致识别结果很难看,就像下图

 检测完毕之后,其实神经网络已经可以实现大体上的任务了,但是还需要写一个接口,获得读数的数值和读数的时间,如果还能把读取的图片一并显示出来就更好了,这样才像一个比较成熟的系统,总不能一直在pycharm里的控制台来看结果吧。

        第五步动手尝试实现一个接口,完成上述目标,看起来简单,做起来难,因为要做一个接口意味着要能够获取到网络detect过程中的参数,同时把他拿出来为己用,这需要对yolov5的源码很了解,才可以做到随心所欲的从里面拿走参数,于是我又花了一段时间来啃yolov5的源码。功夫不负有心人,在一段时间的硬啃之后,我已经大概明白yolov5的推理过程,于是我在源代码的基础上加了一些内容,实现了这个接口功能,具体实现思路如下:

1、首先阅读源代码我们知道,yolov5是先检测到目标以后,再将检测到的目标的信息传递给一个绘图工具(plot_one_box),由这个工具在图像上把检测的目标框起来,并打上其类别和置信度,所以我们要做的就是获得这个检测目标的各种信息,同时我们也要明确,这个信息包含着检测目标的位置信息,类别信息,和置信度。

2、其次我们要知道,yolov5在进行目标检测的时候是无序的,比如一张图上有两只狗一只猫,yolov5在检测的时候有可能先检测出猫,然后把它在图上框出来,再去检测狗1和狗2,也有可能是狗1,猫,狗2,在我们的任务中,一个表的示数是0479,yolov5不一定是按照0479这个顺序来检测,有可能是4790,4097,4970,这就给我们带来了麻烦,表的读数是有序的,yolov5的检测是无序的,我们需要先排序。

3、排序的方法,考虑到仪表读数一定是从左往右依次读取,所以如果可以获得每个检测目标的位置信息,随后计算出中心点坐标,按照中心点坐标从小到大的顺序来输出(yolov5的坐标轴原点在图像的左上角),得到的结果自然就是从左往右的读数结果了,所以我们明确了目标,获得位置信息,计算中心坐标,将中心坐标排序,并按照这个排序输出对应的类别,也就是读数。

4、接下来就是coding过程,烧了几天脑子后,写出了一个读数工具,可以获取网络每次检测目标的参数,同时计算其中心坐标点,并将其和本次检测的目标以键值对的方式保存在字典里,比如0和4在图中的位置是0,4那么就会创建一个字典,保存的内容是{123:0,223:4}最后由于123小于223,所以先输出0再输出4,就完成了对于读数的正确输出。

        最后能够get到读数以后,又花了一些时间,学习编写了一个简单的UI界面,能够显示出本次读取的图片,读数的时间,和读数的结果,同时在本地创建一个txt,把每一次读数的时间和结果保存,方便后续查看。在这里也遇到了一个问题,就是小数点,由于不同类型的表小数点的位置都不同,如何正确输出小数点,也是一个问题,我尝试了前期标注小数点,让网络去识别的方法,但是效果很一般,原因就是小数点就是黑不溜秋的一个小点,基本上没有什么特别的特征,并且小数点实在是太小了,记得武忠祥老师有一句话,叫做有条件麻溜点上,没有条件创造条件也要上,我宁愿犯错,也不愿什么都不做#¥%……&*,所以在考虑后,打算用算法解决,烧了一下脑子,写了一个小数点算法,可以根据表的读数和表的类型自动加上小数点,到此终于完工了,运行,输出,结果如下:

 可以看到在一个UI窗口正确输出了读数的时间和读数的值,并且在txt中保存了当下读数的时间和读数的值,这么模糊的图都能够正确读数,由此可见读数效果还是不错的....

        最后的最后再将写的各种算法进行封装整理,写进yolov5的源码里串联在一起,这样只需要丢进去一张图片,就可以直接得到如上的UI界面,并保存读数,不用手动跑多个程序了,舒服多了,其实可以再写一个入口的UI界面,完全做成一个读表的小程序,但是由于作者太懒,所以暂时先搁置吧,以上就是这一个项目的大致总结,如果想起来什么再进行补充吧....



(下面是一点总结碎碎念,内容已经全部写完,如果只是借鉴思路的话,以下可以跳过)

本次项目中学到了:

  1. 项目到手后先多读文献,多查,多看,上到期刊paper,下到csdn各种文档,这个过程主要的目的就是看看前人是怎么做这个任务的,积累思路,如果遇到不错的方法可以做一下笔记,这个互联网时代,最不缺的就是资源,只要你愿意去找。
  2. 遇到问题先去搜索,看看是否有人遇到过类似的问题,看看别人是怎么解决的,吸收思路,站在巨人的肩膀上学习可以更有效率,如果没有找到类似的问题就自己排查,将代码分成不同的块,一块一块执行,执行一步就print出来,看看是哪一步出了问题。如果是很复杂的算法,就把核心模块写一个简化版本,看看核心部分可不可以跑通,如果核心区域没问题,那就可以考虑是不是数据的传入,或者类型的转换出了问题,实在不行就保留核心,其他重写。如果是和文件,路径,数据集有关的问题,那就一定要打开对应的文件路径看看,是不是有什么东西忘记删除,有中文路径,或者某一个图片有点问题,导致了整个数据集跑不起来,可以遍历文件,一张一张图排查,找到罪魁祸首。如果实在无法解决问题,那就直接remake,从头开始,仔细的把每个步骤过一遍,有时候项目大了注意力分散,很容易在细节的地方出问题,从头走一遍可以帮助自己找到细节的问题。
  3. 不可以忽视理论的重要性,虽然刚开始做这个项目的时候我也是想着赶紧先把整体功能干出来,再去看理论,但是做着做着发现,理论不仅可以帮你优化你的模型,还可以帮你找到很多问题,如果理论理解的比较清晰,甚至可以更改这个大框架,让他为你服务。理论,源码要多看,多积累,但是不可以看了就算了,要做做笔记,毕竟看的过程也是学的过程,学一遍就写一遍,加深印象。
  4. 书里的知识就当做是字典,是help文档,可以帮助你解决一些问题,但是真正的创新还是藏在解决问题的过程之中,你在解决新的问题的同时,就已经是创新了,书上的东西都是旧的,只有卡着你脖子让你停滞不前的,才是新的,在艰难前进突破关卡的时候,回头看看自己烧脑子写出来的,替你过关斩将的算法和工具,那些都是你的创新成果,是任何一本现有的书籍上都没有的新内容。
  5. 路还很长,这个时代就是很卷,不努力就会被淘汰,就会在饭局的角落里沉默不语,只能看着多有成绩的同辈们笑谈风声,早点动手,多吃点苦,提高自己的下限,没有什么成功是一蹴而就的,努力吧骚年!

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

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

相关文章

从单体到SpringBoot/SpringCloud微服务架构无感升级的最佳实践

目录导读 从单体到SpringBoot/SpringCloud微服务架构无感升级的最佳实践1. 业务背景2. 当前问题3. 升级方案3.1 架构设计4. 详细设计4.1 迁移阻碍4.2 解决思路 5. 实现过程5.1 认证兼容改造5.2 抽象业务流程5.2.1 抽象业务的思路5.2.2 抽象业务的抽象编码5.2.3 抽象业务的具体实…

BFF网关模式开发指南

BFF是近些年新衍生出来的一种开发模式&#xff0c;或者说是一种适配模式的系统&#xff0c;BFF全称为Backend OF Front意为后端的前端&#xff0c;为了适配微服务模式下前端后端系统接口调用混乱而出现的。在如今微服务盛行的趋势下&#xff0c;大型系统中划分出了数十个服务模…

前端优化的一些方向

对于浏览器来说&#xff0c;加载网页的过程可以分为两部分&#xff0c;下载文档并响应&#xff08;5%左右&#xff09;&#xff0c;下载各种组件&#xff08;95%左右&#xff09;。 而对比大部分优秀网页来说下载文档&#xff08;10%~ 20%&#xff09;&#xff0c;下载组件&…

23_7第一周LeetCode刷题回顾

目录 1. 两数之和2. 两数相加3.无重复字符的最长子串4.寻找两个正序数组的中位数5.最长回文子串6.N 形变换7.整数反转8.字符串转整数&#xff08;atoi&#xff09;9.回文数10. 正则表达式匹配11. 盛最多水的容器12. 整数转罗马数字13. 罗马数字转整数14. 最长公共前缀15.三数之…

MyBatis中的动态SQL(sql标签、where标签、set标签、批量增加与批量删除)

目录 sql标签 ​编辑 where标签 set标签 foreach标签 批量增加 批量删除 将基础SQL语句中重复性高的增加它的复用性&#xff0c;使得sql语句的灵活性更强 sql标签<sql> <sql id"text">select * from user</sql><select id"selectA…

如何在苹果商店发布App?

一、介绍 众所周知&#xff0c;苹果对于自家产品的安全问题十分重视&#xff0c;他们有严格的一套审核标准和流程&#xff0c;当我们想要在苹果商店发布一款App的时候就需要经过重重艰难险阻&#xff0c;克服不少繁杂的问题去完成这项工作。 另外有一点需要注意的是&#xff…

C语言库函数strcpy学习

strcpy是C语言的一个标准库函数&#xff1b; strcpy把含有\0结束符的字符串复制到另一个地址空间&#xff0c;返回值的类型为char*。 原型声明&#xff1a;char *strcpy(char* dest, const char *src); 头文件&#xff1a;#include <string.h> 和 #include <stdio.h&g…

领域驱动设计(三) - 快速开始 - 【3/3】事件风暴

使用DDD的最终目的是深入学习业务如何运作。然后基于学习试验、质疑、再学习和重建模的过程。过程中面临的最大挑战是如何快速学习&#xff0c;并且在保证学习质量的前提下压缩学习时间&#xff08;你的学习是需要公司付工资的&#xff09;。 事件风暴就是一种相对高效的分析工…

【电子学会】2023年05月图形化二级 -- 接水果

接水果 天上掉落各种水果下来&#xff0c;有草莓、苹果、香蕉&#xff0c;快拿大碗去接住水果吧。 1. 准备工作 &#xff08;1&#xff09;导入背景Blue Sky&#xff1b; &#xff08;2&#xff09;删除小猫角色&#xff0c;导入角色Bowl、Apple、Strawberry、Bananas。 2.…

【技能实训】DMS数据挖掘项目-Day03

文章目录 任务5【任务5.1】基础信息实体类【任务5.2.1】继承DataBase类&#xff0c;重构日志类【任务5.2.2】继承DataBase类&#xff0c;重构物流实体类【任务5.2.3】创建物流、日志测试类&#xff0c;测试任务5.2中的程序&#xff0c;演示物流信息、日志信息的采集及打印输出 …

【Redis】Transaction(事务)

&#x1f3af;前言 Redis事务是一个组有多个Redis命令的集合&#xff0c;这些命令可以作为一个原子操作来执行。 Redis事务通常用于以下两种情况&#xff1a; 保证操作的原子性&#xff1a;在多个命令的执行过程中&#xff0c;如果有一个命令执行失败&#xff0c;整个事务都需…

【数据编制架构】数据编织(Data fabric)架构完整指南

本文探讨了 Data Fabric 的内容、原因、方式和人员&#xff0c;包括 Data Fabric 架构、挑战、优势、核心功能、供应商等。 Data Fabric——以数据为中心的企业的“必备” 在过去几年中&#xff0c;“Data Fabric”一词已成为企业数据集成和管理的代名词。分析公司 Gartner 将“…

vtkButtonWidget Window 添加按钮

有时我们需要在 VTK 窗口中增加 按钮&#xff0c;右上角&#xff1b; 实现&#xff0c;通过回调函数&#xff0c;vtkButtonCallback 获取点击&#xff1a; #include <vtkVersion.h> #include <vtkSmartPointer.h>#include <vtkPolyDataMapper.h> #include &…

Java性能权威指南-总结27

Java性能权威指南-总结27 数据库性能的最佳实践Java集合类API同步还是非同步设定集合的大小 集合与内存使用效率 数据库性能的最佳实践 Java集合类API Java的集合类API有很大的选择余地&#xff1b;Java 7至少提供了58个不同的集合类。在编写应用时&#xff0c;选择恰当的集合…

数据结构算法题——数组

leetcode-1.两数之和 leetcode-1.两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在…

阿里云AliYun物联网平台使用-申请免费试用及完成初始配置

一、项目简介 本专栏文章将围绕阿里云物联网平台&#xff0c;实现其设备向云平台的数据上传&#xff0c;客户端获取云平台数据。设备通过NBIOT技术实现无线采集&#xff0c;定时上传。 二、阿里云平台申请 阿里云物联网平台试用申请地址 进入上述超链接网址&#xff1a; 由于是…

【InnoDB 存储引擎】15.7.1 InnoDB Locking(锁实验,包含了如 记录锁、间隙锁、Next-Key Lock 算法等,重要)

文章目录 1 关于 Record Lock 的实验1.1 实验 1&#xff1a;没有主键时的如何锁定1.2 实验 1&#xff08;续&#xff09;&#xff1a;带着问题继续实验1.3 实验 2&#xff1a;有主键时如何锁定 2 关于 Next-Key Lock 的实验2.1 实验 3&#xff1a;如何确定算法的锁定范围2.2 实…

VS 字体不对齐解决方案

1. 问题描述 输入相同数量但不是同一类型的字符的字符&#xff0c;会出现字符显示不对齐的问题。 在某些需要根据对齐来写的代码的时候&#xff0c;这种情况是相当的折磨。 2. 解决方案 设置等宽字体。 依次点击 VS 上方的 工具 → 选项 → 字体和颜色 → 字体 → 随便选择一款…

基于simulink识别彩色视频序列中的交通警告标志

一、前言 此示例演示如何识别彩色视频序列中的交通警告标志&#xff0c;如“停止”、“请勿进入”和“让行”。 二、模型 下图显示了交通警告标志识别模型&#xff1a; 三、交通警告标志模板 该示例使用两组模板 - 一组用于检测&#xff0c;另一组用于识别。 为了节省计算…

Linux常用命令——ex命令

在线Linux命令查询工具 ex 启动vim编辑器的ex编辑模式 补充说明 在ex模式下启动vim文本编辑器。ex执行效果如同vi -E&#xff0c;适用于法及参数可参照vi指令&#xff0c;如要从Ex模式回到普通模式&#xff0c;则在vim中输入:vi或:visual即可。 语法 ex&#xff08;参数&…