目录

AaronJny

诗酒繁华,书剑天涯。

X

xyolo?几行Python代码轻松完成yolo3目标检测

前段时间在 qqwweee/keras-yolo3 的基础上改写出了一个支持 TensorFlow 2.2 版本的 yolo3 项目,我把它命名为tf2-keras-yolo3

怎么说呢,用是肯定能用的,但操作步骤太过繁琐和反人性化,自己用起来嫌麻烦,别人用起来上手也有一定难度,于是,我萌生了重构并打包它的想法。

于是,就有了xyolo~

xyolo可以通过 pip 一键安装,而且使用起来非常简单,只需要几行代码就可以训练自己的 yolo3 模型,或者调用训练好的模型进行目标检测。废话少说,一起来看一下吧!

image.png

GitHub 地址:https://github.com/AaronJny/xyolo

转载请注明来源:https://www.aaronjny.com/articles/2020/11/08/1604823112022.html

写在前面:

我使用的 Python 是 Anaconda 的 Python 3.7 发行版本,在 shell 里面进行了初始化(python 和 pip 默认指向当前激活环境,而不是默认的 python2),所以文章中的 python 和 pip 请根据自己的情况判断是否需要替换为 python3 和 pip3。

一、安装

1、通用安装方法

xyolo的安装非常简单,通过 pip 一键安装即可。请注意,xyolo需要安装的 TensorFlow 版本 >=2.2(未安装 TensorFlow 的话则会自动安装)

pip install --user xyolo

建议使用 --user参数,避免遇到权限问题。

当然,如果有条件的话,使用 conda 创建一个新环境,并在新环境中进行安装会更好,可以避免意料之外的依赖冲突问题。

2、GPU 版本安装方法

如果你想使用 TensorFlow 的 GPU 版本,可以选择在安装 xyolo 之前,先安装 2.2 及以上的 tensorflow-gpu。以 conda 举例,安装 GPU 支持的操作流程如下:

1.创建一个虚拟环境,名为 xyolo。

conda create -n xyolo python=3.7

2.切换到刚创建好的环境里

conda activate xyolo

切换完成后,可以通过 pip 查看一下环境里安装的包:

pip list

结果不出预料,很干净,毕竟是一个新环境嘛:

Package    Version
---------- -------------------
certifi    2020.6.20
pip        20.2.4
setuptools 50.3.0.post20201006
wheel      0.35.1

3.在新环境中安装 tensorflow-gpu

注意,安装前需要先安装好和 tensorflow 版本对应的显卡驱动,这部分还有点麻烦,就不在这篇文章中说明了,我觉得会选择使用 GPU 跑 xyolo 的同学应该都已经掌握了这部分技能了吧?

毕竟如果完全没有接触过 tensorflow-gpu 的话,接触 xyolo 应该也会选择 CPU 版本更好上手。

我们通过 conda 来安装 tensorflow-gpu,gpu 版本的 tensorflow 存在 cuda 和 cudnn 依赖,使用 conda 可以自动解决这两者的版本依赖和配置问题。

conda install tensorflow-gpu=2.2

安静地等待一段时间,即可完成 tensorflow-gpu 的安装。

4.使用 pip 安装 xyolo

pip install --user xyolo

通过再次执行 pip list,我们能够看到成功安装的 xyolo 及相关依赖。

Package                Version
---------------------- -------------------
absl-py                0.11.0
aiohttp                3.6.3
astunparse             1.6.3
async-timeout          3.0.1
attrs                  20.2.0
blinker                1.4
brotlipy               0.7.0
cachetools             4.1.1
certifi                2020.6.20
cffi                   1.14.3
chardet                3.0.4
click                  7.1.2
cryptography           3.1.1
cycler                 0.10.0
gast                   0.3.3
google-auth            1.23.0
google-auth-oauthlib   0.4.2
google-pasta           0.2.0
grpcio                 1.31.0
h5py                   2.10.0
idna                   2.10
importlib-metadata     2.0.0
Keras-Preprocessing    1.1.0
kiwisolver             1.3.1
loguru                 0.5.3
lxml                   4.6.1
Markdown               3.3.2
matplotlib             3.3.2
mkl-fft                1.2.0
mkl-random             1.1.1
mkl-service            2.3.0
multidict              4.7.6
numpy                  1.18.5
oauthlib               3.1.0
opencv-python          4.4.0.46
opt-einsum             3.1.0
Pillow                 8.0.1
pip                    20.2.4
protobuf               3.13.0
pyasn1                 0.4.8
pyasn1-modules         0.2.8
pycparser              2.20
PyJWT                  1.7.1
pyOpenSSL              19.1.0
pyparsing              2.4.7
PySocks                1.7.1
python-dateutil        2.8.1
requests               2.24.0
requests-oauthlib      1.3.0
rsa                    4.6
scipy                  1.4.1
setuptools             50.3.0.post20201006
six                    1.15.0
tensorboard            2.2.2
tensorboard-plugin-wit 1.6.0
tensorflow             2.2.0
tensorflow-estimator   2.2.0
termcolor              1.1.0
tqdm                   4.51.0
urllib3                1.25.11
Werkzeug               1.0.1
wheel                  0.35.1
wrapt                  1.12.1
xyolo                  0.1.3
yarl                   1.6.2
zipp                   3.4.0

二、使用方法

咳咳,一切准备就绪,嗨起来!

1、使用官方预训练权重,进行目标检测测试

yolov3 官方有提供预训练权重,如果我们要在和 VOC 数据集同分布或相似分布的图片上做目标检测的话,直接使用官方预训练权重也是可以的。

xyolo 调用官方预训练模型的逻辑是:

  • 1.从官网下载预训练权重(Darknet 输出格式)
  • 2.将 Darknet 的权重文件转成 keras 的权重文件(现在 TensorFlow 和 Keras 已经基本不分家啦)
  • 3.构建模型,加载预训练权重
  • 4.对选择的图片进行目标检测

好麻烦啊,是不是要写很多代码?当然不是 ~ 歪嘴.jpg

image.png

首先,准备一张用于检测的图片,假设它的路径是 ./xyolo_data/detect.jpg,图片内容如下:

image.png

一个简单的示例如下:

# 导入包
from xyolo import YOLO, DefaultYolo3Config
from xyolo import init_yolo_v3


# 创建默认配置类对象
config = DefaultYolo3Config()
# 初始化xyolo(下载预训练权重、转换权重等操作都是在这里完成的)
# 下载和转换只在第一次调用的时候进行,之后再调用会使用缓存的文件
init_yolo_v3(config)
# 创建一个yolo对象,这个对象提供使用yolov3进行检测和训练的接口
yolo = YOLO(config)

# 检测并在图片上标注出物体
img = yolo.detect_and_draw_image('./xyolo_data/detect.jpg')
# 展示标注后图片
img.show()

输出如下:

2020-11-03 23:33:49.645 | DEBUG    | xyolo.yolo3.yolo:detect_image:273 - Found 3 boxes for img
2020-11-03 23:33:49.648 | DEBUG    | xyolo.yolo3.yolo:detect_image:289 - Class dog 0.99,Position (402, 176), (586, 310)
2020-11-03 23:33:49.650 | DEBUG    | xyolo.yolo3.yolo:detect_image:289 - Class bicycle 0.96,Position (0, 80), (383, 412)
2020-11-03 23:33:49.652 | DEBUG    | xyolo.yolo3.yolo:detect_image:289 - Class person 1.00,Position (9, 1), (200, 410)
2020-11-03 23:33:49.652 | DEBUG    | xyolo.yolo3.yolo:detect_image:292 - Cost time 6.65432205500838s

同时,打开了一张图片(图片尺寸不一致是我截图和排版的问题):

image.png

这样,一次目标检测就完成了,舒服呀 ~

当然了,我相信肯定有不少同学已经在这段代码的执行过程中遇到困难或疑惑了,我来集中解答几个可能会遇到的。

1.预训练权重下载慢或无法下载

因为 yolov3 的官网在国外,所以国内下载慢也很正常。推荐使用代理,或从备份地址下载后指定预训练权重的地址。具体方法参考 2、预训练权重无法下载或下载速度慢的解决方案

2.提示权限问题

因为 xyolo 会自动下载预训练权重到安装目录下,在某些情况下可能会遇到权限问题。解决方法就是在安装部分说的,通过指定 --user参数指明将包安装在用户目录下,一般就没问题了。

3.检测的速度慢

细心的同学可能发现了,上面对图片做的一次检测,竟然花了 6 秒!这也太慢了吧?

事实上,并不是这样的。TensorFlow 2.x 版本,默认使用动态图,性能会稍弱于 1.x 的静态图。所以,我这里使用了 tf.function 对速度进行了优化,在第一次运算时,模型会自动生成静态图,这部分会比较消耗时间,但后续单次计算的时间都会极大地缩短。

假如我们接着进行几次识别,就能够发现单次识别的时间有了明显降低,一般只需要零点几秒或几十毫秒。

2、预训练权重无法下载或下载速度慢的解决方案

主要有两种方法能够解决,分别看一下。

1.设置代理

如果我们手上有能够加快访问速度的网络代理(你应该知道我在说啥 ~),可以通过设置代理的形式加快下载速度,示例如下:

from xyolo import YOLO, DefaultYolo3Config
from xyolo import init_yolo_v3

# 创建一个DefaultYolo3Config的子类,在子类里覆盖默认的配置
class MyConfig(DefaultYolo3Config):
    def __init__(self):
        super(MyConfig, self).__init__()
        # 这是替换成你的代理地址
        self.requests_proxies = {'https': 'http://localhost:7890'}
    
# 使用修改后的配置创建yolo对象
config = MyConfig()
init_yolo_v3(config)
yolo = YOLO(config)

# 检测
img = yolo.detect_and_draw_image('./xyolo_data/detect.jpg')
img.show()

2.从备用链接手动下载

如果没有代理的话,也可以选择从备用下载地址下载。

我把预训练权重上传到百度云上了,链接如下:

链接: https://pan.baidu.com/s/1jXpoXHQHlp6Ra0jImruPXg 密码: 48ed

从分享页面下载好权重文件后,又有两种设置方式。

① 将文件复制到 xyolo 包的安装目录下的 xyolo_data 目录下即可,就和自动下载的情况是一样的,后续不需要再人为做任何操作。

② 将文件保存在任意位置,并在配置类中设置它的路径。

第一种就不需要多说了,来看一个第二种设置方法的示例:

from xyolo import YOLO, DefaultYolo3Config
from xyolo import init_yolo_v3

# 创建一个DefaultYolo3Config的子类,在子类里覆盖默认的配置
class MyConfig(DefaultYolo3Config):
    def __init__(self):
        super(MyConfig, self).__init__()
        # 这是替换成你的文件路径,为了避免出错,请尽量使用绝对路径
        self._pre_training_weights_darknet_path = '/Users/aaron/data/darknet_yolo.weights'
    
# 使用修改后的配置创建yolo对象
config = MyConfig()
init_yolo_v3(config)
yolo = YOLO(config)

# 检测
img = yolo.detect_and_draw_image('./xyolo_data/detect.jpg')
img.show()

3、使用自己的数据训练模型

首先介绍一下 xyolo 的数据集的输入格式。xyolo 的输入数据集可表示如下:

数据集是一个 txt 文本文件,它包含若干行,每行是一条数据。

每一行的格式: 图片路径 box1 box2 ... boxN

每一个 box 的格式:框框左上角 x 值,框框左上角 y 值,框框右下角 x 值,框框右下角 y 值,框框内物体的类别编号

给出一个示例:

path/to/img1.jpg 50,100,150,200,0 30,50,200,120,3
path/to/img2.jpg 120,300,250,600,2
...

进行图像标注的工具,labelImg 算是用的比较多的。labelImg 的标注文件格式默认是 VOC 的格式,文件类型是 XML,与 xyolo 的输入格式并不相同。不用担心,xyolo 提供了一个数据格式转换的脚本,我们只需要调用即可。

# 引入转换脚本
from xyolo import voc2xyolo

# voc格式的标注数据路径的正则表达式
input_path = '/Users/aaron/data/labels_voc/*.xml'
# classes是我们要检测的所有有效类别名称构成的txt文件,每个类别一行
classes_path = '/Users/aaron/code/xyolo/tests/xyolo_data/classes.txt'
# 转换后的xyolo数据集存放路径
output_path = '/Users/aaron/code/xyolo/tests/xyolo_data/xyolo_label.txt'
# 开始转换
voc2xyolo(input_path=input_path, classes_path=classes_path, output_path=output_path)

脚本执行后,会有进度条提示处理进度,当进度条达到 100% 时,处理完成。

100%|██████████| 106/106 [00:00<00:00, 3076.05it/s]

有了数据集之后,可以准备训练了。在此之前,确认一遍我们需要的东西:

  • xyolo 支持的数据集
  • 要检测的所有有效类别名称构成的 txt 文件(classes.txt

我以检测某个网站的验证码图片上的文字举例。

首先确定要检测的类别,我只需要判断字的位置,不判断每个字是什么,所以就一个类别 text。我们创建一个 classes.txt文件,在里面写入:

text

然后,我标注并使用 xyolo 转换了数据集(xyolo_label.txt):

/home/aaron/tmp/test_xyolo/images/162.png 47,105,75,141,0 157,52,181,80,0 197,85,229,120,0 265,85,296,117,0 257,131,293,166,0 355,63,386,90,0
/home/aaron/tmp/test_xyolo/images/88.png 93,46,129,86,0 79,139,114,174,0 209,42,237,72,0 200,68,226,98,0 256,53,295,86,0 209,134,247,171,0
/home/aaron/tmp/test_xyolo/images/176.png 43,88,76,120,0 123,91,153,127,0 98,155,127,184,0 189,117,224,152,0 289,54,319,86,0 348,123,374,151,0
/home/aaron/tmp/test_xyolo/images/63.png 36,128,72,161,0 79,130,104,161,0 127,120,160,153,0 305,111,329,138,0 302,125,334,153,0 342,81,380,119,0
/home/aaron/tmp/test_xyolo/images/77.png 164,114,200,150,0 193,147,225,182,0 309,90,336,120,0 349,89,382,121,0 321,126,352,155,0 298,150,327,177,0
/home/aaron/tmp/test_xyolo/images/189.png 119,90,148,118,0 122,132,150,159,0 208,44,240,76,0 279,60,314,97,0 299,65,334,98,0 331,93,364,129,0
/home/aaron/tmp/test_xyolo/images/200.png 232,58,265,91,0 288,58,316,85,0 49,118,78,148,0 55,134,83,163,0 75,148,103,175,0 312,131,343,163,0
/home/aaron/tmp/test_xyolo/images/76.png 20,61,56,97,0 29,108,57,139,0 76,117,111,154,0 139,117,167,147,0 204,116,242,157,0 336,153,376,191,0
...

开始训练:

# 导入包
from xyolo import DefaultYolo3Config, YOLO
from xyolo import init_yolo_v3

# 创建一个DefaultYolo3Config的子类,在子类里覆盖默认的配置
class MyConfig(DefaultYolo3Config):
    def __init__(self):
        super(MyConfig, self).__init__()
        # 数据集路径,推荐使用绝对路径
        self._dataset_path = '/home/aaron/tmp/test_xyolo/xyolo_data/yolo_label.txt'
        # 类别名称文件路径,推荐使用绝对路径
        self._classes_path = '/home/aaron/tmp/test_xyolo/xyolo_data/classes.txt'
        # 模型保存路径,默认是保存在当前路径下的xyolo_data下的,也可以进行更改
        # 推荐使用绝对路径
        self._output_model_path = '/home/aaron/tmp/test_xyolo/output_model.h5'
    

# 使用修改后的配置创建yolo对象
config = MyConfig()
init_yolo_v3(config)
# 如果是训练,在创建yolo对象时要传递参数train=True
yolo = YOLO(config, train=True)
# 开始训练,训练完成后会自动保存
yolo.fit()

如果想要使用训练好的模型进行预测,可以这样写:

# 导入包
from xyolo import DefaultYolo3Config, YOLO
from xyolo import init_yolo_v3

# 创建一个DefaultYolo3Config的子类,在子类里覆盖默认的配置
class MyConfig(DefaultYolo3Config):
    def __init__(self):
        super(MyConfig, self).__init__()
        # 类别名称文件路径,推荐使用绝对路径
        self._classes_path = '/home/aaron/tmp/test_xyolo/xyolo_data/classes.txt'
        # yolo对象使用的模型路径,也就是刚刚训练好的模型路径,推荐使用绝对路径
        self._model_path = '/home/aaron/tmp/test_xyolo/output_model.h5'
    

# 使用修改后的配置创建yolo对象
config = MyConfig()
init_yolo_v3(config)
yolo = YOLO(config)
# 检测
img = yolo.detect_and_draw_image('./xyolo_data/detect.jpg')
img.show()

觉得反复配置 Config 类麻烦?实际上,对于一个项目,我们只需要配置一个 Config 类,把项目用到的配置都一起配置了,然后再引用同一个类即可,我现在这么写只是为了方便演示。比如上面这个,可以直接把训练和调用的配置写在一起:

# 创建一个DefaultYolo3Config的子类,在子类里覆盖默认的配置
class MyConfig(DefaultYolo3Config):
    def __init__(self):
        super(MyConfig, self).__init__()
        # 数据集路径,推荐使用绝对路径
        self._dataset_path = '/home/aaron/tmp/test_xyolo/xyolo_data/yolo_label.txt'
        # 类别名称文件路径,推荐使用绝对路径
        self._classes_path = '/home/aaron/tmp/test_xyolo/xyolo_data/classes.txt'
        # 模型保存路径,默认是保存在当前路径下的xyolo_data下的,也可以进行更改
        # 推荐使用绝对路径
        self._output_model_path = '/home/aaron/tmp/test_xyolo/output_model.h5'
		# yolo对象使用的模型路径,也就是刚刚训练好的模型路径,推荐使用绝对路径
        self._model_path = '/home/aaron/tmp/test_xyolo/output_model.h5'

4、可选的全部配置项

我决定在 xyolo工具里面给用户足够的定制空间,所以参数会设置的比较多,这样使用起来会更加灵活。

但也不用担心配置繁琐,因为如上面所见,其实常用的配置项就几个,只要掌握那几个就可以轻松上手了。但是如果想更灵活的话,就需要了解更多参数。下面给出一份完整的参数配置信息(也就是 xyolo 的默认配置):

from os.path import abspath, join, dirname, exists
from os import mkdir


class DefaultYolo3Config:
    """
    yolo3模型的默认设置
    """

    def __init__(self):
        # xyolo各种数据的保存路径,包内置
        self.inner_xyolo_data_dir = abspath(join(dirname(__file__), './xyolo_data'))
        # xyolo各种数据的保存路径,包外,针对于项目
        self.outer_xyolo_data_dir = abspath('./xyolo_data')
        # yolo3预训练权重下载地址
        self.pre_training_weights_url = 'https://pjreddie.com/media/files/yolov3.weights'
        # 下载文件时的http代理,
        # 如需设置,格式为{'https_proxy':'host:port'},如{'https_proxy':'http://127.0.0.1:7890'},
        # 详细设置请参考 https://requests.readthedocs.io/en/master/user/advanced/#proxies
        self.requests_proxies = None
        # Darknet格式的预训练权重路径,请填写相对于inner_xyolo_data_dir的相对或绝对路径
        self._pre_training_weights_darknet_path = 'darknet_yolo.weights'
        # yolo3预训练权重darknet md5 hash值,用于处理异常数据
        self.pre_training_weights_darknet_md5 = 'c84e5b99d0e52cd466ae710cadf6d84c'
        # 转化后的、Keras格式的预训练权重路径,请填写相对于inner_xyolo_data_dir的相对或绝对路径
        self._pre_training_weights_keras_path = 'keras_weights.h5'
        # 预训练权重的配置路径,请填写相对于inner_xyolo_data_dir的相对或绝对路径
        self._pre_training_weights_config_path = 'yolov3.cfg'
        # 默认的anchors box路径,请填写相对于inner_xyolo_data_dir的相对或绝对路径
        self._anchors_path = 'yolo_anchors.txt'
        # 默认的类别文本路径,请填写相对于inner_xyolo_data_dir的相对或绝对路径
        self._classes_path = 'coco_classes.txt'
        # 训练输出的模型地址,请填写相对于outer_xyolo_data_dir的相对或绝对路径
        self._output_model_path = 'output_model.h5'
        # 数据集路径,请填写相对于outer_xyolo_data_dir的相对或绝对路径
        self._dataset_path = 'dataset.txt'
        # 是否开启TensorBoard,默认开启
        self.use_tensorboard = True
        # 训练时TensorBoard输出路径,请填写相对于outer_xyolo_data_dir的相对或绝对路径
        self._tensorboard_log_path = './tensorboard/logs'
        # 是否开启CheckPoint,默认开启
        self.use_checkpoint = True
        # 是否开启学习率衰减
        self.use_reduce_lr = True
        # 学习率衰减监控指标,默认为验证loss
        self.reduce_lr_monitor = 'val_loss'
        # 学习率衰减因子,new_lr = lr * factor
        self.reduce_lr_factor = 0.1
        # 连续patience个epochs内结果未改善,则进行学习率衰减
        self.reduce_lr_patience = 3
        # 是否开启early_stopping
        self.use_early_stopping = True
        # early_stopping监控指标,默认为验证loss
        self.early_stopping_monitor = 'val_loss'
        # 指标至少变化多少认为结果改善了
        self.early_stopping_min_delta = 0
        # 连续patience个epochs内结果未改善,则提前结束训练
        self.early_stopping_patience = 10
        # yolo默认加载的模型路径(最好填写绝对路径),优先级设置见下方model_path方法
        self._model_path = ''
        # 目标检测分数阈值
        self.score = 0.3
        # 交并比阈值
        self.iou = 0.45
        # 模型图片大小
        self.model_image_size = (416, 416)
        # GPU数量
        self.gpu_num = 1
        # 训练时的验证集分割比例,默认为0.1,
        # 即将数据集中90%的数据用于训练,10%的用于测试
        self.val_split = 0.1
        # 训练分为两步,第一步冻结大多数层进行训练,第二步解冻进行微调
        # 是否开启冻结训练,建议开启
        self.frozen_train = True
        # 冻结时,训练的epoch数
        self.frozen_train_epochs = 50
        # 冻结时,训练的batch_size
        self.frozen_batch_size = 32
        # 冻结时的初始学习率
        self.frozen_lr = 1e-3
        # 是否开启解冻训练,建议开启
        self.unfreeze_train = True
        # 解冻时,训练的epoch数
        self.unfreeze_train_epochs = 50
        # 解冻时,训练的batch_size.注意,解冻时训练对GPU内存需求量非常大,这里建议设置小一点
        self.unfreeze_batch_size = 1
        # 解冻时的初始学习率
        self.unfreeze_lr = 1e-4

    def __setattr__(self, key, value):
        _key = '_{}'.format(key)
        if key not in self.__dict__ and _key in self.__dict__:
            self.__dict__[_key] = value
        else:
            self.__dict__[key] = value

    @classmethod
    def make_dir(cls, path):
        if not exists(path):
            mkdir(path)

    @classmethod
    def join_and_abspath(cls, path1, path2):
        return abspath(join(path1, path2))

    def inner_abspath(self, filename):
        self.make_dir(self.inner_xyolo_data_dir)
        return self.join_and_abspath(self.inner_xyolo_data_dir, filename)

    def outer_abspath(self, filename):
        self.make_dir(self.outer_xyolo_data_dir)
        return self.join_and_abspath(self.outer_xyolo_data_dir, filename)

    @property
    def pre_training_weights_darknet_path(self):
        return self.inner_abspath(self._pre_training_weights_darknet_path)

    @property
    def pre_training_weights_config_path(self):
        return self.inner_abspath(self._pre_training_weights_config_path)

    @property
    def pre_training_weights_keras_path(self):
        return self.inner_abspath(self._pre_training_weights_keras_path)

    @property
    def anchors_path(self):
        return self.inner_abspath(self._anchors_path)

    @property
    def classes_path(self):
        return self.inner_abspath(self._classes_path)

    @property
    def output_model_path(self):
        return self.outer_abspath(self._output_model_path)

    @property
    def dataset_path(self):
        return self.outer_abspath(self._dataset_path)

    @property
    def tensorboard_log_path(self):
        return self.outer_abspath(self._tensorboard_log_path)

    @property
    def model_path(self):
        """
        Yolo模型默认加载的权重的路径。
        按照 _model_path > output_model_path > pre_training_weights_keras_path 的优先级选择,即:
        如果设置了_model_path,选择_model_path
        否则,如果设置了output_model_path且路径存在,选择output_model_path
        否则,选择pre_training_weights_keras_path
        """
        _model_path = getattr(self, '_model_path', '')
        if _model_path:
            return abspath(_model_path)
        if self._output_model_path and exists(self.output_model_path):
            return self.output_model_path
        return self.pre_training_weights_keras_path

配置项都有注释说明,就不再多讲了。这里我还想提一下 model_path这个 property

model_path决定了 xyolo 在进行检测时,会加载哪个模型文件。它的选择逻辑如下:

按照 _model_path > output_model_path > pre_training_weights_keras_path 的优先级选择,即:

  • 如果设置了_model_path,选择_model_path(3、使用自己的数据训练模型中检测部分就是这种情况)
  • 否则,如果设置了output_model_path且路径存在,选择output_model_path(也就是说,因为设置了_output_model_path,所以假设3、使用自己的数据训练模型中没配置model_path,它在检测时也能够正确加载模型)
  • 否则,选择pre_training_weights_keras_path (也就是转换后的官方预训练模型,即1、使用官方预训练权重,进行目标检测测试 中的情况)

5、yolo 对象的几个常用方法

上面几个检测示例中,我们调用的都是同一个方法:

img = yolo.detect_and_draw_image('./xyolo_data/detect.jpg')
img.show()

它的输入是一个 PIL.Image.Image 类的实例,或者一个表示图片路径的字符串,返回的是检测并画上框框标记的图片对象,也是 PIL.Image.Image 类的实例。

看一下它的代码:

    def detect_and_draw_image(self, image: typing.Union[Image.Image, str], draw_label=True) -> Image.Image:
        """
        在给定图片上做目标检测,并根据检测结果在图片上画出框框和标签

        Args:
            image: 要检测的图片对象(PIL.Image.Image)或路径(str)
            draw_label: 是否需要为框框标注类别和概率

        Returns:
            添加了检测结果的图片对象
        """
        predicted_results = self.detect_image(image)
        img = self.draw_image(image, predicted_results, draw_label=draw_label)
        return img

可以看到,这个方法实际上是调用了两个方法,detect_image用来获取检测结果,draw_image用来在图片上绘制检测信息。

如果需要的话,我们也可以直接调用这两个方法,比如当我只需要获取图片上的目标检测结果时,我只需要调用 detect_image即可,因为我并不关心图片的绘制。

下面附上这两个方法的接口说明:

    def detect_image(self, img: typing.Union[Image.Image, str]) -> typing.List[
            typing.Tuple[str, int, float, int, int, int, int]]:
        """
        在给定图片上做目标检测并返回检测结果

        Args:
            img: 要检测的图片对象(PIL.Image.Image)或路径(str)

        Returns:
            [[类别名称,类别编号,概率,左上角x值,左上角y值,右下角x值,右下角y值],...]
        """
      	pass
  
   
    def draw_image(self, img: typing.Union[Image.Image, str], predicted_results: typing.List[
      typing.Tuple[str, int, float, int, int, int, int]], draw_label=True) -> Image.Image:
        """
        给定一张图片和目标检测结果,将目标检测结果绘制在图片上,并返回绘制后的图片

        Args:
            img: 要检测的图片对象(PIL.Image.Image)或路径(str)
            predicted_results: 目标检测的结果,[[类别名称,类别编号,概率,左上角x值,左上角y值,右下角x值,右下角y值],...]
            draw_label: 是否需要为框框标注类别和概率

        Returns:
            添加了检测结果的图片对象
        """
        pass

后话

ok,本次分享到这里就结束了,感谢大佬们的阅读。

另外,xyolo 包还没有进行过充足的测试(我自己做过使用测试,但很明显,不能覆盖到全部的情况),所以出个什么 bug 也是可以理解的吧(别打我,别打我,抱头)?碰到什么 bug 的话欢迎联系我哈。

PS:

xyolo 最低支持的 TensorFlow 版本是 2.2,更低版本的没有测试和适配,不保证能用。精力有限,所以低版本也不打算适配了,所以如果是低版本导致的问题,我可能就不做处理了,请见谅 ~

最后,如果喜欢这篇文章的话,点个关注点个赞呗 ~

感谢支持 ~


给自己打个小广告 ~

我这两天搞了个技术型微信公众号……
有些丢脸,这么菜还要搞个公众号……
但怎么说呢,我得迈出第一步对吧?

……

说不定哪天我就成大牛了呢?
image.png
如果想和我一起讨论技术,或者想指导指导本菜鸟的话,欢迎各位大佬关注我的公众号 技术小白成长日记 哈 ~

谢谢大佬们 ~

image.png


标题:xyolo?几行Python代码轻松完成yolo3目标检测
作者:AaronJny
地址:https://aaronjny.com/articles/2020/11/08/1604823112022.html