在python程序中使用YOLO v3(基于keras)

首先给大家推荐一下我老师大神的人工智能教学网站。教学不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵黄段子!点这里可以跳转到网站

在python程序中使用YOLO,可以为YOLO添加python接口,也可以把YOLO的网络框架和权重文件转换成keras或pytorch使用的格式,然后再在python程序中调用。这里介绍基于keras的YOLO调用。

完整项目代码下载地址 : https://github.com/dcrmg/yolo3-training-keras-master

1. 生成keras的.h5文件

需要:

  •  1. yolo网络结构配置文件 .cfg ,如 yolov3.cfg
  •  2. yolo训练好的权重文件 .weights ,如 yolov3.weights

 
使用yolo的网络结构配置文件和权重文件转换成keras的.h5文件,转换代码(convert.py):

# coding: utf-8#! /usr/bin/env python"""Reads Darknet config and weights and creates Keras model with TF backend.""" import argparseimport configparserimport ioimport osfrom collections import defaultdict import numpy as npfrom keras import backend as Kfrom keras.layers import (Conv2D, Input, ZeroPadding2D, Add,                          UpSampling2D, MaxPooling2D, Concatenate)from keras.layers.advanced_activations import LeakyReLUfrom keras.layers.normalization import BatchNormalizationfrom keras.models import Modelfrom keras.regularizers import l2from keras.utils.vis_utils import plot_model as plot  parser = argparse.ArgumentParser(description='Darknet To Keras Converter.')parser.add_argument('config_path', help='Path to Darknet cfg file.')parser.add_argument('weights_path', help='Path to Darknet weights file.')parser.add_argument('output_path', help='Path to output Keras model file.') parser.add_argument(    '-p',    '--plot_model',    help='Plot generated Keras model and save as image.',    action='store_true')parser.add_argument(    '-w',    '--weights_only',    help='Save as Keras weights file instead of model file.',    action='store_true') def unique_config_sections(config_file):    """Convert all config sections to have unique names.    Adds unique suffixes to config sections for compability with configparser.    """    section_counters = defaultdict(int)    # output_stream = io.StringIO()    output_stream = io.BytesIO()    with open(config_file) as fin:        for line in fin:            if line.startswith('['):                section = line.strip().strip('[]')                _section = section + '_' + str(section_counters[section])                section_counters[section] += 1                line = line.replace(section, _section)            output_stream.write(line)    output_stream.seek(0)    return output_stream # %%def _main(args):    config_path = os.path.expanduser(args.config_path)    weights_path = os.path.expanduser(args.weights_path)    assert config_path.endswith('.cfg'), '{} is not a .cfg file'.format(        config_path)    assert weights_path.endswith(        '.weights'), '{} is not a .weights file'.format(weights_path)     output_path = os.path.expanduser(args.output_path)    assert output_path.endswith(        '.h5'), 'output path {} is not a .h5 file'.format(output_path)    output_root = os.path.splitext(output_path)[0]     # Load weights and config.    print('Loading weights.')    weights_file = open(weights_path, 'rb')    major, minor, revision = np.ndarray(        shape=(3, ), dtype='int32', buffer=weights_file.read(12))    if (major*10+minor)>=2 and major<1000 and minor<1000:        seen = np.ndarray(shape=(1,), dtype='int64', buffer=weights_file.read(8))    else:        seen = np.ndarray(shape=(1,), dtype='int32', buffer=weights_file.read(4))    print('Weights Header: ', major, minor, revision, seen)     print('Parsing Darknet config.')    unique_config_file = unique_config_sections(config_path)    cfg_parser = configparser.ConfigParser()    cfg_parser.read_file(unique_config_file)     print('Creating Keras model.')    input_layer = Input(shape=(None, None, 3))    prev_layer = input_layer    all_layers = []     weight_decay = float(cfg_parser['net_0']['decay']                         ) if 'net_0' in cfg_parser.sections() else 5e-4    count = 0    out_index = []    for section in cfg_parser.sections():        print('Parsing section {}'.format(section))        if section.startswith('convolutional'):            filters = int(cfg_parser[section]['filters'])            size = int(cfg_parser[section]['size'])            stride = int(cfg_parser[section]['stride'])            pad = int(cfg_parser[section]['pad'])            activation = cfg_parser[section]['activation']            batch_normalize = 'batch_normalize' in cfg_parser[section]             padding = 'same' if pad == 1 and stride == 1 else 'valid'             # Setting weights.            # Darknet serializes convolutional weights as:            # [bias/beta, [gamma, mean, variance], conv_weights]            prev_layer_shape = K.int_shape(prev_layer)             weights_shape = (size, size, prev_layer_shape[-1], filters)            darknet_w_shape = (filters, weights_shape[2], size, size)            weights_size = np.product(weights_shape)             print('conv2d', 'bn'                  if batch_normalize else '  ', activation, weights_shape)             conv_bias = np.ndarray(                shape=(filters, ),                dtype='float32',                buffer=weights_file.read(filters * 4))            count += filters             if batch_normalize:                bn_weights = np.ndarray(                    shape=(3, filters),                    dtype='float32',                    buffer=weights_file.read(filters * 12))                count += 3 * filters                 bn_weight_list = [                    bn_weights[0],  # scale gamma                    conv_bias,  # shift beta                    bn_weights[1],  # running mean                    bn_weights[2]  # running var                ]             conv_weights = np.ndarray(                shape=darknet_w_shape,                dtype='float32',                buffer=weights_file.read(weights_size * 4))            count += weights_size             # DarkNet conv_weights are serialized Caffe-style:            # (out_dim, in_dim, height, width)            # We would like to set these to Tensorflow order:            # (height, width, in_dim, out_dim)            conv_weights = np.transpose(conv_weights, [2, 3, 1, 0])            conv_weights = [conv_weights] if batch_normalize else [                conv_weights, conv_bias            ]             # Handle activation.            act_fn = None            if activation == 'leaky':                pass  # Add advanced activation later.            elif activation != 'linear':                raise ValueError(                    'Unknown activation function `{}` in section {}'.format(                        activation, section))             # Create Conv2D layer            if stride>1:                # Darknet uses left and top padding instead of 'same' mode                prev_layer = ZeroPadding2D(((1,0),(1,0)))(prev_layer)            conv_layer = (Conv2D(                filters, (size, size),                strides=(stride, stride),                kernel_regularizer=l2(weight_decay),                use_bias=not batch_normalize,                weights=conv_weights,                activation=act_fn,                padding=padding))(prev_layer)             if batch_normalize:                conv_layer = (BatchNormalization(                    weights=bn_weight_list))(conv_layer)            prev_layer = conv_layer             if activation == 'linear':                all_layers.append(prev_layer)            elif activation == 'leaky':                act_layer = LeakyReLU(alpha=0.1)(prev_layer)                prev_layer = act_layer                all_layers.append(act_layer)         elif section.startswith('route'):            ids = [int(i) for i in cfg_parser[section]['layers'].split(',')]            layers = [all_layers[i] for i in ids]            if len(layers) > 1:                print('Concatenating route layers:', layers)                concatenate_layer = Concatenate()(layers)                all_layers.append(concatenate_layer)                prev_layer = concatenate_layer            else:                skip_layer = layers[0]  # only one layer to route                all_layers.append(skip_layer)                prev_layer = skip_layer         elif section.startswith('maxpool'):            size = int(cfg_parser[section]['size'])            stride = int(cfg_parser[section]['stride'])            all_layers.append(                MaxPooling2D(                    pool_size=(size, size),                    strides=(stride, stride),                    padding='same')(prev_layer))            prev_layer = all_layers[-1]         elif section.startswith('shortcut'):            index = int(cfg_parser[section]['from'])            activation = cfg_parser[section]['activation']            assert activation == 'linear', 'Only linear activation supported.'            all_layers.append(Add()([all_layers[index], prev_layer]))            prev_layer = all_layers[-1]         elif section.startswith('upsample'):            stride = int(cfg_parser[section]['stride'])            assert stride == 2, 'Only stride=2 supported.'            all_layers.append(UpSampling2D(stride)(prev_layer))            prev_layer = all_layers[-1]         elif section.startswith('yolo'):            out_index.append(len(all_layers)-1)            all_layers.append(None)            prev_layer = all_layers[-1]         elif section.startswith('net'):            pass         else:            raise ValueError(                'Unsupported section header type: {}'.format(section))     # Create and save model.    if len(out_index)==0: out_index.append(len(all_layers)-1)    model = Model(inputs=input_layer, outputs=[all_layers[i] for i in out_index])    print(model.summary())    if args.weights_only:        model.save_weights('{}'.format(output_path))        print('Saved Keras weights to {}'.format(output_path))    else:        model.save('{}'.format(output_path))        print('Saved Keras model to {}'.format(output_path))     # Check to see if all weights have been read.    remaining_weights = len(weights_file.read()) / 4    weights_file.close()    print('Read {} of {} from Darknet weights.'.format(count, count +                                                       remaining_weights))    if remaining_weights > 0:        print('Warning: {} unused weights'.format(remaining_weights))     if args.plot_model:        plot(model, to_file='{}.png'.format(output_root), show_shapes=True)        print('Saved model plot to {}.png'.format(output_root))  if __name__ == '__main__':    _main(parser.parse_args())

使用方法:

python convert.py yolov3.cfg yolov3.weights yolov3.h5

转换过程输出:

Loading weights.('Weights Header: ', 0, 2, 0, array([1600]))Parsing Darknet config.Creating Keras model.Parsing section net_0Parsing section convolutional_0('conv2d', 'bn', u'leaky', (3, 3, 3, 32))…………conv2d_59 (Conv2D)              (None, None, None, 1 18450       leaky_re_lu_58[0][0]             __________________________________________________________________________________________________conv2d_67 (Conv2D)              (None, None, None, 1 9234        leaky_re_lu_65[0][0]             __________________________________________________________________________________________________conv2d_75 (Conv2D)              (None, None, None, 1 4626        leaky_re_lu_72[0][0]             ==================================================================================================Total params: 61,576,342Trainable params: 61,523,734Non-trainable params: 52,608__________________________________________________________________________________________________NoneSaved Keras model to yolov3.h5Read 61576342 of 61576342 from Darknet weights.

执行完成之后在当前目录下生成yolov3.h5文件。

2. python + keras + yolo检测

需要:

  •  1. 类别标签文件yolo_classes.txt ,如有三个分类 dog、cat、tiger 则classes.txt内容为 dog cat tiger(每个类别一行)
  •  2. anchors文件 yolo_anchors.txt
  •  3. 步骤一中生成的 yolov3.h5

注意:yolo_anchors.txt记录了yolov3通过k-means聚类方法产生的9个anchors(锚点)信息,使用的训练图像大小是416×416,如果 你的训练图片大小不一样,这个yolo_anchors.txt文件需要重新生成,最好使用跟作者一样的416×416的图像训练。

检测代码略,请见项目代码。

通过调整YOLO类 self.score 的大小控制检出目标的阈值。

200个样本简单训练了400轮的检测效果:

点这里可以跳转到人工智能网站

发表评论