【工业机器视觉】基于深度学习的水表盘读数识别(2-数据采集与增强)
【工业机器视觉】基于深度学习的仪表盘识读(1)-CSDN博客
数据采集与增强
为了训练出适应多种表型和环境条件的模型,确保数据集的质量与多样性对于模型的成功至关重要。高质量的数据不仅需要准确无误、具有代表性,还需要涵盖尽可能广泛的情况以确保模型的泛化能力。
面对现场数据采集可能遇到的困难,通过精心设计的数据采集策略,如自动采集、人工采集或两者结合的方式,可以有效获取初始数据集。此外,采用数据增强技术,例如图像变换及合成数据生成等方法,能够扩充数据集规模,增加其多样性,从而帮助克服数据不足的问题。
最终,这些措施共同作用,有助于训练出更加稳定、鲁棒的机器学习模型,使其能够在不同条件下保持良好的性能表现。
数据采集
1. 准备USB工业相机
- 选择相机:确保你选择的USB工业相机支持640x480分辨率。确认相机与你的计算机兼容(例如,是否需要额外驱动程序)。
- 连接电源:如果相机需要外部电源,请确保按照制造商的说明正确连接。
2. 架设简易支架
- 固定位置:使用简易支架将相机固定在水表上方,确保相机镜头垂直对准水表表盘中心。根据相机说明书调整焦距,使表盘清晰显示在画面中,且占据尽可能大的画面比例而不超出边界。
- 稳定性:确保支架足够稳固,避免因震动或风力导致相机位移。
3. 使用简易吹风机
- 轻柔操作:选择小型、功率较低的吹风机,仅用于轻轻吹动水表指针,以便于捕捉不同位置的读数。注意不要让吹风机过于接近水表以免造成损坏或干扰正常工作。
- 安全第一:确保吹风机不会接触到水或其他可能导致电气危险的地方。
4. 确保合适的光照环境
- 均匀照明:为避免阴影和反光,使用柔和而均匀的光源照亮水表表盘。可以考虑使用LED灯环等设备环绕在相机周围提供稳定的光线。
- 避免直射光:防止阳光或强光源直接照射到表盘上产生眩光。
5. 创建自动保存图片的脚本
下面提供一个Python脚本:
import datetimeimport cv2
import random
import numpy as npcamera_path = 'svr-detect-pointer/ALL/1'
# camera_path = 'svr-detect-digit/ALL/1'cv2.namedWindow('camera', cv2.WINDOW_NORMAL)
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)times = 0
collect_freq = random.randint(30, 100)
file_idx = 0
start_collect = Falsewhile cap.isOpened():ret, frame = cap.read()if not ret:breakcv2.resizeWindow("camera", 640, 480)cv2.imshow('camera', frame)key = cv2.waitKey(1)if key == 27:breaktimes += 1if times >= collect_freq and start_collect:times = 0collect_freq = random.randint(30, 100)file_idx += 1filename = camera_path + '/' + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '_' + str(file_idx) + '.jpg'cv2.imwrite(filename, frame)print('save camera image success: ', filename)if key == ord('s'):times = 0start_collect = not start_collectif key == ord('g'):file_idx += 1filename = camera_path + '/' + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '_' + str(file_idx) + '.jpg'cv2.imwrite(filename, frame)print('save camera image success: ', filename)cap.release()
cv2.destroyAllWindows()
确保电脑连接好相机,执行脚本,通过显示的画面调整好镜头焦距,保证表盘图像清晰可见。然后打开吹风机,看见表盘指针转动后,用鼠标左键点击到视频窗体上,然后按下s键,开始自动采集表盘图像。采集频率可以通过修改collect_freq = random.randint()中的参数调整。
指针和字轮数字的数据采集最好分开。
表盘指针
针对指针的图片不需要很多,每种表型300张左右即可,短时间可采集完成。图片示例:
表盘字轮
针对字轮数字的图片采集时间相对较长,需要观察每种表型字轮数字差异,不需要每种表型都采集一遍,选择一种常用表型即可,需要从最低两位00采集到99。图片示例:
数据增强
基于Ultralytics YOLO框架,在训练时,会对数据进行增强:翻转、剪裁、组合等,已能满足数据多样性的扩充,但是针对表盘字轮数字,由于我们只采集了最后两位数字的变化,所以需要自己实现对其他位置的字轮数字进行数据补充。(如果只识别到最后两位,可以不进行额外的数据增强操作)
字轮数字
- 将采集的图片,最后两位字轮数字位置全部剪裁,保存到本地
- 将剪裁出来的纯字轮数字图片,随机取出,覆盖到其他字轮数字位置上即可
- 由于字轮数字走字时的特殊性,不需要对剪裁出来的数字图片在其他位置上进行全数字覆盖和生成,只需要关注数字的过渡状态即可
提供一个参考代码:
if __name__ == '__main__':for filename in os.listdir('ALL/6/'):cut_digit('ALL/6/' + filename, filename.split('.')[0])gen_img(f'ALL/cut/{meter_name}/base.jpg')
cut_digit:对采集的最后两位字轮数字的图片进行剪裁
meter_name = 'wx_meter6'
top = 117
digit_pos = [(182, top), (210, top), (239, top), (267, top), (295, top)]
w_size, h_size = 26, 34def cut_digit(image_path, filename):image = cv2.imread(image_path)cut_img = image[digit_pos[4][1]:digit_pos[4][1] + h_size, digit_pos[4][0]:digit_pos[4][0] + w_size]cv2.imwrite(f'ALL/cut/{meter_name}/digits/{filename}.jpg', cut_img)
gen_img:随机组合生成新的图片,对高3位字轮数字位置
def gen_img(base_img_path):nine_digit_path = f'ALL/cut/{meter_name}/digits/9/'i_count = 0base_image = cv2.imread(base_img_path)for i in range(10):cut_d_path = f'ALL/cut/{meter_name}/digits/{i}/'cut_files = os.listdir(cut_d_path)for cut_file in cut_files:cut_img = cv2.imread(cut_d_path + cut_file)pos = digit_pos[0]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]nine_digit_files = os.listdir(nine_digit_path)random.shuffle(nine_digit_files)cut_img = cv2.imread(nine_digit_path + nine_digit_files[0])pos = digit_pos[1]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]random.shuffle(nine_digit_files)cut_img = cv2.imread(nine_digit_path + nine_digit_files[0])pos = digit_pos[2]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]random.shuffle(nine_digit_files)cut_img = cv2.imread(nine_digit_path + nine_digit_files[0])pos = digit_pos[3]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]i_count += 1save_path = f'{meter_name}_{i}_gen_{i_count}'cv2.imwrite(f'ALL/gen/{meter_name}/{save_path}.jpg', base_image)print(f'gen image success {save_path}.jpg')base_image = cv2.imread(base_img_path)for i in range(10):cut_d_path = f'ALL/cut/{meter_name}/digits/{i}/'cut_files = os.listdir(cut_d_path)for cut_file in cut_files:cut_img = cv2.imread(cut_d_path + cut_file)pos = digit_pos[1]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]nine_digit_files = os.listdir(nine_digit_path)random.shuffle(nine_digit_files)cut_img = cv2.imread(nine_digit_path + nine_digit_files[0])pos = digit_pos[2]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]random.shuffle(nine_digit_files)cut_img = cv2.imread(nine_digit_path + nine_digit_files[0])pos = digit_pos[3]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]i_count += 1save_path = f'{meter_name}_{i}_gen_{i_count}'cv2.imwrite(f'ALL/gen/{meter_name}/{save_path}.jpg', base_image)print(f'gen image success {save_path}.jpg')base_image = cv2.imread(base_img_path)for i in range(10):cut_d_path = f'ALL/cut/{meter_name}/digits/{i}/'cut_files = os.listdir(cut_d_path)for cut_file in cut_files:cut_img = cv2.imread(cut_d_path + cut_file)pos = digit_pos[2]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]nine_digit_files = os.listdir(nine_digit_path)random.shuffle(nine_digit_files)cut_img = cv2.imread(nine_digit_path + nine_digit_files[0])pos = digit_pos[3]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]i_count += 1save_path = f'{meter_name}_{i}_gen_{i_count}'cv2.imwrite(f'ALL/gen/{meter_name}/{save_path}.jpg', base_image)print(f'gen image success {save_path}.jpg')base_image = cv2.imread(base_img_path)for i in range(10):cut_d_path = f'ALL/cut/{meter_name}/digits/{i}/'cut_files = os.listdir(cut_d_path)for cut_file in cut_files:cut_img = cv2.imread(cut_d_path + cut_file)pos = digit_pos[3]base_image[pos[1]:pos[1] + h_size, pos[0]:pos[0] + w_size] = cut_img[::]i_count += 1save_path = f'{meter_name}_{i}_gen_{i_count}'cv2.imwrite(f'ALL/gen/{meter_name}/{save_path}.jpg', base_image)print(f'gen image success {save_path}.jpg')
表盘指针
-
图片采集:对于表盘指针的多样性图片采集,由于其相对简单的背景和固定的模式,确实较容易进行。这意味着在这一阶段,不需要额外的数据增强技术来增加数据集的多样性。
-
人工标注:在人工标注过程中,只对最低位指针进行了直接标注,而高位指针的读数需要通过计算梅花针(用来指示更精细或更高位数值的特殊指针)的方向和角度来间接获取。这表明,为了准确地得到所有指针的读数,不仅需要识别指针的位置,还需要理解它们之间的相对位置以及与表盘刻度的关系。
-
分割模型引入:为了更精确地处理梅花针区域,引入一个新的分割模型来专门针对这部分进行处理。分割模型可以帮助精确定位梅花针所在的区域,从而简化后续的角度计算过程。
-
单独区域标注:既然分割出的梅花针区域对于计算顶点方向非常重要,那么为这些区域创建独立的图像,并对其进行人工标注,将有助于训练更加精准的分割模型。这样做的好处是可以专注于特定区域内的特征,提高模型对该部分的理解和预测能力。
-
数据准备与标注:
- 对于已经采集到的表盘图像,可以使用现有的目标检测模型初步定位指针。
- 然后利用分割模型专注于梅花针区域,确保能够准确提取该区域。
- 最后,人工标注员只需关注分割出来的梅花针区域,标记出必要的信息(如顶点位置等),以便后续用于计算角度和读数。
提供一个指针区域剪裁的代码:
import osimport cv2AREAS = {'A': [260, 360, 383, 471]
}if __name__ == '__main__':for name in ['A']:for filename in os.listdir('ALL/' + name):src_path = os.path.curdir + '/ALL/' + name + '/' + filenamedst_path = os.path.curdir + '/CUTS/' + filenameimg = cv2.imread(src_path)cut_img = img[AREAS[name][0]:AREAS[name][1], AREAS[name][2]:AREAS[name][3]]cv2.imwrite(dst_path, cut_img)
AREAS是已经采集的表盘图片目录,一般来说梅花针切割任务不需要太关注表盘多样性,因为只针对指针区域部分,所有表盘之间的差异很小。
上面已完成数据准备,下面准备开始进行数据标注(使用lableme和labelimg两个库,然后进行数据转换)!
【工业机器视觉】基于深度学习的仪表盘识读(3)-CSDN博客
相关文章:
【工业机器视觉】基于深度学习的水表盘读数识别(2-数据采集与增强)
【工业机器视觉】基于深度学习的仪表盘识读(1)-CSDN博客 数据采集与增强 为了训练出适应多种表型和环境条件的模型,确保数据集的质量与多样性对于模型的成功至关重要。高质量的数据不仅需要准确无误、具有代表性,还需要涵盖尽可能…...
爬虫基础知识点
最近看了看爬虫相关知识点,做了记录,具体代码放到了仓库,本文仅学习使用,如有违规请联系博主删除。 这个流程图是我使用在线AI工具infography生成的,这个网站可以根据url或者文本等数据自动生成流程图,挺…...
高效利用资源:分布式有状态服务的高可靠性设计
在分布式系统设计中,实现有状态服务的高可靠性通常采用主备切换的方式。当主服务停止工作时,备服务接管任务,例如通过Keepalive实现VIP的切换以保证可用性。然而,这种方式存在资源浪费的问题,因为备服务始终处于空转状…...
aws(学习笔记第十六课) 使用负载均衡器(ELB)解耦webserver以及输出ELB的日志到S3
aws(学习笔记第十六课) 使用负载均衡器(ELB)以及输出ELB的日志到S3 学习内容: 使用负载均衡器(ELB)解耦web server输出ELB的日志到S3 1. 使用负载均衡器(ELB) 全体架构 使用ELB(Elastic Load Balancer)能够解耦外部internet访问和web server之间的耦合,…...
关于php://filter过滤器
常规的php://filter过滤器: <?php //index.php include($_REQUEST[file]); ?> <?php //flag.php $flagflag{test_flag}; ?> 同过base64读取flag.php的类容: 常用payload: (这是最常用的payload) ph…...
数据安全法-政务数据安全与开放
第五章 政务数据安全与开放 第三十七条 国家大力推进电子政务建设,提高政务数据的科学性、准确性、时效性,提升运用数据服务经济社会发展的能力。 第三十八条 国家机关为履行法定职责的需要收集、使用数据,应当在其履行法定职责的范围内依…...
MySQL数据库的数据类型
个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 MySQL数据库的数据类型 收录于专栏[MySQL] 本专栏旨在分享学习MySQL的一点学习笔记,欢迎大家在评论区交流讨论💌 目录 数据类型分类 …...
前端H5移动端基础框架模板 :Vue3 + Vite5 + Pinia + Vant4 + Sass + 附源码
技术栈选用 Vue3 Vite5 Pinia Vant4 Sass 源码地址: git clone https://gitee.com/gaiya001/h5-APP.git1. 1.vite.config.js文件配置 ** import { defineConfig } from vite // 导入 Vite 的配置函数 import vue from vitejs/plugin-vue // 导入 Vue 插件 i…...
什么是线程安全
🌈🌈🌈今天给大家分享的是:什么是线程安全 目录 线程安全的定义 线程安全的级别 (1)不可变 (2)绝对线程安全 (3)相对线程安全 (4)线程非安全…...
️️️ 避坑指南:如何修复国密gmssl 库填充问题并提炼优秀加密实践20241212
🛡️ 避坑指南:如何修复国密gmssl 库填充问题并提炼优秀加密实践 ✨ 引言 在当下的数据安全环境中,SM4作为中国国家密码算法的代表性选择,被广泛应用于金融、通信和政府领域。然而,在实际开发中,即便是开…...
深度学习实验十四 循环神经网络(1)——测试简单循环网络的记忆能力和梯度爆炸实验
目录 一、数据集构建 1.1数据集的构建函数 1.2加载数据集并划分 1.3 构建Dataset类 二、模型构建 2.1嵌入层 2.2SRN层 2.3模型汇总 三、模型训练 3.1 训练指定长度的数字预测模型 3.2 损失曲线展示 四、模型评价 五、修改 附完整可运行代码 实验大体步骤&#x…...
AWS re:Invent 发布新的数据库产品 Aurora DSQL; NineData SQL编程大赛开始; 腾讯云支持PostgreSQL 17
重要更新 1. AWS re:Invent 发布新的数据库产品 Aurora DSQL ,提供了跨区域、强一致、多区域读写的能力,同时具备99.999%(多区域部署)的可用性,兼容PostgreSQL;同时发布的还有 DynamoDB 也提供类似的跨区域…...
STM32 OLED屏幕驱动详解
一、介绍 OLED是有机发光二极管,又称为有机电激光显示(Organic Electroluminescence Display, OLED)。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广…...
Python字符串常用操作
Python字符串常用操作 一、字符串的切片 1.1、通过下标及下标范围取值 my_str myNameIsTaichi value1 my_str[2] # 正向 N value2 my_str[-5] # 反向 从 -1 开始 a字符串分割,语法:string[end: step] start:头下标,以0开…...
Redis 生产问题(重要)
缓存穿透 什么是缓存穿透? 缓存穿透说简单点就是大量请求的 key 是不合理的,根本不存在于缓存中,也不存在于数据库中 。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力…...
前端 —— Git
Git安装 下载安装包 【免费】前端前置-Git安装包资源-CSDN文库 安装 ‘git‘不是内部或外部命令及Git 的保姆级安装教程(保姆级教程)_git不是内部或外部命令-CSDN博客 vscode添加gitbash终端 setting.json "terminal.integrated.profiles.win…...
【GL006】Linux 之 shell
目录 一、shell 指令 1.1 体验shell指令 1.2 命令格式 1.3 shell中的通配符 1.4 输入输出重定向 1.5 命令置换 1.6 基本系统维护命令 1.7 Linux的进程管理命令 1.8 文件系统相关命令 1.9 Linux网络配置管理 二、shell 编程 2.1 shell 脚本的基础知识 2.2 shell 变…...
JS听到了强运的回响
正则表达式 介绍 正则表达式是用于匹配字符串中字符组合的模式,在JS中,正则表达式也是对象 通常用来查找,替换那些符合正则表达式的文本 就是筛选出符合条件的一类人 比如说 有人喜欢玩艾斯爱慕,那他喜欢的就是这一类人&…...
Linux下MySQL的简单使用
Linux下MySQL的简单使用 导语MySQL安装与配置 MySQL安装密码设置 MySQL管理 命令 myisamchkmysql其他 常见操作 C语言访问MYSQL 连接例程错误处理使用SQL 总结参考文献 导语 这一章是MySQL的使用,一些常用的MySQL语句属于本科阶段内容,然后是C语言和M…...
.net core使用AutoMapper
AutoMapper 是一个用于 .NET 平台的对象映射工具,它简化了不同对象类型之间的转换过程。在软件开发中,尤其是在分层架构的应用程序里,常常需要在不同的对象模型之间进行数据传递,例如从数据库实体到视图模型、DTO(数据…...
nmap详解
Nmap(Network Mapper)是一个开放源代码的网络探测和安全审核的工具。由于它的功能强大,被广泛应用于网络安全领域。以下是Nmap的一些主要功能及其在实战中的应用举例。 Nmap的主要功能: 端口扫描:检测目标主机上开放…...
CentOS7环境安装php
直接安装 yum -y install php CentOS7默认安装是php5,现在php已有8.3版本 先查看php -v 版本 如果是低版本,可以删除 yum remove php yum remove php-fpm yum remove php-common 一、添加REMI存储库 yum install epel-release yum install -y …...
基于深度学习的猫狗识别系统【深度学习课设】
🏆 作者简介:席万里 ⚡ 个人网站:https://dahua.bloggo.chat/ ✍️ 一名后端开发小趴菜,同时略懂Vue与React前端技术,也了解一点微信小程序开发。 🍻 对计算机充满兴趣,愿意并且希望学习更多的技…...
字体子集化实践探索
最近项目rust生成PDF组件printpdf需要内嵌完整字体导致生成的PDF很大,需要做压缩,但是rust的类库allsorts::subset::subset不支持windows,所以做了一些windows下字体子集化的尝试 方案一:node.js做子集化 fontmin 缺点是也需要集…...
A1017 基于Java+JSP+SQL Server+servlet的二手购物平台的设计与实现
二手购物平台 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 摘 要 计算机以及网络技术的飞速发展,网络的应用在全国乃至全球日益普及,随着人们的思想水平和生活水平的提高,网络已经是人们必不可少的一部分。人们的…...
Simdroid-EC:液冷仿真新星,助力新能源汽车电机控制器高效散热
近年来,新能源电动车的销量呈现出快速增长的态势。据统计,2024 年1-10月中国新能源汽车销量达728万辆,同比增长37.8%。 电机控制器在新能源汽车中对于保障动力和安全性能扮演着至关重要的角色,其核心部件IGBT(绝缘栅双…...
C语言——实现并求出两个数的最大公约数
问题描述:求出两个数的最大公约数 //求两个数的最大公约数 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<time.h>int main() {int a,b;printf("请您输入两个数 a 和 b\n");scanf…...
今天你学C++了吗?——C++中的类与对象(日期类的实现)——实践与知识的碰撞❤
♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…...
享元模式的理解和实践
在软件开发中,性能优化是一个永恒的话题。在追求高性能的过程中,减少内存的使用是一项重要的任务。享元模式(Flyweight Pattern)就是一种用于减少内存使用量的设计模式,它特别适用于存在大量重复对象的场景。本文将详细…...
Unreal Engine 中的UI界面开发
推荐的使用方式 轻量级 HUD:使用 Canvas 绘制简单的文本、调试信息或基础 UI(如准星、血量条等)。 复杂 UI:使用 UMG(Unreal Motion Graphics)和 Slate 进行布局和交互,避免手动管理 Canvas 绘制。 避免遮挡场景:仅绘制必要的内容,并利用透明度(如 FLinearColor(1, 1…...
微信答题小程序怎么做/视频号排名优化帝搜软件
本文整理了一些面试时面试官必问的知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。 以下是文档涉及的主要内容: JVM …...
镇网站建设管理工作总结/郑州网站营销推广公司
async/await Task Timeout 在日常的电脑使用过程中,估计最难以忍受的就是软件界面“卡住”“无响应”,在我有限的开发生涯中一直都是在挑战 它。在WPF中,主线程即UI线程,当我们在UI线程中执行一个很耗时的操作,以至于UI线程没能继…...
湖南网站营销推广设计/找客户资源的软件免费的
通知码进一步给出每条消息的意思,下面是按钮的通知码的可能值。 按钮通知码标识符值BN_CLICKED0BN_PAINT1BN_HILITE或BN_PUSHED2BN_DISABLE3BN_DISABLE4BN_DOUBLECLICKED或BN_DBLCLK5 BN_SETFOCUS 6BN_KILLFOCUS7 每个子窗口有一个句柄和唯一的ID,知道其…...
网站打开显示建设中/2024年重大政治时事汇总
1,C中内存申请有哪些(标准库函数) 函数名 函数原型作用例子二维数组的申请,释放mallocvoid * malloc(size_t size) 动态内存的申请 函数本身不知内存类型,关心内存的总字节数 int * p ÿ…...
wordpress没有权限建立目录权限/软件推广赚钱
晚上仔细的推敲了下大骆驼的案例,由于有段时间没继续看下去了,导致有些地方忘记了。 今天仔细的翻了下面对对象那块,说实话,认真看,用心看的话,就能看明白它写神码。 看完前面一堆的理论,发现一…...
辽阳男科医院哪家最好/seo排名教程
6月中旬,3GPP全会(TSG#80)完成了第五代移动通信技术(5G NR)独立组网标准,此前华为以PolarCode码成为eMBB场景编码中控制信道编码的最终解决方案成功提升了中国在5G标准中的话语权,不过在本次5G …...