目标检测数据集下载及算法训练教程

1.带标注数据集免费下载方法

1.1 查询open image v4是否有我们需要的类别

在这个网页的search框处输入类别查询.

1.2 使用OIDv4_ToolKit工具

此工具在文件包中提供了,原地址为GitHub地址.

1.3 按照readme指示,将所需类别写进项目中的classes.txt中,再运行命令即可

如:

python main.py downloader --classes Handgun Shotgun Knife Axe Hammer --type_csv all

2. YOLO V5训练自己的数据集教程

2.1 环境部署

yolov5基于pytorch,在测试时发现与环境内的tensorflow会产生冲突,建议用ANACONDA新建pytorch的独立环境。
打开文件夹里的requirements.txt文件,用ANACONDA手动安装,或

pip install -U -r requirements.txt

目标检测数据集下载及算法训练教程_第1张图片

2.2数据集准备

yolov5使用的是yolo格式的标注文件,内容长这样,第一个数是标签的序号,后面四个是坐标。
目标检测数据集下载及算法训练教程_第2张图片

2.2.1 若按照上述“1.带标注数据集免费下载方法”下载数据集

由于上述数据集自带标注,故只需将下载的数据集格式转换为自己需要的格式,我们提供了转换的脚本voc2yolo.py,实际得根据自己需要更改。例如需将标签labels文件xml格式改为txt格式,将标注annotations文件中voc格式转换为yolo格式等。

2.2.2 若是未标注的数据集

标注工具可选择LabelImg,工具在文件包中有提供,使用转换的脚本voc2yolo.pyvoc格式转换为yolo即可,这部分可参照文件包里LabelImg中的操作手册做。
目标检测数据集下载及算法训练教程_第3张图片
如果有之前标注好的xml文件,可以通过脚本直接转成yolo所需的txt格式
不过在转换完成后记得添加labels文件,标注文件根据序号从labels里面对应标签。

2.3 参数调整

2.3.1 转换数据集格式

标注完后,建立如下目录,目录要求除了images/labels不同外,其他文件夹名必须相同,程序会根据images的路径推算出labels的路径。
目标检测数据集下载及算法训练教程_第4张图片

2.3.2 修改数据集方面的yaml文件

到此数据集准备完毕,按照data/coco.yaml文件格式,添加data/danger_dataset.yaml文件,修改为自己的参数


# Train command: python train.py --data danger_dataset.yaml
# Default dataset location is next to /yolov5:
#   /parent_folder
#     /VOC
#     /yolov5


# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: ../danger_dataset/images/train  
val: ../danger_dataset/images/val/  

# number of classes
nc: 9

# class names
names: [ 'Axe','Barrel','Hammer','Handgun','Knife','Scissors','Shotgun','Sword','Tin_can']

2.3.3 修改网络参数方面的yaml文件

yolov5提供了几种权重供选择,文件包中均有提供。其中5l的性价比最高,适合CV爱好者日常研究;5x效果最好,如果硬件配置低,还可以选用只有27M的5s,我训练的时候选用的是5l
目标检测数据集下载及算法训练教程_第5张图片
修改yolo5l.yaml中数据集类别数及网络结构,将nc改为和你样本库匹配的值

# parameters
nc: 9  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple

# anchors
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

# YOLOv5 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Focus, [64, 3]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 9, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 1, SPP, [1024, [5, 9, 13]]],
   [-1, 3, C3, [1024, False]],  # 9
  ]

# YOLOv5 head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

2.3.4 修改train.py中的一些参数

train.py修改你选用的权重文件名称
根据要求修改epochsbatch-size,就可以开始初步的训练了。

parser.add_argument('--epochs', type=int, default=200)  # 训练的epoch
parser.add_argument('--batch-size', type=int, default=16)  # batch_size 显卡不好的话,就调小点
parser.add_argument('--cfg', type=str, default='models/yolov5l.yaml', help='*.cfg path')
parser.add_argument('--data', type=str, default='data/coco.yaml', help='*.data path')
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='train,test sizes')

2.4 开始训练

直接 python train.py 就Ok了,最终训练结果位于models/last.pt

3.SSD训练自己的数据集

3.1 环境部署

requirements.txt

tensorflow==1.9.0
jupyter==1.0.0
notebook==5.6.0
opencv-python==3.4.2.17
matplotlib==2.2.2
Pillow==5.2.0

3.2 数据集准备

SSD使用的是VOC2007格式的数据集。

3.2.1 VOC2007数据集格式

目标检测数据集下载及算法训练教程_第6张图片
VOC2007格式中共有5个文件夹,而制作自己的数据集只需用到前三个文件夹,所以请事先建好这三个文件夹放入同一文件夹内,同时ImageSets文件夹内包含Main文件夹

  1. JPEGImages:用于存放训练、测试的图片(图片格式最好为.jpg)
  2. Annotations:用于存放.xml格式的文件,也就是图片对应的标签,每个.xml文件都对应于JPEGImages文件夹的一张图片
  3. ImageSets:内含Main文件夹,在…/ImageSets/Main文件夹下包含test.txttrain.txtval.txttrainval.txt四个文件,生成的方式下一步有详细说明

3.2.2 制作自己的数据集

第一步:下载原始图片,存入JPEGImages文件夹,命名格式统一为"00xxxx.jpg",如下图:
目标检测数据集下载及算法训练教程_第7张图片
第二步:使用LabelImg工具给图片打标签,这个工具在包里在该安装包里也有提供,并且附带详细的操作说明,简化步骤如下,注意标签不能有中文。
labelImg工具简单的使用步骤就是:

  1. 打开单个文件,或者打开一个图片文件夹
  2. 给目标物体建立box边框
  3. 对box边框内的物体贴上标签
  4. 把一张图片内所有目标物都打上各自标签后,再保存生成.xml文件,注意存入Annotations文件夹,文件名也要与当前图片保存一致
  5. 然后next下一张图片继续打标签,直到所有图片内物体都打上了标签,最后exit
    目标检测数据集下载及算法训练教程_第8张图片
    第三步:生成ImageSets/Main文件夹下的.txt文件
    在主目录下运行以下代码既可生成test.txttrain.txtval.txttrainval.txt四个文件,请注意每一个path地址是否正确
# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing  CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/18; 15:19
# -*- python3.5
import os  
import random  

trainval_percent = 0.7  
train_percent = 0.8
xmlfilepath = 'Annotations/'
txtsavepath = 'ImageSets/Main'  
total_xml = os.listdir(xmlfilepath)  

num = len(total_xml)  
list = range(num)  
tv = int(num*trainval_percent)  
tr = int(tv*train_percent)  
trainval = random.sample(list,tv)  
train = random.sample(trainval,tr)  

ftrainval = open(txtsavepath+'/trainval.txt', 'w')  
ftest = open(txtsavepath+'/test.txt', 'w')  
ftrain = open(txtsavepath+'/train.txt', 'w')  
fval = open(txtsavepath+'/val.txt', 'w')  

for i in list:  
    name = total_xml[i][:-4]+'\n'  
    if i in trainval:  
        ftrainval.write(name)  
        if i in train:  
            ftrain.write(name)  
        else:  
            fval.write(name)  
    else:  
        ftest.write(name)  

ftrainval.close()  
ftrain.close()  
fval.close()  
ftest .close()
print('Well Done!!!')

运行完成,得到如下文件:可以打开看一看,内容就是各个图片的索引,意味着哪些图片用做训练,哪些用做测试。

3.2.3 用.xml标签,生成.tfrecord文件

SSD框架所用到的标签文件并不直接是.xml格式文件,而是.tfrecord文件。
第一步:前期准备工作:
在SSD项目主目录中依次创建tfrecords_train_modelVOC2007文件夹,再将上一步制作得到的三个文件夹AnnotationsImageSetsJPEGImages全都拖入VOC2007文件夹内;文件构成如下图所示,需注意的是:

  • 请注意红色框VOCxxx使用的是具体的名字,不过一般都是VOC2007
  • 目录对应的从属关系不要出错
  • tfrecords_文件夹是用来存储.tfrecords文件(后面有程序可以直接生成)
  • train_model文件夹是用来存储模型的记录与参数的
    目标检测数据集下载及算法训练教程_第9张图片
    第二步:生成.tfrecords文件的代码调整
    生成.tfrecord文件的代码如下,下面需要对其进行一些适当的调整:
# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing  CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/17; 13:18
# -*- python3.5
"""
特别注意: 17行VOC_LABELS标签要修改,189行的path地址要正确
"""

import os
import sys
import random
import numpy as np
import tensorflow as tf
import xml.etree.ElementTree as ET

# 我的标签定义只有手表这一类,所以下面的VOC_LABELS要根据自己的图片标签而定,第一组'none': (0, 'Background')是不能删除的;
VOC_LABELS = {
    'none': (0, 'Background'),
    'watch': (1, 'watch')
}

# 图片和标签存放的文件夹.
DIRECTORY_ANNOTATIONS = 'Annotations/'
DIRECTORY_IMAGES = 'JPEGImages/'

# 随机种子.
RANDOM_SEED = 4242
SAMPLES_PER_FILES = 3  # 每个.tfrecords文件包含几个.xml样本



def int64_feature(value):
    """
    生成整数型,浮点型和字符串型的属性
    """
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))


def float_feature(value):
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(float_list=tf.train.FloatList(value=value))


def bytes_feature(value):
    if not isinstance(value, list):
        value = [value]
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))


def _process_image(directory, name):
    """
    图片处理
    """
    # Read the image file.
    filename = directory + DIRECTORY_IMAGES + name + '.jpg'
    image_data = tf.gfile.FastGFile(filename, 'rb').read()

    # Read the XML annotation file.
    filename = os.path.join(directory, DIRECTORY_ANNOTATIONS, name + '.xml')
    tree = ET.parse(filename)
    root = tree.getroot()

    # Image shape.
    size = root.find('size')
    shape = [int(size.find('height').text),
             int(size.find('width').text),
             int(size.find('depth').text)]
    # Find annotations.
    bboxes = []
    labels = []
    labels_text = []
    difficult = []
    truncated = []
    for obj in root.findall('object'):
        label = obj.find('name').text
        labels.append(int(VOC_LABELS[label][0]))
        labels_text.append(label.encode('ascii'))  # 变为ascii格式

        if obj.find('difficult'):
            difficult.append(int(obj.find('difficult').text))
        else:
            difficult.append(0)
        if obj.find('truncated'):
            truncated.append(int(obj.find('truncated').text))
        else:
            truncated.append(0)

        bbox = obj.find('bndbox')
        a = float(bbox.find('ymin').text) / shape[0]
        b = float(bbox.find('xmin').text) / shape[1]
        a1 = float(bbox.find('ymax').text) / shape[0]
        b1 = float(bbox.find('xmax').text) / shape[1]
        a_e = a1 - a
        b_e = b1 - b
        if abs(a_e) < 1 and abs(b_e) < 1:
            bboxes.append((a, b, a1, b1))

    return image_data, shape, bboxes, labels, labels_text, difficult, truncated


def _convert_to_example(image_data, labels, labels_text, bboxes, shape,difficult, truncated):
    """
    转化样例
    """
    xmin = []
    ymin = []
    xmax = []
    ymax = []

    for b in bboxes:
        assert len(b) == 4
        # pylint: disable=expression-not-assigned
        [l.append(point) for l, point in zip([ymin, xmin, ymax, xmax], b)]
        # pylint: enable=expression-not-assigned

    image_format = b'JPEG'
    example = tf.train.Example(features=tf.train.Features(feature={
        'image/height': int64_feature(shape[0]),
        'image/width': int64_feature(shape[1]),
        'image/channels': int64_feature(shape[2]),
        'image/shape': int64_feature(shape),
        'image/object/bbox/xmin': float_feature(xmin),
        'image/object/bbox/xmax': float_feature(xmax),
        'image/object/bbox/ymin': float_feature(ymin),
        'image/object/bbox/ymax': float_feature(ymax),
        'image/object/bbox/label': int64_feature(labels),
        'image/object/bbox/label_text': bytes_feature(labels_text),
        'image/object/bbox/difficult': int64_feature(difficult),
        'image/object/bbox/truncated': int64_feature(truncated),
        'image/format': bytes_feature(image_format),
        'image/encoded': bytes_feature(image_data)}))
    return example


def _add_to_tfrecord(dataset_dir, name, tfrecord_writer):
    """
    增加到tfrecord
    """
    image_data, shape, bboxes, labels, labels_text, difficult, truncated = \
        _process_image(dataset_dir, name)
    example = _convert_to_example(image_data, labels, labels_text,
                                  bboxes, shape, difficult, truncated)
    tfrecord_writer.write(example.SerializeToString())


def _get_output_filename(output_dir, name, idx):
    """
    name为转化文件的前缀
    """
    return '%s/%s_%03d.tfrecord' % (output_dir, name, idx)


def run(dataset_dir, output_dir, name='voc_train', shuffling=False):
    if not tf.gfile.Exists(dataset_dir):
        tf.gfile.MakeDirs(dataset_dir)

    path = os.path.join(dataset_dir, DIRECTORY_ANNOTATIONS)
    filenames = sorted(os.listdir(path))
    if shuffling:
        random.seed(RANDOM_SEED)
        random.shuffle(filenames)

    i = 0
    fidx = 0
    while i < len(filenames):
        # Open new TFRecord file.
        tf_filename = _get_output_filename(output_dir, name, fidx)
        with tf.python_io.TFRecordWriter(tf_filename) as tfrecord_writer:
            j = 0
            while i < len(filenames) and j < SAMPLES_PER_FILES:
                sys.stdout.write(' Converting image %d/%d \n' % (i + 1, len(filenames)))  # 终端打印,类似print
                sys.stdout.flush()  # 缓冲

                filename = filenames[i]
                img_name = filename[:-4]
                _add_to_tfrecord(dataset_dir, img_name, tfrecord_writer)
                i += 1
                j += 1
            fidx += 1

    print('\nFinished converting the Pascal VOC dataset!')


def main(_):
    # 原数据集路径,输出路径以及输出文件名,要根据自己实际做改动
    dataset_dir = "../VOC2007_test/"
    output_dir = "tfrecords_/"
    if not os.path.exists(output_dir):
        os.mkdir(output_dir)

    run(dataset_dir, output_dir)


if __name__ == '__main__':
    tf.app.run()

1.修改标签项——打开datasets文件夹中pascalvoc_common.py文件,将自己的标签项填入。我之前做的图片标签.xml文件中,就只有一个标签项“watch”,所以要根据你自己数据集实际情况进行修改;
2.修改读取个数、读取方式——打开datasets文件夹中的pascalvoc_to_tfrecords.py文件,

  • 修改67行SAMPLES_PER_FILES的个数;
  • 修改83行读取方式为’rb’;
  • 如果你的文件不是.jpg格式,也可以修改图片的类型;
    目标检测数据集下载及算法训练教程_第10张图片
    第三步:生成.tfrecords文件
    打开tf_convert_data.py文件,依次点击:runEdit Configuration,在Parameters中填入以下内容,再运行tf_convert_data.py文件,在面板中得到成功信息,可以在tfrecords_文件夹下看到生成的.tfrecords文件;
--dataset_name=pascalvoc
--dataset_dir=./VOC2007/
--output_name=voc_2007_train
--output_dir=./tfrecords_

目标检测数据集下载及算法训练教程_第11张图片
得到的.tfrecords文件如下:
目标检测数据集下载及算法训练教程_第12张图片

3.3 模型代码参数调整

3.3.1 修改训练数据shape

打开datasets文件夹中的pascalvoc_2007.py文件,
根据自己训练数据修改:NUM_CLASSES = 类别数
目标检测数据集下载及算法训练教程_第13张图片

3.3.2 修改类别个数

打开nets文件夹中的ssd_vgg_300.py文件,
根据自己训练类别数修改96 和97行:等于类别数+1

3.3.3 修改训练步数epoch

打开train_ssd_network.py文件,

  • 修改27行的数据格式,改为'NHWC'
  • 修改135行的类别个数:等于类别数+1;
  • 修改154行训练总步数,None会无限训练下去;
  • 说明:60行、63行是关于模型保存的参数;

3.4 开始训练

第一步:下载vgg_16模型,下载完成解压后存入checkpoint文件中,这个文件已经准备在了SSD工具包中;
第二步:重新训练模型。打开train_ssd_network.py文件,依次点击:runEdit Configuration,在Parameters中填入以下内容,再运行train_ssd_network.py文件。

--train_dir=./train_model/
--dataset_dir=./tfrecords_/
--dataset_name=pascalvoc_2007
--dataset_split_name=train
--model_name=ssd_300_vgg
--checkpoint_path=./checkpoints/vgg_16.ckpt
--checkpoint_model_scope=vgg_16
--checkpoint_exclude_scopes=ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box
--trainable_scopes=ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box
--save_summaries_secs=60
--save_interval_secs=100
--weight_decay=0.0005
--optimizer=adam
--learning_rate=0.001
--learning_rate_decay_factor=0.94
--batch_size=4
--gpu_memory_fraction=0.7

注意:上面是输入参数:

  1. --save_interval_secs是训练多少次保存参数的步长;
  2. --optimizer是优化器;
  3. --learning_rate是学习率;
  4. --learning_rate_decay_factor是学习率衰减因子;
  5. 如果你的机器比较强大,可以适当增大--batch_size的数值,以及调高GPU的占比--gpu_memory_fraction
  6. --model_name:我并没有尝试使用其他的模型做增量训练
    若得到下图日志,即说明模型开始训练:
    目标检测数据集下载及算法训练教程_第14张图片训练结束可以在train_model文件夹下看到生成的参数文件;
    目标检测数据集下载及算法训练教程_第15张图片

3.5 模型预测

1、在日志中,选取最后一次生成模型作为测试模型进行测试;
2、在demo文件夹下放入测试图片;
3、最后在notebooks文件夹下建立demo_test.py测试文件,代码如下:
4、注意第48行,导入的新模型的名称是否正确;

# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing  CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/20; 15:19
# -*- python3.6
import os
import math
import random
import numpy as np
import tensorflow as tf
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from nets import ssd_vgg_300, ssd_common, np_methods
from preprocessing import ssd_vgg_preprocessing
from notebooks import visualization
import sys

sys.path.append('../')
slim = tf.contrib.slim
# TensorFlow session: grow memory when needed. TF, DO NOT USE ALL MY GPU MEMORY!!!
gpu_options = tf.GPUOptions(allow_growth=True)
config = tf.ConfigProto(log_device_placement=False, gpu_options=gpu_options)
isess = tf.InteractiveSession(config=config)


# 定义数据格式,设置占位符
net_shape = (300, 300)
# 输入图像的通道排列形式,'NHWC'表示 [batch_size,height,width,channel]
data_format = 'NHWC'
# 预处理,以Tensorflow backend, 将输入图片大小改成 300x300,作为下一步输入
img_input = tf.placeholder(tf.uint8, shape=(None, None, 3))
# 数据预处理,将img_input输入的图像resize为300大小,labels_pre,bboxes_pre,bbox_img待解析
image_pre, labels_pre, bboxes_pre, bbox_img = ssd_vgg_preprocessing.preprocess_for_eval(
    img_input, None, None, net_shape, data_format, resize=ssd_vgg_preprocessing.Resize.WARP_RESIZE)
# 拓展为4维变量用于输入
image_4d = tf.expand_dims(image_pre, 0)

# 定义SSD模型
# 是否复用,目前我们没有在训练所以为None
reuse = True if 'ssd_net' in locals() else None
# 调出基于VGG神经网络的SSD模型对象,注意这是一个自定义类对象
ssd_net = ssd_vgg_300.SSDNet()
# 得到预测类和预测坐标的Tensor对象,这两个就是神经网络模型的计算流程
with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)):
    predictions, localisations, _, _ = ssd_net.net(image_4d, is_training=False, reuse=reuse)

# 导入新训练的模型参数
ckpt_filename = '../train_model/model.ckpt-xxx'   # 注意xxx代表的数字是否和文件夹下的一致
# ckpt_filename = '../checkpoints/VGG_VOC0712_SSD_300x300_ft_iter_120000.ckpt'
isess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.restore(isess, ckpt_filename)

# 在网络模型结构中,提取搜索网格的位置
# 根据模型超参数,得到每个特征层(这里用了6个特征层,分别是4,7,8,9,10,11)的anchors_boxes
ssd_anchors = ssd_net.anchors(net_shape)
"""
每层的anchors_boxes包含4个arrayList,前两个List分别是该特征层下x,y坐标轴对于原图(300x300)大小的映射
第三,四个List为anchor_box的长度和宽度,同样是经过归一化映射的,根据每个特征层box数量的不同,这两个List元素
个数会变化。其中,长宽的值根据超参数anchor_sizes和anchor_ratios制定。
"""


# 主流程函数
def process_image(img, select_threshold=0.6, nms_threshold=.01, net_shape=(300, 300)):
    # select_threshold:box阈值——每个像素的box分类预测数据的得分会与box阈值比较,高于一个box阈值则认为这个box成功框到了一个对象
    # nms_threshold:重合度阈值——同一对象的两个框的重合度高于该阈值,则运行下面去重函数

    # 执行SSD模型,得到4维输入变量,分类预测,坐标预测,rbbox_img参数为最大检测范围,本文固定为[0,0,1,1]即全图
    rimg, rpredictions, rlocalisations, rbbox_img = isess.run([image_4d, predictions, localisations, bbox_img],
                                                              feed_dict={img_input: img})

    # ssd_bboxes_select()函数根据每个特征层的分类预测分数,归一化后的映射坐标,
    # ancohor_box的大小,通过设定一个阈值计算得到每个特征层检测到的对象以及其分类和坐标
    rclasses, rscores, rbboxes = np_methods.ssd_bboxes_select(
        rpredictions, rlocalisations, ssd_anchors,
        select_threshold=select_threshold, img_shape=net_shape, num_classes=21, decode=True)
    """
    这个函数做的事情比较多,这里说的细致一些:
    首先是输入,输入的数据为每个特征层(一共6个,见上文)的:
                                                rpredictions: 分类预测数据,
                                                rlocalisations: 坐标预测数据,
                                                ssd_anchors: anchors_box数据
                                            其中:
                                               分类预测数据为当前特征层中每个像素的每个box的分类预测
                                               坐标预测数据为当前特征层中每个像素的每个box的坐标预测
                                               anchors_box数据为当前特征层中每个像素的每个box的修正数据

        函数根据坐标预测数据和anchors_box数据,计算得到每个像素的每个box的中心和长宽,这个中心坐标和长宽会根据一个算法进行些许的修正,
    从而得到一个更加准确的box坐标;修正的算法会在后文中详细解释,如果只是为了理解算法流程也可以不必深究这个,因为这个修正算法属于经验算
    法,并没有太多逻辑可循。
        修正完box和中心后,函数会计算每个像素的每个box的分类预测数据的得分,当这个分数高于一个阈值(这里是0.5)则认为这个box成功
    框到了一个对象,然后将这个box的坐标数据,所属分类和分类得分导出,从而得到:
        rclasses:所属分类
        rscores:分类得分
        rbboxes:坐标
        
        最后要注意的是,同一个目标可能会在不同的特征层都被检测到,并且他们的box坐标会有些许不同,这里并没有去掉重复的目标,而是在下文
    中专门用了一个函数来去重
    """

    # 检测有没有超出检测边缘
    rbboxes = np_methods.bboxes_clip(rbbox_img, rbboxes)
    rclasses, rscores, rbboxes = np_methods.bboxes_sort(rclasses, rscores, rbboxes, top_k=400)
    # 去重,将重复检测到的目标去掉
    rclasses, rscores, rbboxes = np_methods.bboxes_nms(rclasses, rscores, rbboxes, nms_threshold=nms_threshold)
    # 将box的坐标重新映射到原图上(上文所有的坐标都进行了归一化,所以要逆操作一次)
    rbboxes = np_methods.bboxes_resize(rbbox_img, rbboxes)
    return rclasses, rscores, rbboxes
    
# 测试的文件夹
path = '../demo/'
image_names = sorted(os.listdir(path))
# 文件夹中的第几张图,-1代表最后一张
img = mpimg.imread(path + image_names[-1])
rclasses, rscores, rbboxes = process_image(img)

# visualization.bboxes_draw_on_img(img, rclasses, rscores, rbboxes, visualization.colors_plasma)
visualization.plt_bboxes(img, rclasses, rscores, rbboxes)

若最终测试结果不理想,解决的办法可能有两个:

  1. 训练次数太少,loss过高。解决方法除了优化数据集外,就是增大训练次数;
  2. 另外上面程序65行的select_thresholdnms_threshold参数你也可以做调整。

你可能感兴趣的:(学Python不秃头,目标检测,算法)