1.前沿

之前总结过yolov5来做皮卡丘的检测,用来熟悉yolov5的使用,不过总归是个demo型的应用,没啥实用价值。后来正好项目上有需要在成像条件不好的情况去检测二维码,传统的二维码检测方式基本上是通过角点检测定位二维码的三个定位点,在成像不好的时候,很容易失败。如果用深度学习去做鲁棒性就强很多,在检测到二维码之后,可以进行调焦或图像增强等手段,辅助后续的二维码识别过程。

环境准备同 yolov5实战之皮卡丘检测

2.二维码数据

首先第一步肯定是需要准备数据了,通过网络我们可以找到不少二维码数据,通过打标后,就可以得到第一批数据了。仅通过网络图片还是不够的,因为找到的二维码图像的背景不一定符合我们的实际使用场景,仅仅用这些数据训练,虽然能检测到二维码,但是误检也会比较严重。所以还需要人造一些数据,我们可以将二维码抠出来,贴到各种各样的背景图上去,用于扩增我们的数据集。



可以在文末去下载我收集的二维码数据,基于它们在自己的数据上贴图生成更多的数据。其中数据的标签和yolov5的格式一致,具体也可以参考皮卡丘那篇,或者官方repo: https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data

数据生成大概的代码:

def synthetise_image(background_image,front_image,scale=0.1,degree=10,borderValue=(114,114,114)):
background_image_cp=copy.deepcopy(background_image)
bg_h, bg_w = background_image_cp.shape[0:2]
qr_h, qr_w = front_image.shape[0:2]
roate_image, rotate_label = random_perspective(front_image, np.array([0, 0, 0, qr_w, qr_h]).reshape((-1, 5)),
translate=0, scale=scale,degrees=degree, shear=0, border=(qr_w//2,qr_w//2),borderValue=borderValue) crop_rotate = roate_image[rotate_label[0][2]:rotate_label[0][4], rotate_label[0][1]:rotate_label[0][3]] if bg_w<crop_rotate.shape[1] or bg_h<crop_rotate.shape[0]:
return None,None random_x = random.randint(0, bg_w - crop_rotate.shape[1])
random_y = random.randint(0, bg_h - crop_rotate.shape[0]) if random_y + crop_rotate.shape[0]>bg_h or random_x + crop_rotate.shape[1]>bg_w:
return None,None mask = (crop_rotate != np.array(list(borderValue)))
mask = (mask[:, :, 0] | mask[:, :, 1] | mask[:, :, 2])
mask_inv=(~mask) roi = background_image_cp[random_y:random_y + crop_rotate.shape[0], random_x:random_x + crop_rotate.shape[1]]
roi_bg = cv2.bitwise_and(roi, roi, mask=mask_inv.astype(np.uint8)) roi_fg = cv2.bitwise_and(crop_rotate, crop_rotate, mask=mask.astype(np.uint8)) dst = cv2.add(roi_bg, roi_fg)
roi[:, :] = dst[:, :] return background_image_cp,[random_x,random_y,crop_rotate.shape[1],crop_rotate.shape[0]] ... background_image=cv2.imread(os.path.join(background_dir,background_lst[index]))
background_h,background_w=background_image.shape[0:2] mixup_image,box=synthetise_image(background_image,qr_image,scale=0.2,degree=45) if mixup_image is None or (box[2]<60 or box[3]<60):
continue cnt+=1 center_x=box[0]+box[2]/2
center_y=box[1]+box[3]/2
label=[0,center_x/background_w,center_y/background_h,box[2]/background_w,box[3]/background_h] #save image
cv2.imwrite(os.path.join(save_dir,"{:0>8d}_sync_{}.jpg".format(thread_start_cnt,sub_dir)),mixup_image)
#save label
with open(os.path.join(save_label,"{:0>8d}_sync_{}.txt".format(thread_start_cnt,sub_dir)),'w') as f:
f.write("{0} {1} {2} {3} {4}\n".format(label[0],label[1],label[2],label[3],label[4]))

3.训练配置

3.1数据集设置

新建qrcode_dataset.yml, 设置下数据集的路径

train: ./data/qrcode/images/train/  # train
val: ./data/qrcode/images/val/ # val # number of classes
nc: 1 # class names
names: ['qrcode']

3.2训练参数的配置

可以根据自己的任务设置下数据增强比例这些:

# Hyperparameters for COCO training from scratch
# python train.py --batch 40 --cfg yolov5m.yaml --weights '' --data coco.yaml --img 640 --epochs 300
# See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3)
lrf: 0.001 # final OneCycleLR learning rate (lr0 * lrf)
momentum: 0.937 # SGD momentum/Adam beta1
weight_decay: 0.0005 # optimizer weight decay 5e-4
warmup_epochs: 3.0 # warmup epochs (fractions ok)
warmup_momentum: 0.8 # warmup initial momentum
warmup_bias_lr: 0.1 # warmup initial bias lr
box: 0.05 # box loss gain
cls: 0.5 # cls loss gain
cls_pw: 1.0 # cls BCELoss positive_weight
obj: 1.0 # obj loss gain (scale with pixels)
obj_pw: 1.0 # obj BCELoss positive_weight
iou_t: 0.20 # IoU training threshold
anchor_t: 4.0 # anchor-multiple threshold
# anchors: 3 # anchors per output layer (0 to ignore)
fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5)
hsv_h: 0.015 # image HSV-Hue augmentation (fraction)
hsv_s: 0.7 # image HSV-Saturation augmentation (fraction)
hsv_v: 0.4 # image HSV-Value augmentation (fraction)
degrees: 0.0 # image rotation (+/- deg)
translate: 0. # image translation (+/- fraction)
scale: 0.0 # image scale (+/- gain)
shear: 0.0 # image shear (+/- deg)
perspective: 0.0 # image perspective (+/- fraction), range 0-0.001
flipud: 0.0 # image flip up-down (probability)
fliplr: 0.5 # image flip left-right (probability)
mosaic: 0.2 # image mosaic (probability)
mixup: 0.0 # image mixup (probability)

3.3网络结构设置

根据需要调整设置自己的网络大小以及根据想要检测的二维码大小设置anchor:

anchor在非finetune训练方式下,默认是通过数据集计算出来的,若要关闭这个功能,需要训练时开启”--noautoanchor"选项。

# parameters
nc: 1 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple # anchors
anchors:
- [38,38, 53,53, 68,67] # P3/8
- [83,83, 101,100, 121,121] # P4/16
- [146,145, 176,175, 218,218] # P5/32 # YOLOv5 backbone
backbone:
# [from, number, module, args]
[#[-1, 1, Focus, [64, 3]], # 0-P1/2
[ -1, 1, Conv, [ 32, 3, 2 ] ],
[ -1, 1, Conv, [ 64, 3, 1 ] ], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, BottleneckCSP, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 9, BottleneckCSP, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, BottleneckCSP, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 1, SPP, [1024, [5, 9, 13]]],
[-1, 3, BottleneckCSP, [1024, False]], # 10
] # YOLOv5 head
head:
[[-1, 1, Conv, [512, 1, 1]],
[ -1, 1, DeConv, [ 512, 4, 2 ] ],
[[-1, 7], 1, Concat, [1]], # cat backbone P4
[-1, 3, BottleneckCSP, [512, False]], # 14 [-1, 1, Conv, [256, 1, 1]],
[ -1, 1, DeConv, [ 256, 4, 2 ] ],
[[-1, 5], 1, Concat, [1]], # cat backbone P3
[-1, 3, BottleneckCSP, [256, False]], # 18 (P3/8-small) [-1, 1, Conv, [256, 3, 2]],
[[-1, 15], 1, Concat, [1]], # cat head P4
[-1, 3, BottleneckCSP, [512, False]], # 21 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]],
[[-1, 11], 1, Concat, [1]], # cat head P5
[-1, 3, BottleneckCSP, [1024, False]], # 24 (P5/32-large) [[18, 21, 24], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]

3.4训练

设置上述这些,就可以开始训练了,由于我修改了网络结构,所以先是从头训练了,然后又进行了一次finetune训练。训练可以指定的参数:

    parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path')#finetune时基于的模型
parser.add_argument('--cfg', type=str, default='', help='model.yaml path')#模型结构文件
parser.add_argument('--data', type=str, default='data/coco128.yaml', help='data.yaml path')#数据配置
parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')#训练参数
parser.add_argument('--epochs', type=int, default=300)
parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')
parser.add_argument('--rect', action='store_true', help='rectangular training')
parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
parser.add_argument('--notest', action='store_true', help='only test final epoch')
parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')#开启后不自动计算anchor
parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')#显卡
parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')
parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
parser.add_argument('--log-imgs', type=int, default=16, help='number of images for W&B logging, max 100')
parser.add_argument('--log-artifacts', action='store_true', help='log artifacts, i.e. final trained model')
parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')#数据读取线程数
parser.add_argument('--project', default='runs/train', help='save to project/name')
parser.add_argument('--name', default='exp', help='save to project/name')

同样的,训练结果可以在控制台看或通过wandb查看(参见pikachu那篇)。

3.5结果示例

附录:数据集下载

链接:https://pan.baidu.com/s/1LFN2Kqip-hf_6V1S8sw-3g

提取码:6666

yolov5实战之二维码检测的更多相关文章

  1. 【React Native 实战】二维码扫描

    1.前言今天介绍React Native来实现二维码扫描的功能.首先我们要借助第三方插件react-native-barcode-scanner-universal来实现跨平台二维码扫描. 2.介绍 ...

  2. 《O2O实战:二维码全渠道营销》读书笔记思维导图(530KB)

  3. 基于Opencv识别,矫正二维码(C++)

    参考链接 [ 基于opencv 识别.定位二维码 (c++版) ](https://www.cnblogs.com/yuanchenhui/p/opencv_qr.html) OpenCV4.0.0二 ...

  4. (转载)Android项目实战(二十八):Zxing二维码实现及优化

    Android项目实战(二十八):Zxing二维码实现及优化   前言: 多年之前接触过zxing实现二维码,没想到今日项目中再此使用竟然使用的还是zxing,百度之,竟是如此牛的玩意. 当然,项目中 ...

  5. (转载)Android项目实战(二十八):使用Zxing实现二维码及优化实例

    Android项目实战(二十八):使用Zxing实现二维码及优化实例 作者:听着music睡 字体:[增加 减小] 类型:转载 时间:2016-11-21我要评论 这篇文章主要介绍了Android项目 ...

  6. Android项目实战(四十四):Zxing二维码切换横屏扫描

    原文:Android项目实战(四十四):Zxing二维码切换横屏扫描 Demo链接 默认是竖屏扫描,但是当我们在清单文件中配置横屏显示的时候: <activity android:name=&q ...

  7. zbar+opencv检测图片中的二维码或条形码

    zbar本身自带检测二维码条形码功能,这里使用opencv只是做一些简单的读取图片,灰度图片以及显示条形码和二维码时用到一些绘制 // barcode-qrcodescanner.cpp: 定义控制台 ...

  8. Android项目实战(二十八):Zxing二维码实现及优化

    前言: 多年之前接触过zxing实现二维码,没想到今日项目中再此使用竟然使用的还是zxing,百度之,竟是如此牛的玩意. 当然,项目中我们也许只会用到二维码的扫描和生成两个功能,所以不必下载完整的ja ...

  9. wex5 实战 二维码生成,扫描,蓝牙打印

    给人设计了一个小模块,要求是,把一个单号生成二维码,实现扫描查询单号具体信息,并能通过蓝牙把二维码打印出来.功能实现并不复杂,今天一口气把它搞定.来看效果. 一   效果演示: 二.二维码生成 1 在 ...

随机推荐

  1. Google 开发console查找元素或方法

    F12 后 在console中输入: $("#R")[0] 查找ID 为R的元素, 如需打印出元素属性值,则输入: console.dir($("#R")[0] ...

  2. 阿里云(CentOS)搭建MediaWiki

    搭建环境 系统:CentOS 7.3 PHP:5.4.16 Mysql:maria MediaWiki:1.26.4(更高版本不再支持PHP5.4) 下面我们开始安装LAMP环境: 1.安装Apach ...

  3. C# - 习题03_分析代码写出结果A.X、B.Y

    时间:2017-08-23 整理:byzqy 题目:分析代码,写出程序的输出结果: 文件:Program.cs 1 using System; 2 3 namespace Interview2 4 { ...

  4. C# 简单粗暴的毫秒转换成 分秒的格式

    C# 简单粗暴的毫秒转换成 分秒的格式 1:code(网络上很多存在拷贝或者存在bug的或者不满足自己的要求) 1 public static string RevertToTime(double m ...

  5. 微信小程序--聊天室小程序(云开发)

    微信小程序 -- 聊天室小程序(云开发) 从微信小程序开发社区更新watch接口之后,一直在构思这个项目.项目已经完成很久,但是一直都没有空写一篇博客记录展示一下. 开源地址 wx-cloud-im: ...

  6. Java 学习:数据类型

    前言:Java属于强类型语言 强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用 优势就是安全性高,但劣势速度慢 数据类型 Java的数据类型分为两大类: 基本类型(primit ...

  7. 解决方案-问题001:物理机、虚机等等Linux操作系统/usr/bin目录权限误操作,导致无法切换root

    导语:平常运维人员会误操作一些目录权限,导致一些问题,那么如何恢复呢? 问题:物理机.虚机等等Linux操作系统/usr/bin目录权限误操作,导致无法切换root? 实验环境: ip地址 是否目录正 ...

  8. Docker容器编排工具——docker-compose

    1.docker-compose介绍 2.使用的三个步骤 3.安装docker-compose yum install docker-compose 4.docker-compose.yml 文件 ( ...

  9. 被面试官问懵:TCP 四次挥手收到乱序的 FIN 包会如何处理?

    摘要:收到个读者的问题,他在面试的时候,被搞懵了,因为面试官问了他这么一个网络问题. 本文分享自华为云社区<TCP 四次挥手收到乱序的 FIN 包会如何处理?>,作者:小林coding . ...

  10. git跟踪忽略规则文件.gitignore

    在使用Git的过程中,我们希望有的文件比如临时文件,编译的中间文件等不要被跟踪,也不需要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交. 配置语法 以斜杠"/"开 ...