#1024 程序员节 大图像中的小目标检测——基于YOLOV8+OnnxRuntime部署+滑动窗口+Zbar的条码检测研究

news2024/12/24 20:10:03

文章目录

  • 前言
  • 1 训练一个YOLOV8的一维码检测模型
  • 2 创建滑动窗口
    • 2.1 模块导入与测试图片展示
    • 2.2 创建滑动窗口检测,窗口大小为(640,640),滑动距离为640。对不足(640,640)的窗口进行填充
  • 3 创建onnxruntime推理引擎
    • 3.1推理测试
    • 3.2获得ONNX模型输入层(输出层)和数据维度
    • 3.3 预处理-构造输入张量 torch_list
  • 4 执行推理预测
  • 5 后处理-置信度过滤、NMS过滤
    • 5.1解析目标检测预测结果
  • 6 opencv 可视化
  • 7 检测完的切片重新放回原图
  • 8 用Zbar识别二维码
    • 8.1条码剪切
    • 8.1 筛选出选合格的条码
  • 总结
  • 相关文章

原创声明:如有转载请注明文章来源。码字不易,如对卿有所帮助,欢迎评论、点赞、收藏。
本文为深度学习的条码检测方案,如需传统算法的条码检测方案请阅读(基于Opencv+Kmeans+Zbar的条码检测与基于锐化+双边高斯滤波+Zbar的条码检测在工业光伏产线上的检测效果研究)

前言

最近项目中用到了条码检测,查阅很多资料,说用Zbar等工具检测的比较多。但是我们会发现,检测是不稳定的,Zbar是解析条码的工具包,运用好它的前提是:能够准确将条码区域提取出来,以及图像质量(分辨率、打光效果等)要把握很好。本文基于YOLOV8+OnnxRuntime部署+滑动窗口+Zbar对于条码检测进行升级,可以有效解决条码检测问题,并且速度也很高。

1 训练一个YOLOV8的一维码检测模型

  • 关于如何训练模型不是本文的重点,可以根据这篇文章简单训练一个模型(YOLOV8目标检测——模型训练)

  • 如果需要本人训练好的模型可以在点击这里下载( 文章顶部文件包:best.pt、best.onnx、 数据集、实验图片)

  • 当然若有训练和环境的相关问题,可以在评论里写明。看到一定回复。

2 创建滑动窗口

2.1 模块导入与测试图片展示

import cv2
import numpy as np
from PIL import Image

import onnxruntime

import torch
# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

import matplotlib.pyplot as plt
%matplotlib inline
```在这里插入图片描述

```python
# 显示图片
image = cv2.imread("D:/yolov8/data/images/Pic_2023_04_18_104022_3.bmp")
plt.imshow(image[:,:,::-1])
image_1 = image.copy()

在这里插入图片描述

2.2 创建滑动窗口检测,窗口大小为(640,640),滑动距离为640。对不足(640,640)的窗口进行填充

win = 0 
image_list = [] # 用来存储滑动窗口
while(win<4000):
    img_test = image[340:980,win:win+640]
    if img_test.shape[1]<640:
        top = 0
        bottom = 0
        left = 0
        right = 640-img_test.shape[1]
        img_test = cv2.copyMakeBorder(img_test,top,bottom,left,right,cv2.BORDER_CONSTANT,value=[255,255,255])  
    image_list.append(img_test)
    win = win + 640

展示窗口切片

for i in range(len(image_list)):
    plt.subplot(2,4,i+1)
    plt.imshow(image_list[i][:,:,::-1])
plt.show()

在这里插入图片描述

3 创建onnxruntime推理引擎

ort_session = onnxruntime.InferenceSession('E:/ultralytics/runs/detect/train4/weights/best.onnx', providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])

3.1推理测试

x = torch.randn(1, 3, 640, 640).numpy()
ort_inputs = {'images': x}
ort_output = ort_session.run(['output0'], ort_inputs)[0]
ort_output

输出了结果,说明onnxruntime引擎没有问题。
在这里插入图片描述

3.2获得ONNX模型输入层(输出层)和数据维度

在这里插入图片描述
在这里插入图片描述

3.3 预处理-构造输入张量 torch_list

这里是标准的yolov8输入图像的预处理模式

# 有 GPU 就用 GPU,没有就用 CPU
torch_list = []
for i in range(len(image_list)):
# 预处理-归一化
    image = image_list[i][:,:,::-1]
    image = image / 255
    # 预处理-构造输入 Tensor
    image = np.expand_dims(image, axis=0) # 加 batch 维度
    image = image.transpose((0, 3, 1, 2)) # N, C, H, W
    image = np.ascontiguousarray(image)   # 将内存不连续存储的数组,转换为内存连续存储的数组,使得内存访问速度更快
    image = torch.from_numpy(image).to(device).float() # 转 Pytorch Tensor
    # input_tensor = input_tensor.half() # 是否开启半精度,即 uint8 转 fp16,默认转 fp32 
    torch_list.append(image)

可以查看一下相关数据
在这里插入图片描述

4 执行推理预测

当你在对切片处理的时候,还需要用字典去标记切片的,即:{切片:目标检测结果}。这个字典是贯穿始终的,能够保证最后把检测之后的切片,贴到原图的时候,知道那个预测结果是哪个窗口预测得来的。

preds_dict = {}

for i in range(len(torch_list)):
    # ONNX Runtime 推理预测
    ort_output = ort_session.run(output_name, {input_name[0]: torch_list[i].cpu().numpy()})[0]

    # 转 Tensor
    preds = torch.Tensor(ort_output)
    preds_dict[str(i)] = preds

在这里插入图片描述

5 后处理-置信度过滤、NMS过滤

from ultralytics.utils import ops
for i in range(len(preds_dict)):
    pred = ops.non_max_suppression(preds_dict[str(i)], conf_thres=0.7, iou_thres=0.7, nc=1)
    preds_dict[str(i)] = pred[0]

经过非极大值抑制,条码基本都被标记出来了。
在这里插入图片描述

5.1解析目标检测预测结果

pred_det = []
num_bboxs = {} # 画框框的记录,需要标记好每个切片的目标检测数量
for i in range(len(preds_dict)):
    det = preds_dict[str(i)][:,0:6].cpu().numpy()
    preds_dict[str(i)] = det
    num_bboxs[str(i)] = len(det)

这里的类别会变成0,本文中0代表条码
在这里插入图片描述

6 opencv 可视化

# 框(rectangle)可视化配置
bbox_color = (150, 0, 0)             # 框的 BGR 颜色
bbox_thickness = 6                   # 框的线宽

# 框类别文字
bbox_labelstr = {
    'font_size':2,         # 字体大小
    'font_thickness':3,   # 字体粗细
    'offset_x':0,          # X 方向,文字偏移距离,向右为正
    'offset_y':-10,        # Y 方向,文字偏移距离,向下为正
}
for i in range(len(preds_dict)):
    
    # 遍历每个框
    for j in range(len(preds_dict[str(i)])):
    # 获取该框坐标
        bboxes_xyxy = preds_dict[str(i)][:, :4].astype('uint32')
        bbox_xyxy = bboxes_xyxy[j] 
        # 获取框的预测类别(对于关键点检测,只有一个类别)
        bbox_label = 'code'

        # 画框
        image_list[i] = cv2.rectangle(image_list[i], (bbox_xyxy[0], bbox_xyxy[1]), (bbox_xyxy[2], bbox_xyxy[3]), bbox_color, bbox_thickness)

        # 写框类别文字:图片,文字字符串,文字左上角坐标,字体,字体大小,颜色,字体粗细
        image_list[i] = cv2.putText(image_list[i], bbox_label, (bbox_xyxy[0]+bbox_labelstr['offset_x'], bbox_xyxy[1]+bbox_labelstr['offset_y']), cv2.FONT_HERSHEY_SIMPLEX, bbox_labelstr['font_size'], bbox_color, bbox_labelstr['font_thickness'])

for i in range(len(image_list)):
    plt.subplot(2,4,i+1)
    plt.imshow(image_list[i][:,:,::-1])
plt.show()

运行结果如下:
在这里插入图片描述

7 检测完的切片重新放回原图

wide = 0

for i in range(len(image_list)):  
    wide += 640
    if wide < 4000:
        image_1[340:980,i*640:640*(i+1)] = image_list[i]
    else:
        image_1[340:980,3840:4000] = image_list[i][:,0:160]
plt.imshow(image_1[:,:,::-1])

在这里插入图片描述

8 用Zbar识别二维码

8.1条码剪切

codes = []
for i in range(len(preds_dict)): 
    # 遍历每个框
    for j in range(len(preds_dict[str(i)])):
    # 获取该框坐标
        bboxes_xyxy = preds_dict[str(i)][:, :4].astype('uint32') # 目标检测预测结果:左上角X、左上角Y、右下角X、右下角Y、置信度、类别ID
        bbox_xyxy = bboxes_xyxy[j] 
        code = image_list[i][bbox_xyxy[1]:bbox_xyxy[3],bbox_xyxy[0]:bbox_xyxy[2]]
        codes.append(code)
# 展示一下提取到的条码图片
for i in range(len(codes)):
    plt.subplot(4,5,i+1)
    plt.imshow(codes[i],cmap="gray")
plt.show()

此时我们会发现条码中是有一个条码被标注上两个框的。15个目标,但涵盖了16个检测框。
在这里插入图片描述

8.1 筛选出选合格的条码

条码的宽度如果小于条码的一半,则剔除该条码

for code in codes:
    if code.shape[1]<20:  # 去除宽度小于20的条码
        codes.remove(code)
```python
# 展示一下提取到的条码图片
for i in range(len(codes)):
    plt.subplot(4,5,i+1)
    plt.imshow(codes[i],cmap="gray")
plt.show()

在这里插入图片描述

from pyzbar import pyzbar
res_1=[]
for i in range(len(codes)):
    res = pyzbar.decode(codes[i])
    res_1.append(res)
res_1

在这里插入图片描述
但是我们会发现,最后检测出的15个目标,只有14个条码。那是因为在滑动窗口中是需要去调节滑动窗口的大小以及滑动的距离。总得来说,将滑动距离从640变为320会是比较好的选择,最后对于检测出的条码再做一下处理即可。

总结

市面上有很多条码、二维码检测的算法,最让人印象深刻的就是腾讯微信扫码——基于SSD和超分算法的二维码检测方式。本文深受启发,将SSD的提取网络换成YOLOV8,将二维码的提取换成一维码。总的来说,试一次不错的体验。
本人是一名计算机视觉应用工程师,喜欢将算法应用于实际当中,这是自己的乐趣,如有什么需要讨论,欢迎评论区留言。
如您百忙之中还看到了这里,那是缘分。想来您和我一样对深度学习的应用深有兴趣,还请您帮忙点个赞,以便于更多的你我这样的人发现本文章,谢谢。

相关文章

基于Opencv+Kmeans+Zbar的条码检测与基于锐化+双边高斯滤波+Zbar的条码检测在工业光伏产线上的检测效果研究

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

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

相关文章

并发编程-并发三大特性

并发三大特性 并发编程Bug源头&#xff1a;原子性、可见性和有序性问题。 原子性 一个或多个操作&#xff0c;要么全部执行且在执行过程中不被任何因素打断&#xff0c;要么全部不执行。 注意&#xff1a;不采取任何的原子性保障措施的自增操作并不是原子性的&#xff0c;比…

【题解 树形dp 拆位】 树上异或

「KDOI-06-S」树上异或 题目描述 给定一棵包含 n n n 个节点的树&#xff0c;第 i i i 个点有一个点权 x i x_i xi​。 对于树上的 n − 1 n-1 n−1 条边&#xff0c;每条边选择删除或不删除&#xff0c;有 2 n − 1 2^{n-1} 2n−1 种选择是否删除每条边的方案。 对于…

Vue中的响应式原理是如何实现的?

Vue中的响应式原理是通过使用Vue的响应式系统来实现的。这个系统依赖于JavaScript的Object.defineProperty方法&#xff0c;以及ES6的Proxy对象&#xff08;在Vue 3中&#xff09;。 下面是Vue中响应式原理的概述&#xff1a; 1&#xff1a;数据初始化&#xff1a; 在Vue组件…

【面试HOT100】链表树

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于LeetCodeHot100进行的&#xff0c;每个知识点的修正和深入主要参考…

Java面向对象(基础)--方法应用

文章目录 一、方法的重载介绍案例&#xff08;1&#xff09;案例1 练习&#xff08;1&#xff09;练习1&#xff08;2&#xff09;练习2&#xff08;3&#xff09;练习3&#xff08;4&#xff09;练习4 二、可变个数形参的方法介绍举例&#xff08;1&#xff09;举例1&#xff…

计算机组成原理 new07 真值和机器数 无符号整数 定点整数 定点小数 $\color{red}{Δ}$

文章目录 真值和机器数 无符号整数无符号整数的定义无符号整数的特征无符号整数的表示范围无符号整数的加法无符号数的减法 有符号整数(定点整数)有符号整数的定义原码原码的特点反码反码的特点补码补码的特点快速求解n位负数补码的方法为什么补码能够多表示一个范围(重点)变形…

java1.8新特性流

案例描述 今天跟着黑马程序员的视频&#xff0c;完成“瑞吉外卖”项目的菜品信息管理模块的时候&#xff0c;遇到了一个比较陌生的写法 用到了Java8的新特性 stream().map((item) -> {}).collect() List<DishDto> collect records.stream().map((item) -> {DishDt…

人工智能(5):深度学习简介

1 深度学习 —— 神经网络简介 深度学习&#xff08;Deep Learning&#xff09;&#xff08;也称为深度结构学习【Deep Structured Learning】、层次学习【Hierarchical Learning】或者是深度机器学习【Deep Machine Learning】&#xff09;是一类算法集合&#xff0c;是机器学…

MySQL表操作—存储

建表&#xff1a; mysql> create table sch( -> id int primary key, -> name varchar(50) not null, -> glass varchar(50) not null -> ); Query OK, 0 rows affected (0.01 sec) 插入数据&#xff1a; mysql> insert into sch (id,name,…

【微信小程序】6天精准入门(第5天:利用案例与后台的数据交互)附源码

一、什么是后台交互&#xff1f; 在小程序中&#xff0c;与后台交互指的是小程序前端与后台服务器之间的数据通信和请求处理过程。通过与后台交互&#xff0c;小程序能够获取服务器端的数据、上传用户数据、发送请求等。 小程序与后台交互可以实现数据的传输、用户认证、实时消…

Babylonjs学习笔记(一)——搭建基础场景

React typescript umi Babylonjs 搭建基础场景 yarn add --save babylonjs babylonjs-loaders 1、封装基础场景 import { Engine, Scene } from "babylonjs"; import { useEffect,useRef,FC } from "react"; import "./index.less"type Prop…

自用bat脚本,命令

redis配置环境变量后 关机脚本 redis-server --service-stop启动脚本 :: 注释 rem echo off cd /d d:\\Redis :: redis-cli :: shutdown :: exit :: netstat -ano |findstr "6639" :: taskkill /pid {pid} /F redis-server redis.windows.conf pausecmd中替代gr…

BFS专题8 中国象棋-马-无障碍

题目&#xff1a; 样例&#xff1a; 输入 3 3 2 1 输出 3 2 1 0 -1 4 3 2 1 思路&#xff1a; 单纯的BFS走一遍即可&#xff0c;只是方向坐标的移动变化&#xff0c;需要变化一下。 代码详解如下&#xff1a; #include <iostream> #include <vector> #include…

上次的那段代码后续

之前写了一篇文章&#xff0c;说是一个要修改一个代码&#xff0c;很多人评论说代码说得不清不楚&#xff0c;不过在评论说又解释了一波之后&#xff0c;大家至少对这个代码有理解了&#xff0c;至少知道这个代码是做什么事情了。 如果是你&#xff0c;会不会修改这段代码&…

数据结构初阶——时间复杂度

朋友们我们又见面了&#xff0c;今天我们来学习数据结构的时间复杂度&#xff0c;在讲数据结构之前&#xff0c;大家可能只知道我们学习的是数据结构&#xff0c;但是还是不知道数据结构的具体定义&#xff0c;其实就是在内存上的数据。然后我们就像通讯录一样对它进行增删查改…

Qt 目录操作(QDir 类)及展示系统文件实战 QFilelnfo 类介绍和获取文件属性项目实战

一、目录操作(QDir 类) QDir 类提供访问系统目录结构 QDir 类提供对目录结构及其内容的访问。QDir 用于操作路径名、访问有关路径和文件的信息以及操作底层文件系统。它还可以用于访问 Qt 的资源系统 Qt 使用“/”作为通用目录分隔符&#xff0c;与“/”在 URL 中用作路径分…

istio介绍(一)

1. 概念 1.1 虚拟服务 虚拟服务提供流量路由功能&#xff0c;它基于 Istio 和平台提供的基本的连通性和服务发现能力&#xff0c;让您配置如何在服务网格内将请求路由到服务 示例&#xff1a; apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata:nam…

高项.项目管理经验、理念、教训

一、项目管理的一些经验 管项目重在管理&#xff0c;而不是死抠无关紧要的技术细节等等。 真正的团队一定是11>2&#xff0c;要把重心放在凝聚团队协力&#xff0c;共同完成目标上。 项目的推进永远都是不确定性的&#xff0c;真正考验项目经理的是不断出现的需求变更和状…

vue重修之路由【上】

文章目录 单页应用程序: SPA - Single Page Application路由简介Vue Reouter简介VueRouter的使用&#xff08;52&#xff09;组件的存放目录问题组件分类存放目录 路由的封装抽离 单页应用程序: SPA - Single Page Application 单页面应用(SPA): 所有功能在 一个html页面 上 单…

常用的跨域解决方案有哪些?

在 Web 开发中,跨域是指在浏览器环境下,通过 JavaScript 代码从一个域名的网页去访问另一个域名的资源。由于同源策略的限制,跨域请求通常会被浏览器阻止,为了实现跨域访问,HTML5 提供了一些机制来解决这个问题。 以下是一些常用的跨域解决方案: 1:JSONP(JSON with P…