使用Pytorch-Yolov3识别滑动验证码

上一篇介绍了keras版本的yolov3项目使用经验,这一篇是pytorch版本的。

前言

本文主要的参考资料和github项目如下:

Could not find the Qt platform plugin windows错误解决方法:

https://blog.csdn.net/DonetRen/article/details/106437538

pytorchyolov3训练自己数据集:

https://www.cnblogs.com/pprp/p/10863496.html

https://blog.csdn.net/qq_38587510/article/details/106019905

https://blog.csdn.net/qq_39056987/article/details/104327638

github项目:

labelImg:https://github.com/tzutalin/labelImg

yolov3:https://github.com/ultralytics/yolov3(master是yolov5,archive是yolov3

labelImg

安装PyQt5lxml就可以运行了,运行可能会需要配置环境变量

具体参考文章:https://blog.csdn.net/DonetRen/article/details/106437538

数据标注的方式和数据集分割的方式(划分为训练集、验证集和测试集)与keras-yolov3项目是一样的,但是数据格式转换的操作有所不同,pytorch-yolov3项目中没有keras项目中的voc_annotation.py用来转换数据格式,需要使用以下代码来完成操作。

# -*- coding: utf-8 -*-
"""
Created on Tue Oct  2 11:42:13 2018
此脚本VOC格式数据集构造完毕
需要说明的是:如果打算使用coco评价标准,需要继续构造符合 darknet格式的数据集(coco),构造coco中json格式
如果要求不高,只需要VOC格式即可,使用作者写的mAP计算程序即可
需要修改的地方:
1. sets中替换为自己的数据集
2. classes中替换为自己的类别
3. 将本文件放到VOC2007同级目录
4. 直接开始运行
"""

from xml.etree import ElementTree
import pickle
import os
from os import listdir, getcwd
from os.path import join


sets = [('2007', 'train'), ('2007', 'val'), ('2007', 'test')]  # 替换为自己的数据集
# classes = ["head", "eye", "nose"]     # 修改为自己的类别
classes = ["target"]  # 因为仅有一个类别,所以给自己的类别命名为target


def convert(size, box):
    dw = 1./(size[0])
    dh = 1./(size[1])
    x = (box[0] + box[1])/2.0 - 1
    y = (box[2] + box[3])/2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return x, y, w, h


def convert_annotation(_year, _image_id):
    in_file = open(f'VOC{_year}/Annotations/{_image_id}.xml')  # 将数据集放于当前目录下
    out_file = open(f'VOC{_year}/labels/{_image_id}.txt', 'w')
    # 读取xml文件
    tree = ElementTree.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult) == 1:
            continue
        cls_id = classes.index(cls)
        xml_box = obj.find('bndbox')
        b = (float(xml_box.find('xmin').text),
             float(xml_box.find('xmax').text),
             float(xml_box.find('ymin').text),
             float(xml_box.find('ymax').text))
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


wd = getcwd()
for year, image_set in sets:
    if not os.path.exists(f'VOC{year}/labels/'):
        os.makedirs(f'VOC{year}/labels/')
    image_ids = open(f'VOC{year}/ImageSets/Main/{image_set}.txt').read().strip().split()
    list_file = open(f'{year}_{image_set}.txt', 'w')
    for image_id in image_ids:
        # 这里的文件路径前面做了修改
        list_file.write(f'data/VOCdevkit/VOC{year}/JPEGImages/{image_id}.jpg\n')
        convert_annotation(year, image_id)
    list_file.close()


# os.system("cat 2007_train.txt 2007_val.txt > train.txt")     #修改为自己的数据集用作训练

list_file.write(f'data/VOCdevkit/VOC{year}/JPEGImages/{image_id}.jpg\n')

这里对写入的文件路径做了调整,前面加了data/VOCdevkit,这样就可以直接把VOCdevkit整个项目复制到pytorch-yolov3项目的data目录下。

使用以上脚本生成的

MiniConda

实际上就是可以创建虚拟环境和包管理,但是不得不说使用conda安装包很麻烦,使用国内镜像源下载很多包依然很慢很慢,而且想换源还要不断的改配置,相比较pip是真的好用,使用哪个镜像直接一个-i就可以,而且安装包快的飞起,这次算是对miniconda的一次尝试吧,后面如果再做项目,没有特殊的环境需求,我可能还是倾向于使用统一环境,使用pip来安装包。

补充一个使用pycharm配置conda虚拟环境的方式。

使用Pytorch-Yolov3识别滑动验证码_第1张图片

Pytorch YoloV3

apex

apex混合精度加速,无显卡不能加速。若想要使用,需要单独从github拉相关的项目。

修改配置文件

data目录下新建*.data文件,文件内容和说明如下:

classes = 1 # 改成你的数据集的类别个数
train = data/VOCdevkit/VOCdevkit/2007_train.txt # 通过voc_label.py文件生成的txt文件
valid = data/VOCdevkit/VOCdevkit/2007_test.txt # 通过voc_label.py文件生成的txt文件
names = data/yidun.names # 记录类别
backup = backup/ # 记录checkpoint存放位置
eval = coco # 选择map计算方式

backup=backup/eval=coco这两项根据自身实际情况添加即可:

backup=backup/ 指的是会在backup文件夹下存放训练得到的checkpoint;

eval=coco指的是在测试和评判模型时采用coco数据集中的评判标准,倘若加了这一项,还需要对之前生成的.xml文件做转换,将其由VOC格式转换为coco格式的json文件。

特别注意:文件真正的内容不要包含后面的注释,否则运行时会报错

修改yolov3.cfg文件:

[convolutional]
size=1
stride=1
pad=1
filters=18  # 修改为3*(classes+5)
activation=linear


[yolo]
mask = 0,1,2
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=1  # 修改为自己的类别数
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1

只需要更改每个[yolo]层前边卷积层的filter个数和[yolo]层的classes数量即可,也就是上面代码注释部分标注的内容,一共三组6处地方需要修改;同样地,实际内容要去掉注释,否则会报错。

在Windows下运行该项目,可能会有多个地方出现打开文件时编码不正确的情况,因为Windows下默认编码为gbk,所以要么将文件编码改为gbk,要么在打开文件的地方设置encoding='utf-8'

预训练权重文件

我们在训练数据的时候,一般使用预训练权重,即yolov3.weights,但是因为其是darket版的权重文件,需要通过一定的方式转换成pytorch版的;另外,除了最初的yolov3模型,还有在此基础上改写的tiny模型和spp模型。

这几个预训练权重文件(Darket版)在weights目录下的download_yolov3_weights.sh文件中给出了说明

文件权重模型转换:

$ git clone https://github.com/ultralytics/yolov3 && cd yolov3

# convert darknet cfg/weights to pytorch model
$ python3  -c "from models import *; convert('cfg/yolov3-spp.cfg', 'weights/yolov3-spp.weights')"
Success: converted 'weights/yolov3-spp.weights' to 'weights/yolov3-spp.pt'

# convert cfg/pytorch model to darknet weights
$ python3  -c "from models import *; convert('cfg/yolov3-spp.cfg', 'weights/yolov3-spp.pt')"
Success: converted 'weights/yolov3-spp.pt' to 'weights/yolov3-spp.weights'

实际上就是使用models.py脚本中的convert方法来转化,并且支持双向转换。不过需要搭配cfg文件夹下的.cfg文件来完成转换,会生成对应名称的.pt文件,反之亦然。

入口train.py

这个yolov3.pt文件在train.py的main函数的weights配置中使用,如果我们是使用命令行运行,那就在命令行中指定--weights参数就好了,如果是在类似pycharm的软件中直接执行,可以直接修改这个值,类似需要修改的还有--cfg, --data等。

命令行运行示例:

python train.py --data data/coco.data --cfg cfg/yolov3.cfg --weights yolov3.pt

另外,按照我这里训练数据存放的形式,还需要在修改datasets.py文件中的这个地方:

replace('images', 'labels') – > replace('JPEGImages', 'labels')(共两处)

因为这里在读取标签时,是直接将文件路径中的images替换为labels,后缀由jpg改为txt,而我并没有把图片放到指定的images文件下,所以这里需要改一下。

当一些必要的调整做完之后,如果不着急进度,我的建议是可以从train.py开始逐步调试,搞清楚整个项目运行的流程,具体细节一开始不用搞的很清楚,只要大概知道哪些方法都是做什么用的就行了,而且,因为一些特殊原因,可能会遇到一些报错,基本一直调试下去,就可以把所有的错误都自己独立的解决掉。

比如上面遇到的编码问题、weights参数报错(pt文件不存在)、修改配置文件不能加注释、改VOC数据生成路径、修改datasets.py等问题,均是在调试过程中发现并解决的。

模型加载

上一次研究yolov3一直不清楚yolov3.cfg这个文件的作用,只是单纯的按照要求修改文件,这次研究了下,发现这个就是yolov3模型的核心配置文件,就是从这个文件加载的神经网络模型,经过对比里面的内容发现与网络结构图一一对应:

[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky

# Downsample

[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky

[convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky

[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky

比如这里截取的部分,对应着Darknet53最开始的地方。

相应的还有yolov3-spp.cfgyolov3-tiny.cfg,对应着稍有不同的模型,使用的时候,要搭配yolov3-spp.weightsyolov3-tiny.weights使用,不过要先转换为.pt文件才能通过pytorch使用。

训练过程

训练过程的输出(上面是输出的代码):

s = ('%10s' * 2 + '%10.3g' * 6) % ('%g/%g' % (epoch, epochs - 1), mem, *mloss, len(targets), img_size)
Epoch   gpu_mem      GIoU       obj       cls     total   targets  img_size
11/299        0G      3.54      1.44         0      4.97         6       480: 100%|██████████| 16/16 [04:12<00:00, 15.77s/it]

Epoch:当前轮次

gpu_mem:推测大概是GPU占的内存吧,如果没有GPU,那就是0

GIoU:一种类似IoU(交并比)的算法,使用其作为目标损失函数;

obj:objectness(置信度);

cls:classification

*/16表示共16个批次,当前训练到第几批,因为这里总共244张图片,batch_size=16,所以总共16个batch,最后一个batch的size只有4。

验证过程的输出:

Class    Images   Targets         P         R   [email protected]        F1: 100%|██████████| 3/3 [00:24<00:00,  8.18s/it]
all        48        48     0.911     0.917     0.892     0.914

Class

Images:表示有多少张图片

Targets:表示有多少个要检测的目标

P:Precision准确率

R:Recall召回率

mAPMean Average Precision 的缩写,即均值平均精度。
作为 object dection 中衡量检测精度的指标。
计算公式为: 所有类别的平均精度求和除以所有类别。

F1

模型保存

训练的过程中,会在weights文件夹下生成best.ptlast.pt两个权重文件,last.pt随着训练轮次不断更新,best.pt则保留截止当前训练过程中最好的权重。

测试

测试使用detect.py脚本,与train.py类似,运行时通过很多命令行参数来配置,如果是直接在pycharm中运行,那么直接修改相应的参数值就好了,比如这几个参数

parser.add_argument('--cfg', type=str, default='cfg/yolov3.cfg', help='*.cfg path')
parser.add_argument('--names', type=str, default='data/yidun.names', help='*.names path')
parser.add_argument('--weights', type=str, default='weights/best.pt', help='weights path')
parser.add_argument('--source', type=str, default='data/samples', help='source')  # input file/folder, 0 for webcam

cfgnames这两个和train.py保持一致,weights这里改成我们训练过程中保存下来的最好的模型权重;

source这里需要注意,这里默认的是读取data/samples目录下的图片来测试,后面的注释还写了如果设置为0,他会测试视频;因为我上面说了我是直接把数据集都放在了data/VOCdevkit下,samples里没有图片,如果要测试的话,我还需要把测试集的图片复制过去,所以我想是不是可以把这里的source值改成我的测试目录,所以就改成了data/VOCdevkit/2007_test.txt(这个文件里保存了所有测试图片的路径,但是不是实际的测试图片)我也不确定这个source这样设置是否可行,所以我就去调试了,调试发现

webcam = source == '0' or source.startswith('rtsp') or source.startswith('http') or source.endswith('.txt')

if webcam:
    view_img = True
    torch.backends.cudnn.benchmark = True  # set True to speed up constant image size inference
    dataset = LoadStreams(source, img_size=imgsz)
else:
    save_img = True
    dataset = LoadImages(source, img_size=imgsz)

因为我这里source设置的值满足后缀是.txt,所以webcam=True,然后接下来走了视频测试的流程;

然后我就把webcam主动设置为False,让程序去走图片检测的流程,最终发现source的值要求该路径下必须是实打实的图片,所以只好把测试图片全部放到samples路径下,经过下面简单的代码移动就可以了:

with open(r'data/VOCdevkit/2007_test.txt', 'r', encoding='utf-8') as fr:
    images = fr.readlines()
    for i, image in enumerate(images):
        with open(image.strip(), 'rb') as f:
            image_content = f.read()
            fw = open(f'data/samples/test{i+1}.jpg', 'wb')
            fw.write(image_content)
            fw.close()

预测完成之后,会在output文件夹下,生成检测结果——带有检测框的图片,直接打开查看框是否准确即可。
鉴于上次在keras版本的项目上踩了较多的坑,增长了很多经验,这次实践轻车熟路,而且使用更少的数据集,得到了更高的准确率,100张训练图片就得到了90%以上的准确率。

另外这篇博客补充了pytorch-YOLOv3项目更改anchor box的尺寸和数量需要做哪些调整,另外还根据个人经验,提出了一些关于文字点选验证码的一些解决思路,不涉及具体的实现方案。

还有这篇博客,介绍一些传统方式比如模板匹配来进行滑块缺口识别的技术。

我的gtihub博客地址:https://forchenxi.github.io/

另外,如果对投资理财感兴趣的同学,可以关注我的微信公众号:运气与实力。

你可能感兴趣的:(深度学习,yolov3,目标检测,深度学习,模板匹配,OCR)