[
  {
    "path": ".gitignore",
    "content": "*.jpg\n*.png\n*.weights\n*.h5\nlogs/\n*_test.py\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# dotenv\n.env\n\n# virtualenv\n.venv\nvenv/\nENV/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 qqwweee\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# keras-yolo3\n\n[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)\n\n## Introduction\n\nA Keras implementation of YOLOv3 (Tensorflow backend) inspired by [allanzelener/YAD2K](https://github.com/allanzelener/YAD2K).\n\n\n---\n\n## Quick Start\n\n1. Download YOLOv3 weights from [YOLO website](http://pjreddie.com/darknet/yolo/).\n2. Convert the Darknet YOLO model to a Keras model.\n3. Run YOLO detection.\n\n```\nwget https://pjreddie.com/media/files/yolov3.weights\npython convert.py yolov3.cfg yolov3.weights model_data/yolo.h5\npython yolo_video.py [OPTIONS...] --image, for image detection mode, OR\npython yolo_video.py [video_path] [output_path (optional)]\n```\n\nFor Tiny YOLOv3, just do in a similar way, just specify model path and anchor path with `--model model_file` and `--anchors anchor_file`.\n\n### Usage\nUse --help to see usage of yolo_video.py:\n```\nusage: yolo_video.py [-h] [--model MODEL] [--anchors ANCHORS]\n                     [--classes CLASSES] [--gpu_num GPU_NUM] [--image]\n                     [--input] [--output]\n\npositional arguments:\n  --input        Video input path\n  --output       Video output path\n\noptional arguments:\n  -h, --help         show this help message and exit\n  --model MODEL      path to model weight file, default model_data/yolo.h5\n  --anchors ANCHORS  path to anchor definitions, default\n                     model_data/yolo_anchors.txt\n  --classes CLASSES  path to class definitions, default\n                     model_data/coco_classes.txt\n  --gpu_num GPU_NUM  Number of GPU to use, default 1\n  --image            Image detection mode, will ignore all positional arguments\n```\n---\n\n4. MultiGPU usage: use `--gpu_num N` to use N GPUs. It is passed to the [Keras multi_gpu_model()](https://keras.io/utils/#multi_gpu_model).\n\n## Training\n\n1. Generate your own annotation file and class names file.  \n    One row for one image;  \n    Row format: `image_file_path box1 box2 ... boxN`;  \n    Box format: `x_min,y_min,x_max,y_max,class_id` (no space).  \n    For VOC dataset, try `python voc_annotation.py`  \n    Here is an example:\n    ```\n    path/to/img1.jpg 50,100,150,200,0 30,50,200,120,3\n    path/to/img2.jpg 120,300,250,600,2\n    ...\n    ```\n\n2. Make sure you have run `python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5`  \n    The file model_data/yolo_weights.h5 is used to load pretrained weights.\n\n3. Modify train.py and start training.  \n    `python train.py`  \n    Use your trained weights or checkpoint weights with command line option `--model model_file` when using yolo_video.py\n    Remember to modify class path or anchor path, with `--classes class_file` and `--anchors anchor_file`.\n\nIf you want to use original pretrained weights for YOLOv3:  \n    1. `wget https://pjreddie.com/media/files/darknet53.conv.74`  \n    2. rename it as darknet53.weights  \n    3. `python convert.py -w darknet53.cfg darknet53.weights model_data/darknet53_weights.h5`  \n    4. use model_data/darknet53_weights.h5 in train.py\n\n---\n\n## Some issues to know\n\n1. The test environment is\n    - Python 3.5.2\n    - Keras 2.1.5\n    - tensorflow 1.6.0\n\n2. Default anchors are used. If you use your own anchors, probably some changes are needed.\n\n3. The inference result is not totally the same as Darknet but the difference is small.\n\n4. The speed is slower than Darknet. Replacing PIL with opencv may help a little.\n\n5. Always load pretrained weights and freeze layers in the first stage of training. Or try Darknet training. It's OK if there is a mismatch warning.\n\n6. The training strategy is for reference only. Adjust it according to your dataset and your goal. And add further strategy if needed.\n\n7. For speeding up the training process with frozen layers train_bottleneck.py can be used. It will compute the bottleneck features of the frozen model first and then only trains the last layers. This makes training on CPU possible in a reasonable time. See [this](https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html) for more information on bottleneck features.\n"
  },
  {
    "path": "coco_annotation.py",
    "content": "import json\nfrom collections import defaultdict\n\nname_box_id = defaultdict(list)\nid_name = dict()\nf = open(\n    \"mscoco2017/annotations/instances_train2017.json\",\n    encoding='utf-8')\ndata = json.load(f)\n\nannotations = data['annotations']\nfor ant in annotations:\n    id = ant['image_id']\n    name = 'mscoco2017/train2017/%012d.jpg' % id\n    cat = ant['category_id']\n\n    if cat >= 1 and cat <= 11:\n        cat = cat - 1\n    elif cat >= 13 and cat <= 25:\n        cat = cat - 2\n    elif cat >= 27 and cat <= 28:\n        cat = cat - 3\n    elif cat >= 31 and cat <= 44:\n        cat = cat - 5\n    elif cat >= 46 and cat <= 65:\n        cat = cat - 6\n    elif cat == 67:\n        cat = cat - 7\n    elif cat == 70:\n        cat = cat - 9\n    elif cat >= 72 and cat <= 82:\n        cat = cat - 10\n    elif cat >= 84 and cat <= 90:\n        cat = cat - 11\n\n    name_box_id[name].append([ant['bbox'], cat])\n\nf = open('train.txt', 'w')\nfor key in name_box_id.keys():\n    f.write(key)\n    box_infos = name_box_id[key]\n    for info in box_infos:\n        x_min = int(info[0][0])\n        y_min = int(info[0][1])\n        x_max = x_min + int(info[0][2])\n        y_max = y_min + int(info[0][3])\n\n        box_info = \" %d,%d,%d,%d,%d\" % (\n            x_min, y_min, x_max, y_max, int(info[1]))\n        f.write(box_info)\n    f.write('\\n')\nf.close()\n"
  },
  {
    "path": "convert.py",
    "content": "#! /usr/bin/env python\n\"\"\"\nReads Darknet config and weights and creates Keras model with TF backend.\n\n\"\"\"\n\nimport argparse\nimport configparser\nimport io\nimport os\nfrom collections import defaultdict\n\nimport numpy as np\nfrom keras import backend as K\nfrom keras.layers import (Conv2D, Input, ZeroPadding2D, Add,\n                          UpSampling2D, MaxPooling2D, Concatenate)\nfrom keras.layers.advanced_activations import LeakyReLU\nfrom keras.layers.normalization import BatchNormalization\nfrom keras.models import Model\nfrom keras.regularizers import l2\nfrom keras.utils.vis_utils import plot_model as plot\n\n\nparser = argparse.ArgumentParser(description='Darknet To Keras Converter.')\nparser.add_argument('config_path', help='Path to Darknet cfg file.')\nparser.add_argument('weights_path', help='Path to Darknet weights file.')\nparser.add_argument('output_path', help='Path to output Keras model file.')\nparser.add_argument(\n    '-p',\n    '--plot_model',\n    help='Plot generated Keras model and save as image.',\n    action='store_true')\nparser.add_argument(\n    '-w',\n    '--weights_only',\n    help='Save as Keras weights file instead of model file.',\n    action='store_true')\n\ndef unique_config_sections(config_file):\n    \"\"\"Convert all config sections to have unique names.\n\n    Adds unique suffixes to config sections for compability with configparser.\n    \"\"\"\n    section_counters = defaultdict(int)\n    output_stream = io.StringIO()\n    with open(config_file) as fin:\n        for line in fin:\n            if line.startswith('['):\n                section = line.strip().strip('[]')\n                _section = section + '_' + str(section_counters[section])\n                section_counters[section] += 1\n                line = line.replace(section, _section)\n            output_stream.write(line)\n    output_stream.seek(0)\n    return output_stream\n\n# %%\ndef _main(args):\n    config_path = os.path.expanduser(args.config_path)\n    weights_path = os.path.expanduser(args.weights_path)\n    assert config_path.endswith('.cfg'), '{} is not a .cfg file'.format(\n        config_path)\n    assert weights_path.endswith(\n        '.weights'), '{} is not a .weights file'.format(weights_path)\n\n    output_path = os.path.expanduser(args.output_path)\n    assert output_path.endswith(\n        '.h5'), 'output path {} is not a .h5 file'.format(output_path)\n    output_root = os.path.splitext(output_path)[0]\n\n    # Load weights and config.\n    print('Loading weights.')\n    weights_file = open(weights_path, 'rb')\n    major, minor, revision = np.ndarray(\n        shape=(3, ), dtype='int32', buffer=weights_file.read(12))\n    if (major*10+minor)>=2 and major<1000 and minor<1000:\n        seen = np.ndarray(shape=(1,), dtype='int64', buffer=weights_file.read(8))\n    else:\n        seen = np.ndarray(shape=(1,), dtype='int32', buffer=weights_file.read(4))\n    print('Weights Header: ', major, minor, revision, seen)\n\n    print('Parsing Darknet config.')\n    unique_config_file = unique_config_sections(config_path)\n    cfg_parser = configparser.ConfigParser()\n    cfg_parser.read_file(unique_config_file)\n\n    print('Creating Keras model.')\n    input_layer = Input(shape=(None, None, 3))\n    prev_layer = input_layer\n    all_layers = []\n\n    weight_decay = float(cfg_parser['net_0']['decay']\n                         ) if 'net_0' in cfg_parser.sections() else 5e-4\n    count = 0\n    out_index = []\n    for section in cfg_parser.sections():\n        print('Parsing section {}'.format(section))\n        if section.startswith('convolutional'):\n            filters = int(cfg_parser[section]['filters'])\n            size = int(cfg_parser[section]['size'])\n            stride = int(cfg_parser[section]['stride'])\n            pad = int(cfg_parser[section]['pad'])\n            activation = cfg_parser[section]['activation']\n            batch_normalize = 'batch_normalize' in cfg_parser[section]\n\n            padding = 'same' if pad == 1 and stride == 1 else 'valid'\n\n            # Setting weights.\n            # Darknet serializes convolutional weights as:\n            # [bias/beta, [gamma, mean, variance], conv_weights]\n            prev_layer_shape = K.int_shape(prev_layer)\n\n            weights_shape = (size, size, prev_layer_shape[-1], filters)\n            darknet_w_shape = (filters, weights_shape[2], size, size)\n            weights_size = np.product(weights_shape)\n\n            print('conv2d', 'bn'\n                  if batch_normalize else '  ', activation, weights_shape)\n\n            conv_bias = np.ndarray(\n                shape=(filters, ),\n                dtype='float32',\n                buffer=weights_file.read(filters * 4))\n            count += filters\n\n            if batch_normalize:\n                bn_weights = np.ndarray(\n                    shape=(3, filters),\n                    dtype='float32',\n                    buffer=weights_file.read(filters * 12))\n                count += 3 * filters\n\n                bn_weight_list = [\n                    bn_weights[0],  # scale gamma\n                    conv_bias,  # shift beta\n                    bn_weights[1],  # running mean\n                    bn_weights[2]  # running var\n                ]\n\n            conv_weights = np.ndarray(\n                shape=darknet_w_shape,\n                dtype='float32',\n                buffer=weights_file.read(weights_size * 4))\n            count += weights_size\n\n            # DarkNet conv_weights are serialized Caffe-style:\n            # (out_dim, in_dim, height, width)\n            # We would like to set these to Tensorflow order:\n            # (height, width, in_dim, out_dim)\n            conv_weights = np.transpose(conv_weights, [2, 3, 1, 0])\n            conv_weights = [conv_weights] if batch_normalize else [\n                conv_weights, conv_bias\n            ]\n\n            # Handle activation.\n            act_fn = None\n            if activation == 'leaky':\n                pass  # Add advanced activation later.\n            elif activation != 'linear':\n                raise ValueError(\n                    'Unknown activation function `{}` in section {}'.format(\n                        activation, section))\n\n            # Create Conv2D layer\n            if stride>1:\n                # Darknet uses left and top padding instead of 'same' mode\n                prev_layer = ZeroPadding2D(((1,0),(1,0)))(prev_layer)\n            conv_layer = (Conv2D(\n                filters, (size, size),\n                strides=(stride, stride),\n                kernel_regularizer=l2(weight_decay),\n                use_bias=not batch_normalize,\n                weights=conv_weights,\n                activation=act_fn,\n                padding=padding))(prev_layer)\n\n            if batch_normalize:\n                conv_layer = (BatchNormalization(\n                    weights=bn_weight_list))(conv_layer)\n            prev_layer = conv_layer\n\n            if activation == 'linear':\n                all_layers.append(prev_layer)\n            elif activation == 'leaky':\n                act_layer = LeakyReLU(alpha=0.1)(prev_layer)\n                prev_layer = act_layer\n                all_layers.append(act_layer)\n\n        elif section.startswith('route'):\n            ids = [int(i) for i in cfg_parser[section]['layers'].split(',')]\n            layers = [all_layers[i] for i in ids]\n            if len(layers) > 1:\n                print('Concatenating route layers:', layers)\n                concatenate_layer = Concatenate()(layers)\n                all_layers.append(concatenate_layer)\n                prev_layer = concatenate_layer\n            else:\n                skip_layer = layers[0]  # only one layer to route\n                all_layers.append(skip_layer)\n                prev_layer = skip_layer\n\n        elif section.startswith('maxpool'):\n            size = int(cfg_parser[section]['size'])\n            stride = int(cfg_parser[section]['stride'])\n            all_layers.append(\n                MaxPooling2D(\n                    pool_size=(size, size),\n                    strides=(stride, stride),\n                    padding='same')(prev_layer))\n            prev_layer = all_layers[-1]\n\n        elif section.startswith('shortcut'):\n            index = int(cfg_parser[section]['from'])\n            activation = cfg_parser[section]['activation']\n            assert activation == 'linear', 'Only linear activation supported.'\n            all_layers.append(Add()([all_layers[index], prev_layer]))\n            prev_layer = all_layers[-1]\n\n        elif section.startswith('upsample'):\n            stride = int(cfg_parser[section]['stride'])\n            assert stride == 2, 'Only stride=2 supported.'\n            all_layers.append(UpSampling2D(stride)(prev_layer))\n            prev_layer = all_layers[-1]\n\n        elif section.startswith('yolo'):\n            out_index.append(len(all_layers)-1)\n            all_layers.append(None)\n            prev_layer = all_layers[-1]\n\n        elif section.startswith('net'):\n            pass\n\n        else:\n            raise ValueError(\n                'Unsupported section header type: {}'.format(section))\n\n    # Create and save model.\n    if len(out_index)==0: out_index.append(len(all_layers)-1)\n    model = Model(inputs=input_layer, outputs=[all_layers[i] for i in out_index])\n    print(model.summary())\n    if args.weights_only:\n        model.save_weights('{}'.format(output_path))\n        print('Saved Keras weights to {}'.format(output_path))\n    else:\n        model.save('{}'.format(output_path))\n        print('Saved Keras model to {}'.format(output_path))\n\n    # Check to see if all weights have been read.\n    remaining_weights = len(weights_file.read()) / 4\n    weights_file.close()\n    print('Read {} of {} from Darknet weights.'.format(count, count +\n                                                       remaining_weights))\n    if remaining_weights > 0:\n        print('Warning: {} unused weights'.format(remaining_weights))\n\n    if args.plot_model:\n        plot(model, to_file='{}.png'.format(output_root), show_shapes=True)\n        print('Saved model plot to {}.png'.format(output_root))\n\n\nif __name__ == '__main__':\n    _main(parser.parse_args())\n"
  },
  {
    "path": "darknet53.cfg",
    "content": "[net]\n# Testing\nbatch=1\nsubdivisions=1\n# Training\n# batch=64\n# subdivisions=16\nwidth=416\nheight=416\nchannels=3\nmomentum=0.9\ndecay=0.0005\nangle=0\nsaturation = 1.5\nexposure = 1.5\nhue=.1\n\nlearning_rate=0.001\nburn_in=1000\nmax_batches = 500200\npolicy=steps\nsteps=400000,450000\nscales=.1,.1\n\n[convolutional]\nbatch_normalize=1\nfilters=32\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=32\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n"
  },
  {
    "path": "font/SIL Open Font License.txt",
    "content": "Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ with Reserved Font Name Fira Mono.\n\nCopyright (c) 2014, Telefonica S.A.\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the copyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as distributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.\n\n5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are not met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE."
  },
  {
    "path": "kmeans.py",
    "content": "import numpy as np\n\n\nclass YOLO_Kmeans:\n\n    def __init__(self, cluster_number, filename):\n        self.cluster_number = cluster_number\n        self.filename = \"2012_train.txt\"\n\n    def iou(self, boxes, clusters):  # 1 box -> k clusters\n        n = boxes.shape[0]\n        k = self.cluster_number\n\n        box_area = boxes[:, 0] * boxes[:, 1]\n        box_area = box_area.repeat(k)\n        box_area = np.reshape(box_area, (n, k))\n\n        cluster_area = clusters[:, 0] * clusters[:, 1]\n        cluster_area = np.tile(cluster_area, [1, n])\n        cluster_area = np.reshape(cluster_area, (n, k))\n\n        box_w_matrix = np.reshape(boxes[:, 0].repeat(k), (n, k))\n        cluster_w_matrix = np.reshape(np.tile(clusters[:, 0], (1, n)), (n, k))\n        min_w_matrix = np.minimum(cluster_w_matrix, box_w_matrix)\n\n        box_h_matrix = np.reshape(boxes[:, 1].repeat(k), (n, k))\n        cluster_h_matrix = np.reshape(np.tile(clusters[:, 1], (1, n)), (n, k))\n        min_h_matrix = np.minimum(cluster_h_matrix, box_h_matrix)\n        inter_area = np.multiply(min_w_matrix, min_h_matrix)\n\n        result = inter_area / (box_area + cluster_area - inter_area)\n        return result\n\n    def avg_iou(self, boxes, clusters):\n        accuracy = np.mean([np.max(self.iou(boxes, clusters), axis=1)])\n        return accuracy\n\n    def kmeans(self, boxes, k, dist=np.median):\n        box_number = boxes.shape[0]\n        distances = np.empty((box_number, k))\n        last_nearest = np.zeros((box_number,))\n        np.random.seed()\n        clusters = boxes[np.random.choice(\n            box_number, k, replace=False)]  # init k clusters\n        while True:\n\n            distances = 1 - self.iou(boxes, clusters)\n\n            current_nearest = np.argmin(distances, axis=1)\n            if (last_nearest == current_nearest).all():\n                break  # clusters won't change\n            for cluster in range(k):\n                clusters[cluster] = dist(  # update clusters\n                    boxes[current_nearest == cluster], axis=0)\n\n            last_nearest = current_nearest\n\n        return clusters\n\n    def result2txt(self, data):\n        f = open(\"yolo_anchors.txt\", 'w')\n        row = np.shape(data)[0]\n        for i in range(row):\n            if i == 0:\n                x_y = \"%d,%d\" % (data[i][0], data[i][1])\n            else:\n                x_y = \", %d,%d\" % (data[i][0], data[i][1])\n            f.write(x_y)\n        f.close()\n\n    def txt2boxes(self):\n        f = open(self.filename, 'r')\n        dataSet = []\n        for line in f:\n            infos = line.split(\" \")\n            length = len(infos)\n            for i in range(1, length):\n                width = int(infos[i].split(\",\")[2]) - \\\n                    int(infos[i].split(\",\")[0])\n                height = int(infos[i].split(\",\")[3]) - \\\n                    int(infos[i].split(\",\")[1])\n                dataSet.append([width, height])\n        result = np.array(dataSet)\n        f.close()\n        return result\n\n    def txt2clusters(self):\n        all_boxes = self.txt2boxes()\n        result = self.kmeans(all_boxes, k=self.cluster_number)\n        result = result[np.lexsort(result.T[0, None])]\n        self.result2txt(result)\n        print(\"K anchors:\\n {}\".format(result))\n        print(\"Accuracy: {:.2f}%\".format(\n            self.avg_iou(all_boxes, result) * 100))\n\n\nif __name__ == \"__main__\":\n    cluster_number = 9\n    filename = \"2012_train.txt\"\n    kmeans = YOLO_Kmeans(cluster_number, filename)\n    kmeans.txt2clusters()\n"
  },
  {
    "path": "model_data/coco_classes.txt",
    "content": "person\nbicycle\ncar\nmotorbike\naeroplane\nbus\ntrain\ntruck\nboat\ntraffic light\nfire hydrant\nstop sign\nparking meter\nbench\nbird\ncat\ndog\nhorse\nsheep\ncow\nelephant\nbear\nzebra\ngiraffe\nbackpack\numbrella\nhandbag\ntie\nsuitcase\nfrisbee\nskis\nsnowboard\nsports ball\nkite\nbaseball bat\nbaseball glove\nskateboard\nsurfboard\ntennis racket\nbottle\nwine glass\ncup\nfork\nknife\nspoon\nbowl\nbanana\napple\nsandwich\norange\nbroccoli\ncarrot\nhot dog\npizza\ndonut\ncake\nchair\nsofa\npottedplant\nbed\ndiningtable\ntoilet\ntvmonitor\nlaptop\nmouse\nremote\nkeyboard\ncell phone\nmicrowave\noven\ntoaster\nsink\nrefrigerator\nbook\nclock\nvase\nscissors\nteddy bear\nhair drier\ntoothbrush\n"
  },
  {
    "path": "model_data/tiny_yolo_anchors.txt",
    "content": "10,14,  23,27,  37,58,  81,82,  135,169,  344,319\n"
  },
  {
    "path": "model_data/voc_classes.txt",
    "content": "aeroplane\nbicycle\nbird\nboat\nbottle\nbus\ncar\ncat\nchair\ncow\ndiningtable\ndog\nhorse\nmotorbike\nperson\npottedplant\nsheep\nsofa\ntrain\ntvmonitor\n"
  },
  {
    "path": "model_data/yolo_anchors.txt",
    "content": "10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326\n"
  },
  {
    "path": "train.py",
    "content": "\"\"\"\nRetrain the YOLO model for your own dataset.\n\"\"\"\n\nimport numpy as np\nimport keras.backend as K\nfrom keras.layers import Input, Lambda\nfrom keras.models import Model\nfrom keras.optimizers import Adam\nfrom keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\n\nfrom yolo3.model import preprocess_true_boxes, yolo_body, tiny_yolo_body, yolo_loss\nfrom yolo3.utils import get_random_data\n\n\ndef _main():\n    annotation_path = 'train.txt'\n    log_dir = 'logs/000/'\n    classes_path = 'model_data/voc_classes.txt'\n    anchors_path = 'model_data/yolo_anchors.txt'\n    class_names = get_classes(classes_path)\n    num_classes = len(class_names)\n    anchors = get_anchors(anchors_path)\n\n    input_shape = (416,416) # multiple of 32, hw\n\n    is_tiny_version = len(anchors)==6 # default setting\n    if is_tiny_version:\n        model = create_tiny_model(input_shape, anchors, num_classes,\n            freeze_body=2, weights_path='model_data/tiny_yolo_weights.h5')\n    else:\n        model = create_model(input_shape, anchors, num_classes,\n            freeze_body=2, weights_path='model_data/yolo_weights.h5') # make sure you know what you freeze\n\n    logging = TensorBoard(log_dir=log_dir)\n    checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',\n        monitor='val_loss', save_weights_only=True, save_best_only=True, period=3)\n    reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)\n    early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)\n\n    val_split = 0.1\n    with open(annotation_path) as f:\n        lines = f.readlines()\n    np.random.seed(10101)\n    np.random.shuffle(lines)\n    np.random.seed(None)\n    num_val = int(len(lines)*val_split)\n    num_train = len(lines) - num_val\n\n    # Train with frozen layers first, to get a stable loss.\n    # Adjust num epochs to your dataset. This step is enough to obtain a not bad model.\n    if True:\n        model.compile(optimizer=Adam(lr=1e-3), loss={\n            # use custom yolo_loss Lambda layer.\n            'yolo_loss': lambda y_true, y_pred: y_pred})\n\n        batch_size = 32\n        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))\n        model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes),\n                steps_per_epoch=max(1, num_train//batch_size),\n                validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, anchors, num_classes),\n                validation_steps=max(1, num_val//batch_size),\n                epochs=50,\n                initial_epoch=0,\n                callbacks=[logging, checkpoint])\n        model.save_weights(log_dir + 'trained_weights_stage_1.h5')\n\n    # Unfreeze and continue training, to fine-tune.\n    # Train longer if the result is not good.\n    if True:\n        for i in range(len(model.layers)):\n            model.layers[i].trainable = True\n        model.compile(optimizer=Adam(lr=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred}) # recompile to apply the change\n        print('Unfreeze all of the layers.')\n\n        batch_size = 32 # note that more GPU memory is required after unfreezing the body\n        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))\n        model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes),\n            steps_per_epoch=max(1, num_train//batch_size),\n            validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, anchors, num_classes),\n            validation_steps=max(1, num_val//batch_size),\n            epochs=100,\n            initial_epoch=50,\n            callbacks=[logging, checkpoint, reduce_lr, early_stopping])\n        model.save_weights(log_dir + 'trained_weights_final.h5')\n\n    # Further training if needed.\n\n\ndef get_classes(classes_path):\n    '''loads the classes'''\n    with open(classes_path) as f:\n        class_names = f.readlines()\n    class_names = [c.strip() for c in class_names]\n    return class_names\n\ndef get_anchors(anchors_path):\n    '''loads the anchors from a file'''\n    with open(anchors_path) as f:\n        anchors = f.readline()\n    anchors = [float(x) for x in anchors.split(',')]\n    return np.array(anchors).reshape(-1, 2)\n\n\ndef create_model(input_shape, anchors, num_classes, load_pretrained=True, freeze_body=2,\n            weights_path='model_data/yolo_weights.h5'):\n    '''create the training model'''\n    K.clear_session() # get a new session\n    image_input = Input(shape=(None, None, 3))\n    h, w = input_shape\n    num_anchors = len(anchors)\n\n    y_true = [Input(shape=(h//{0:32, 1:16, 2:8}[l], w//{0:32, 1:16, 2:8}[l], \\\n        num_anchors//3, num_classes+5)) for l in range(3)]\n\n    model_body = yolo_body(image_input, num_anchors//3, num_classes)\n    print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))\n\n    if load_pretrained:\n        model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)\n        print('Load weights {}.'.format(weights_path))\n        if freeze_body in [1, 2]:\n            # Freeze darknet53 body or freeze all but 3 output layers.\n            num = (185, len(model_body.layers)-3)[freeze_body-1]\n            for i in range(num): model_body.layers[i].trainable = False\n            print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers)))\n\n    model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',\n        arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(\n        [*model_body.output, *y_true])\n    model = Model([model_body.input, *y_true], model_loss)\n\n    return model\n\ndef create_tiny_model(input_shape, anchors, num_classes, load_pretrained=True, freeze_body=2,\n            weights_path='model_data/tiny_yolo_weights.h5'):\n    '''create the training model, for Tiny YOLOv3'''\n    K.clear_session() # get a new session\n    image_input = Input(shape=(None, None, 3))\n    h, w = input_shape\n    num_anchors = len(anchors)\n\n    y_true = [Input(shape=(h//{0:32, 1:16}[l], w//{0:32, 1:16}[l], \\\n        num_anchors//2, num_classes+5)) for l in range(2)]\n\n    model_body = tiny_yolo_body(image_input, num_anchors//2, num_classes)\n    print('Create Tiny YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))\n\n    if load_pretrained:\n        model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)\n        print('Load weights {}.'.format(weights_path))\n        if freeze_body in [1, 2]:\n            # Freeze the darknet body or freeze all but 2 output layers.\n            num = (20, len(model_body.layers)-2)[freeze_body-1]\n            for i in range(num): model_body.layers[i].trainable = False\n            print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers)))\n\n    model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',\n        arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.7})(\n        [*model_body.output, *y_true])\n    model = Model([model_body.input, *y_true], model_loss)\n\n    return model\n\ndef data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes):\n    '''data generator for fit_generator'''\n    n = len(annotation_lines)\n    i = 0\n    while True:\n        image_data = []\n        box_data = []\n        for b in range(batch_size):\n            if i==0:\n                np.random.shuffle(annotation_lines)\n            image, box = get_random_data(annotation_lines[i], input_shape, random=True)\n            image_data.append(image)\n            box_data.append(box)\n            i = (i+1) % n\n        image_data = np.array(image_data)\n        box_data = np.array(box_data)\n        y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)\n        yield [image_data, *y_true], np.zeros(batch_size)\n\ndef data_generator_wrapper(annotation_lines, batch_size, input_shape, anchors, num_classes):\n    n = len(annotation_lines)\n    if n==0 or batch_size<=0: return None\n    return data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes)\n\nif __name__ == '__main__':\n    _main()\n"
  },
  {
    "path": "train_bottleneck.py",
    "content": "\"\"\"\nRetrain the YOLO model for your own dataset.\n\"\"\"\nimport os\nimport numpy as np\nimport keras.backend as K\nfrom keras.layers import Input, Lambda\nfrom keras.models import Model\nfrom keras.optimizers import Adam\nfrom keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\n\nfrom yolo3.model import preprocess_true_boxes, yolo_body, tiny_yolo_body, yolo_loss\nfrom yolo3.utils import get_random_data\n\n\ndef _main():\n    annotation_path = 'train.txt'\n    log_dir = 'logs/000/'\n    classes_path = 'model_data/coco_classes.txt'\n    anchors_path = 'model_data/yolo_anchors.txt'\n    class_names = get_classes(classes_path)\n    num_classes = len(class_names)\n    anchors = get_anchors(anchors_path)\n\n    input_shape = (416,416) # multiple of 32, hw\n\n    model, bottleneck_model, last_layer_model = create_model(input_shape, anchors, num_classes,\n            freeze_body=2, weights_path='model_data/yolo_weights.h5') # make sure you know what you freeze\n\n    logging = TensorBoard(log_dir=log_dir)\n    checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',\n        monitor='val_loss', save_weights_only=True, save_best_only=True, period=3)\n    reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)\n    early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)\n\n    val_split = 0.1\n    with open(annotation_path) as f:\n        lines = f.readlines()\n    np.random.seed(10101)\n    np.random.shuffle(lines)\n    np.random.seed(None)\n    num_val = int(len(lines)*val_split)\n    num_train = len(lines) - num_val\n\n    # Train with frozen layers first, to get a stable loss.\n    # Adjust num epochs to your dataset. This step is enough to obtain a not bad model.\n    if True:\n        # perform bottleneck training\n        if not os.path.isfile(\"bottlenecks.npz\"):\n            print(\"calculating bottlenecks\")\n            batch_size=8\n            bottlenecks=bottleneck_model.predict_generator(data_generator_wrapper(lines, batch_size, input_shape, anchors, num_classes, random=False, verbose=True),\n             steps=(len(lines)//batch_size)+1, max_queue_size=1)\n            np.savez(\"bottlenecks.npz\", bot0=bottlenecks[0], bot1=bottlenecks[1], bot2=bottlenecks[2])\n    \n        # load bottleneck features from file\n        dict_bot=np.load(\"bottlenecks.npz\")\n        bottlenecks_train=[dict_bot[\"bot0\"][:num_train], dict_bot[\"bot1\"][:num_train], dict_bot[\"bot2\"][:num_train]]\n        bottlenecks_val=[dict_bot[\"bot0\"][num_train:], dict_bot[\"bot1\"][num_train:], dict_bot[\"bot2\"][num_train:]]\n\n        # train last layers with fixed bottleneck features\n        batch_size=8\n        print(\"Training last layers with bottleneck features\")\n        print('with {} samples, val on {} samples and batch size {}.'.format(num_train, num_val, batch_size))\n        last_layer_model.compile(optimizer='adam', loss={'yolo_loss': lambda y_true, y_pred: y_pred})\n        last_layer_model.fit_generator(bottleneck_generator(lines[:num_train], batch_size, input_shape, anchors, num_classes, bottlenecks_train),\n                steps_per_epoch=max(1, num_train//batch_size),\n                validation_data=bottleneck_generator(lines[num_train:], batch_size, input_shape, anchors, num_classes, bottlenecks_val),\n                validation_steps=max(1, num_val//batch_size),\n                epochs=30,\n                initial_epoch=0, max_queue_size=1)\n        model.save_weights(log_dir + 'trained_weights_stage_0.h5')\n        \n        # train last layers with random augmented data\n        model.compile(optimizer=Adam(lr=1e-3), loss={\n            # use custom yolo_loss Lambda layer.\n            'yolo_loss': lambda y_true, y_pred: y_pred})\n        batch_size = 16\n        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))\n        model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes),\n                steps_per_epoch=max(1, num_train//batch_size),\n                validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, anchors, num_classes),\n                validation_steps=max(1, num_val//batch_size),\n                epochs=50,\n                initial_epoch=0,\n                callbacks=[logging, checkpoint])\n        model.save_weights(log_dir + 'trained_weights_stage_1.h5')\n\n    # Unfreeze and continue training, to fine-tune.\n    # Train longer if the result is not good.\n    if True:\n        for i in range(len(model.layers)):\n            model.layers[i].trainable = True\n        model.compile(optimizer=Adam(lr=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred}) # recompile to apply the change\n        print('Unfreeze all of the layers.')\n\n        batch_size = 4 # note that more GPU memory is required after unfreezing the body\n        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))\n        model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes),\n            steps_per_epoch=max(1, num_train//batch_size),\n            validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, anchors, num_classes),\n            validation_steps=max(1, num_val//batch_size),\n            epochs=100,\n            initial_epoch=50,\n            callbacks=[logging, checkpoint, reduce_lr, early_stopping])\n        model.save_weights(log_dir + 'trained_weights_final.h5')\n\n    # Further training if needed.\n\n\ndef get_classes(classes_path):\n    '''loads the classes'''\n    with open(classes_path) as f:\n        class_names = f.readlines()\n    class_names = [c.strip() for c in class_names]\n    return class_names\n\ndef get_anchors(anchors_path):\n    '''loads the anchors from a file'''\n    with open(anchors_path) as f:\n        anchors = f.readline()\n    anchors = [float(x) for x in anchors.split(',')]\n    return np.array(anchors).reshape(-1, 2)\n\n\ndef create_model(input_shape, anchors, num_classes, load_pretrained=True, freeze_body=2,\n            weights_path='model_data/yolo_weights.h5'):\n    '''create the training model'''\n    K.clear_session() # get a new session\n    image_input = Input(shape=(None, None, 3))\n    h, w = input_shape\n    num_anchors = len(anchors)\n\n    y_true = [Input(shape=(h//{0:32, 1:16, 2:8}[l], w//{0:32, 1:16, 2:8}[l], \\\n        num_anchors//3, num_classes+5)) for l in range(3)]\n\n    model_body = yolo_body(image_input, num_anchors//3, num_classes)\n    print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))\n\n    if load_pretrained:\n        model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)\n        print('Load weights {}.'.format(weights_path))\n        if freeze_body in [1, 2]:\n            # Freeze darknet53 body or freeze all but 3 output layers.\n            num = (185, len(model_body.layers)-3)[freeze_body-1]\n            for i in range(num): model_body.layers[i].trainable = False\n            print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers)))\n\n    # get output of second last layers and create bottleneck model of it\n    out1=model_body.layers[246].output\n    out2=model_body.layers[247].output\n    out3=model_body.layers[248].output\n    bottleneck_model = Model([model_body.input, *y_true], [out1, out2, out3])\n\n    # create last layer model of last layers from yolo model\n    in0 = Input(shape=bottleneck_model.output[0].shape[1:].as_list()) \n    in1 = Input(shape=bottleneck_model.output[1].shape[1:].as_list())\n    in2 = Input(shape=bottleneck_model.output[2].shape[1:].as_list())\n    last_out0=model_body.layers[249](in0)\n    last_out1=model_body.layers[250](in1)\n    last_out2=model_body.layers[251](in2)\n    model_last=Model(inputs=[in0, in1, in2], outputs=[last_out0, last_out1, last_out2])\n    model_loss_last =Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',\n        arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(\n        [*model_last.output, *y_true])\n    last_layer_model = Model([in0,in1,in2, *y_true], model_loss_last)\n\n    \n    model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',\n        arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(\n        [*model_body.output, *y_true])\n    model = Model([model_body.input, *y_true], model_loss)\n\n    return model, bottleneck_model, last_layer_model\n\ndef data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes, random=True, verbose=False):\n    '''data generator for fit_generator'''\n    n = len(annotation_lines)\n    i = 0\n    while True:\n        image_data = []\n        box_data = []\n        for b in range(batch_size):\n            if i==0 and random:\n                np.random.shuffle(annotation_lines)\n            image, box = get_random_data(annotation_lines[i], input_shape, random=random)\n            image_data.append(image)\n            box_data.append(box)\n            i = (i+1) % n\n        image_data = np.array(image_data)\n        if verbose:\n            print(\"Progress: \",i,\"/\",n)\n        box_data = np.array(box_data)\n        y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)\n        yield [image_data, *y_true], np.zeros(batch_size)\n\ndef data_generator_wrapper(annotation_lines, batch_size, input_shape, anchors, num_classes, random=True, verbose=False):\n    n = len(annotation_lines)\n    if n==0 or batch_size<=0: return None\n    return data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes, random, verbose)\n\ndef bottleneck_generator(annotation_lines, batch_size, input_shape, anchors, num_classes, bottlenecks):\n    n = len(annotation_lines)\n    i = 0\n    while True:\n        box_data = []\n        b0=np.zeros((batch_size,bottlenecks[0].shape[1],bottlenecks[0].shape[2],bottlenecks[0].shape[3]))\n        b1=np.zeros((batch_size,bottlenecks[1].shape[1],bottlenecks[1].shape[2],bottlenecks[1].shape[3]))\n        b2=np.zeros((batch_size,bottlenecks[2].shape[1],bottlenecks[2].shape[2],bottlenecks[2].shape[3]))\n        for b in range(batch_size):\n            _, box = get_random_data(annotation_lines[i], input_shape, random=False, proc_img=False)\n            box_data.append(box)\n            b0[b]=bottlenecks[0][i]\n            b1[b]=bottlenecks[1][i]\n            b2[b]=bottlenecks[2][i]\n            i = (i+1) % n\n        box_data = np.array(box_data)\n        y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)\n        yield [b0, b1, b2, *y_true], np.zeros(batch_size)\n\nif __name__ == '__main__':\n    _main()\n"
  },
  {
    "path": "voc_annotation.py",
    "content": "import xml.etree.ElementTree as ET\nfrom os import getcwd\n\nsets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')]\n\nclasses = [\"aeroplane\", \"bicycle\", \"bird\", \"boat\", \"bottle\", \"bus\", \"car\", \"cat\", \"chair\", \"cow\", \"diningtable\", \"dog\", \"horse\", \"motorbike\", \"person\", \"pottedplant\", \"sheep\", \"sofa\", \"train\", \"tvmonitor\"]\n\n\ndef convert_annotation(year, image_id, list_file):\n    in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))\n    tree=ET.parse(in_file)\n    root = tree.getroot()\n\n    for obj in root.iter('object'):\n        difficult = obj.find('difficult').text\n        cls = obj.find('name').text\n        if cls not in classes or int(difficult)==1:\n            continue\n        cls_id = classes.index(cls)\n        xmlbox = obj.find('bndbox')\n        b = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text), int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text))\n        list_file.write(\" \" + \",\".join([str(a) for a in b]) + ',' + str(cls_id))\n\nwd = getcwd()\n\nfor year, image_set in sets:\n    image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()\n    list_file = open('%s_%s.txt'%(year, image_set), 'w')\n    for image_id in image_ids:\n        list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg'%(wd, year, image_id))\n        convert_annotation(year, image_id, list_file)\n        list_file.write('\\n')\n    list_file.close()\n\n"
  },
  {
    "path": "yolo.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nClass definition of YOLO_v3 style detection model on image and video\n\"\"\"\n\nimport colorsys\nimport os\nfrom timeit import default_timer as timer\n\nimport numpy as np\nfrom keras import backend as K\nfrom keras.models import load_model\nfrom keras.layers import Input\nfrom PIL import Image, ImageFont, ImageDraw\n\nfrom yolo3.model import yolo_eval, yolo_body, tiny_yolo_body\nfrom yolo3.utils import letterbox_image\nimport os\nfrom keras.utils import multi_gpu_model\n\nclass YOLO(object):\n    _defaults = {\n        \"model_path\": 'model_data/yolo.h5',\n        \"anchors_path\": 'model_data/yolo_anchors.txt',\n        \"classes_path\": 'model_data/coco_classes.txt',\n        \"score\" : 0.3,\n        \"iou\" : 0.45,\n        \"model_image_size\" : (416, 416),\n        \"gpu_num\" : 1,\n    }\n\n    @classmethod\n    def get_defaults(cls, n):\n        if n in cls._defaults:\n            return cls._defaults[n]\n        else:\n            return \"Unrecognized attribute name '\" + n + \"'\"\n\n    def __init__(self, **kwargs):\n        self.__dict__.update(self._defaults) # set up default values\n        self.__dict__.update(kwargs) # and update with user overrides\n        self.class_names = self._get_class()\n        self.anchors = self._get_anchors()\n        self.sess = K.get_session()\n        self.boxes, self.scores, self.classes = self.generate()\n\n    def _get_class(self):\n        classes_path = os.path.expanduser(self.classes_path)\n        with open(classes_path) as f:\n            class_names = f.readlines()\n        class_names = [c.strip() for c in class_names]\n        return class_names\n\n    def _get_anchors(self):\n        anchors_path = os.path.expanduser(self.anchors_path)\n        with open(anchors_path) as f:\n            anchors = f.readline()\n        anchors = [float(x) for x in anchors.split(',')]\n        return np.array(anchors).reshape(-1, 2)\n\n    def generate(self):\n        model_path = os.path.expanduser(self.model_path)\n        assert model_path.endswith('.h5'), 'Keras model or weights must be a .h5 file.'\n\n        # Load model, or construct model and load weights.\n        num_anchors = len(self.anchors)\n        num_classes = len(self.class_names)\n        is_tiny_version = num_anchors==6 # default setting\n        try:\n            self.yolo_model = load_model(model_path, compile=False)\n        except:\n            self.yolo_model = tiny_yolo_body(Input(shape=(None,None,3)), num_anchors//2, num_classes) \\\n                if is_tiny_version else yolo_body(Input(shape=(None,None,3)), num_anchors//3, num_classes)\n            self.yolo_model.load_weights(self.model_path) # make sure model, anchors and classes match\n        else:\n            assert self.yolo_model.layers[-1].output_shape[-1] == \\\n                num_anchors/len(self.yolo_model.output) * (num_classes + 5), \\\n                'Mismatch between model and given anchor and class sizes'\n\n        print('{} model, anchors, and classes loaded.'.format(model_path))\n\n        # Generate colors for drawing bounding boxes.\n        hsv_tuples = [(x / len(self.class_names), 1., 1.)\n                      for x in range(len(self.class_names))]\n        self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))\n        self.colors = list(\n            map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),\n                self.colors))\n        np.random.seed(10101)  # Fixed seed for consistent colors across runs.\n        np.random.shuffle(self.colors)  # Shuffle colors to decorrelate adjacent classes.\n        np.random.seed(None)  # Reset seed to default.\n\n        # Generate output tensor targets for filtered bounding boxes.\n        self.input_image_shape = K.placeholder(shape=(2, ))\n        if self.gpu_num>=2:\n            self.yolo_model = multi_gpu_model(self.yolo_model, gpus=self.gpu_num)\n        boxes, scores, classes = yolo_eval(self.yolo_model.output, self.anchors,\n                len(self.class_names), self.input_image_shape,\n                score_threshold=self.score, iou_threshold=self.iou)\n        return boxes, scores, classes\n\n    def detect_image(self, image):\n        start = timer()\n\n        if self.model_image_size != (None, None):\n            assert self.model_image_size[0]%32 == 0, 'Multiples of 32 required'\n            assert self.model_image_size[1]%32 == 0, 'Multiples of 32 required'\n            boxed_image = letterbox_image(image, tuple(reversed(self.model_image_size)))\n        else:\n            new_image_size = (image.width - (image.width % 32),\n                              image.height - (image.height % 32))\n            boxed_image = letterbox_image(image, new_image_size)\n        image_data = np.array(boxed_image, dtype='float32')\n\n        print(image_data.shape)\n        image_data /= 255.\n        image_data = np.expand_dims(image_data, 0)  # Add batch dimension.\n\n        out_boxes, out_scores, out_classes = self.sess.run(\n            [self.boxes, self.scores, self.classes],\n            feed_dict={\n                self.yolo_model.input: image_data,\n                self.input_image_shape: [image.size[1], image.size[0]],\n                K.learning_phase(): 0\n            })\n\n        print('Found {} boxes for {}'.format(len(out_boxes), 'img'))\n\n        font = ImageFont.truetype(font='font/FiraMono-Medium.otf',\n                    size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))\n        thickness = (image.size[0] + image.size[1]) // 300\n\n        for i, c in reversed(list(enumerate(out_classes))):\n            predicted_class = self.class_names[c]\n            box = out_boxes[i]\n            score = out_scores[i]\n\n            label = '{} {:.2f}'.format(predicted_class, score)\n            draw = ImageDraw.Draw(image)\n            label_size = draw.textsize(label, font)\n\n            top, left, bottom, right = box\n            top = max(0, np.floor(top + 0.5).astype('int32'))\n            left = max(0, np.floor(left + 0.5).astype('int32'))\n            bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))\n            right = min(image.size[0], np.floor(right + 0.5).astype('int32'))\n            print(label, (left, top), (right, bottom))\n\n            if top - label_size[1] >= 0:\n                text_origin = np.array([left, top - label_size[1]])\n            else:\n                text_origin = np.array([left, top + 1])\n\n            # My kingdom for a good redistributable image drawing library.\n            for i in range(thickness):\n                draw.rectangle(\n                    [left + i, top + i, right - i, bottom - i],\n                    outline=self.colors[c])\n            draw.rectangle(\n                [tuple(text_origin), tuple(text_origin + label_size)],\n                fill=self.colors[c])\n            draw.text(text_origin, label, fill=(0, 0, 0), font=font)\n            del draw\n\n        end = timer()\n        print(end - start)\n        return image\n\n    def close_session(self):\n        self.sess.close()\n\ndef detect_video(yolo, video_path, output_path=\"\"):\n    import cv2\n    vid = cv2.VideoCapture(video_path)\n    if not vid.isOpened():\n        raise IOError(\"Couldn't open webcam or video\")\n    video_FourCC    = int(vid.get(cv2.CAP_PROP_FOURCC))\n    video_fps       = vid.get(cv2.CAP_PROP_FPS)\n    video_size      = (int(vid.get(cv2.CAP_PROP_FRAME_WIDTH)),\n                        int(vid.get(cv2.CAP_PROP_FRAME_HEIGHT)))\n    isOutput = True if output_path != \"\" else False\n    if isOutput:\n        print(\"!!! TYPE:\", type(output_path), type(video_FourCC), type(video_fps), type(video_size))\n        out = cv2.VideoWriter(output_path, video_FourCC, video_fps, video_size)\n    accum_time = 0\n    curr_fps = 0\n    fps = \"FPS: ??\"\n    prev_time = timer()\n    while True:\n        return_value, frame = vid.read()\n        image = Image.fromarray(frame)\n        image = yolo.detect_image(image)\n        result = np.asarray(image)\n        curr_time = timer()\n        exec_time = curr_time - prev_time\n        prev_time = curr_time\n        accum_time = accum_time + exec_time\n        curr_fps = curr_fps + 1\n        if accum_time > 1:\n            accum_time = accum_time - 1\n            fps = \"FPS: \" + str(curr_fps)\n            curr_fps = 0\n        cv2.putText(result, text=fps, org=(3, 15), fontFace=cv2.FONT_HERSHEY_SIMPLEX,\n                    fontScale=0.50, color=(255, 0, 0), thickness=2)\n        cv2.namedWindow(\"result\", cv2.WINDOW_NORMAL)\n        cv2.imshow(\"result\", result)\n        if isOutput:\n            out.write(result)\n        if cv2.waitKey(1) & 0xFF == ord('q'):\n            break\n    yolo.close_session()\n\n"
  },
  {
    "path": "yolo3/__init__.py",
    "content": ""
  },
  {
    "path": "yolo3/model.py",
    "content": "\"\"\"YOLO_v3 Model Defined in Keras.\"\"\"\n\nfrom functools import wraps\n\nimport numpy as np\nimport tensorflow as tf\nfrom keras import backend as K\nfrom keras.layers import Conv2D, Add, ZeroPadding2D, UpSampling2D, Concatenate, MaxPooling2D\nfrom keras.layers.advanced_activations import LeakyReLU\nfrom keras.layers.normalization import BatchNormalization\nfrom keras.models import Model\nfrom keras.regularizers import l2\n\nfrom yolo3.utils import compose\n\n\n@wraps(Conv2D)\ndef DarknetConv2D(*args, **kwargs):\n    \"\"\"Wrapper to set Darknet parameters for Convolution2D.\"\"\"\n    darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}\n    darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same'\n    darknet_conv_kwargs.update(kwargs)\n    return Conv2D(*args, **darknet_conv_kwargs)\n\ndef DarknetConv2D_BN_Leaky(*args, **kwargs):\n    \"\"\"Darknet Convolution2D followed by BatchNormalization and LeakyReLU.\"\"\"\n    no_bias_kwargs = {'use_bias': False}\n    no_bias_kwargs.update(kwargs)\n    return compose(\n        DarknetConv2D(*args, **no_bias_kwargs),\n        BatchNormalization(),\n        LeakyReLU(alpha=0.1))\n\ndef resblock_body(x, num_filters, num_blocks):\n    '''A series of resblocks starting with a downsampling Convolution2D'''\n    # Darknet uses left and top padding instead of 'same' mode\n    x = ZeroPadding2D(((1,0),(1,0)))(x)\n    x = DarknetConv2D_BN_Leaky(num_filters, (3,3), strides=(2,2))(x)\n    for i in range(num_blocks):\n        y = compose(\n                DarknetConv2D_BN_Leaky(num_filters//2, (1,1)),\n                DarknetConv2D_BN_Leaky(num_filters, (3,3)))(x)\n        x = Add()([x,y])\n    return x\n\ndef darknet_body(x):\n    '''Darknent body having 52 Convolution2D layers'''\n    x = DarknetConv2D_BN_Leaky(32, (3,3))(x)\n    x = resblock_body(x, 64, 1)\n    x = resblock_body(x, 128, 2)\n    x = resblock_body(x, 256, 8)\n    x = resblock_body(x, 512, 8)\n    x = resblock_body(x, 1024, 4)\n    return x\n\ndef make_last_layers(x, num_filters, out_filters):\n    '''6 Conv2D_BN_Leaky layers followed by a Conv2D_linear layer'''\n    x = compose(\n            DarknetConv2D_BN_Leaky(num_filters, (1,1)),\n            DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),\n            DarknetConv2D_BN_Leaky(num_filters, (1,1)),\n            DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),\n            DarknetConv2D_BN_Leaky(num_filters, (1,1)))(x)\n    y = compose(\n            DarknetConv2D_BN_Leaky(num_filters*2, (3,3)),\n            DarknetConv2D(out_filters, (1,1)))(x)\n    return x, y\n\n\ndef yolo_body(inputs, num_anchors, num_classes):\n    \"\"\"Create YOLO_V3 model CNN body in Keras.\"\"\"\n    darknet = Model(inputs, darknet_body(inputs))\n    x, y1 = make_last_layers(darknet.output, 512, num_anchors*(num_classes+5))\n\n    x = compose(\n            DarknetConv2D_BN_Leaky(256, (1,1)),\n            UpSampling2D(2))(x)\n    x = Concatenate()([x,darknet.layers[152].output])\n    x, y2 = make_last_layers(x, 256, num_anchors*(num_classes+5))\n\n    x = compose(\n            DarknetConv2D_BN_Leaky(128, (1,1)),\n            UpSampling2D(2))(x)\n    x = Concatenate()([x,darknet.layers[92].output])\n    x, y3 = make_last_layers(x, 128, num_anchors*(num_classes+5))\n\n    return Model(inputs, [y1,y2,y3])\n\ndef tiny_yolo_body(inputs, num_anchors, num_classes):\n    '''Create Tiny YOLO_v3 model CNN body in keras.'''\n    x1 = compose(\n            DarknetConv2D_BN_Leaky(16, (3,3)),\n            MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),\n            DarknetConv2D_BN_Leaky(32, (3,3)),\n            MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),\n            DarknetConv2D_BN_Leaky(64, (3,3)),\n            MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),\n            DarknetConv2D_BN_Leaky(128, (3,3)),\n            MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),\n            DarknetConv2D_BN_Leaky(256, (3,3)))(inputs)\n    x2 = compose(\n            MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'),\n            DarknetConv2D_BN_Leaky(512, (3,3)),\n            MaxPooling2D(pool_size=(2,2), strides=(1,1), padding='same'),\n            DarknetConv2D_BN_Leaky(1024, (3,3)),\n            DarknetConv2D_BN_Leaky(256, (1,1)))(x1)\n    y1 = compose(\n            DarknetConv2D_BN_Leaky(512, (3,3)),\n            DarknetConv2D(num_anchors*(num_classes+5), (1,1)))(x2)\n\n    x2 = compose(\n            DarknetConv2D_BN_Leaky(128, (1,1)),\n            UpSampling2D(2))(x2)\n    y2 = compose(\n            Concatenate(),\n            DarknetConv2D_BN_Leaky(256, (3,3)),\n            DarknetConv2D(num_anchors*(num_classes+5), (1,1)))([x2,x1])\n\n    return Model(inputs, [y1,y2])\n\n\ndef yolo_head(feats, anchors, num_classes, input_shape, calc_loss=False):\n    \"\"\"Convert final layer features to bounding box parameters.\"\"\"\n    num_anchors = len(anchors)\n    # Reshape to batch, height, width, num_anchors, box_params.\n    anchors_tensor = K.reshape(K.constant(anchors), [1, 1, 1, num_anchors, 2])\n\n    grid_shape = K.shape(feats)[1:3] # height, width\n    grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]),\n        [1, grid_shape[1], 1, 1])\n    grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]),\n        [grid_shape[0], 1, 1, 1])\n    grid = K.concatenate([grid_x, grid_y])\n    grid = K.cast(grid, K.dtype(feats))\n\n    feats = K.reshape(\n        feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5])\n\n    # Adjust preditions to each spatial grid point and anchor size.\n    box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[::-1], K.dtype(feats))\n    box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[::-1], K.dtype(feats))\n    box_confidence = K.sigmoid(feats[..., 4:5])\n    box_class_probs = K.sigmoid(feats[..., 5:])\n\n    if calc_loss == True:\n        return grid, feats, box_xy, box_wh\n    return box_xy, box_wh, box_confidence, box_class_probs\n\n\ndef yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape):\n    '''Get corrected boxes'''\n    box_yx = box_xy[..., ::-1]\n    box_hw = box_wh[..., ::-1]\n    input_shape = K.cast(input_shape, K.dtype(box_yx))\n    image_shape = K.cast(image_shape, K.dtype(box_yx))\n    new_shape = K.round(image_shape * K.min(input_shape/image_shape))\n    offset = (input_shape-new_shape)/2./input_shape\n    scale = input_shape/new_shape\n    box_yx = (box_yx - offset) * scale\n    box_hw *= scale\n\n    box_mins = box_yx - (box_hw / 2.)\n    box_maxes = box_yx + (box_hw / 2.)\n    boxes =  K.concatenate([\n        box_mins[..., 0:1],  # y_min\n        box_mins[..., 1:2],  # x_min\n        box_maxes[..., 0:1],  # y_max\n        box_maxes[..., 1:2]  # x_max\n    ])\n\n    # Scale boxes back to original image shape.\n    boxes *= K.concatenate([image_shape, image_shape])\n    return boxes\n\n\ndef yolo_boxes_and_scores(feats, anchors, num_classes, input_shape, image_shape):\n    '''Process Conv layer output'''\n    box_xy, box_wh, box_confidence, box_class_probs = yolo_head(feats,\n        anchors, num_classes, input_shape)\n    boxes = yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape)\n    boxes = K.reshape(boxes, [-1, 4])\n    box_scores = box_confidence * box_class_probs\n    box_scores = K.reshape(box_scores, [-1, num_classes])\n    return boxes, box_scores\n\n\ndef yolo_eval(yolo_outputs,\n              anchors,\n              num_classes,\n              image_shape,\n              max_boxes=20,\n              score_threshold=.6,\n              iou_threshold=.5):\n    \"\"\"Evaluate YOLO model on given input and return filtered boxes.\"\"\"\n    num_layers = len(yolo_outputs)\n    anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]] # default setting\n    input_shape = K.shape(yolo_outputs[0])[1:3] * 32\n    boxes = []\n    box_scores = []\n    for l in range(num_layers):\n        _boxes, _box_scores = yolo_boxes_and_scores(yolo_outputs[l],\n            anchors[anchor_mask[l]], num_classes, input_shape, image_shape)\n        boxes.append(_boxes)\n        box_scores.append(_box_scores)\n    boxes = K.concatenate(boxes, axis=0)\n    box_scores = K.concatenate(box_scores, axis=0)\n\n    mask = box_scores >= score_threshold\n    max_boxes_tensor = K.constant(max_boxes, dtype='int32')\n    boxes_ = []\n    scores_ = []\n    classes_ = []\n    for c in range(num_classes):\n        # TODO: use keras backend instead of tf.\n        class_boxes = tf.boolean_mask(boxes, mask[:, c])\n        class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c])\n        nms_index = tf.image.non_max_suppression(\n            class_boxes, class_box_scores, max_boxes_tensor, iou_threshold=iou_threshold)\n        class_boxes = K.gather(class_boxes, nms_index)\n        class_box_scores = K.gather(class_box_scores, nms_index)\n        classes = K.ones_like(class_box_scores, 'int32') * c\n        boxes_.append(class_boxes)\n        scores_.append(class_box_scores)\n        classes_.append(classes)\n    boxes_ = K.concatenate(boxes_, axis=0)\n    scores_ = K.concatenate(scores_, axis=0)\n    classes_ = K.concatenate(classes_, axis=0)\n\n    return boxes_, scores_, classes_\n\n\ndef preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):\n    '''Preprocess true boxes to training input format\n\n    Parameters\n    ----------\n    true_boxes: array, shape=(m, T, 5)\n        Absolute x_min, y_min, x_max, y_max, class_id relative to input_shape.\n    input_shape: array-like, hw, multiples of 32\n    anchors: array, shape=(N, 2), wh\n    num_classes: integer\n\n    Returns\n    -------\n    y_true: list of array, shape like yolo_outputs, xywh are reletive value\n\n    '''\n    assert (true_boxes[..., 4]<num_classes).all(), 'class id must be less than num_classes'\n    num_layers = len(anchors)//3 # default setting\n    anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]\n\n    true_boxes = np.array(true_boxes, dtype='float32')\n    input_shape = np.array(input_shape, dtype='int32')\n    boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2\n    boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2]\n    true_boxes[..., 0:2] = boxes_xy/input_shape[::-1]\n    true_boxes[..., 2:4] = boxes_wh/input_shape[::-1]\n\n    m = true_boxes.shape[0]\n    grid_shapes = [input_shape//{0:32, 1:16, 2:8}[l] for l in range(num_layers)]\n    y_true = [np.zeros((m,grid_shapes[l][0],grid_shapes[l][1],len(anchor_mask[l]),5+num_classes),\n        dtype='float32') for l in range(num_layers)]\n\n    # Expand dim to apply broadcasting.\n    anchors = np.expand_dims(anchors, 0)\n    anchor_maxes = anchors / 2.\n    anchor_mins = -anchor_maxes\n    valid_mask = boxes_wh[..., 0]>0\n\n    for b in range(m):\n        # Discard zero rows.\n        wh = boxes_wh[b, valid_mask[b]]\n        if len(wh)==0: continue\n        # Expand dim to apply broadcasting.\n        wh = np.expand_dims(wh, -2)\n        box_maxes = wh / 2.\n        box_mins = -box_maxes\n\n        intersect_mins = np.maximum(box_mins, anchor_mins)\n        intersect_maxes = np.minimum(box_maxes, anchor_maxes)\n        intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.)\n        intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]\n        box_area = wh[..., 0] * wh[..., 1]\n        anchor_area = anchors[..., 0] * anchors[..., 1]\n        iou = intersect_area / (box_area + anchor_area - intersect_area)\n\n        # Find best anchor for each true box\n        best_anchor = np.argmax(iou, axis=-1)\n\n        for t, n in enumerate(best_anchor):\n            for l in range(num_layers):\n                if n in anchor_mask[l]:\n                    i = np.floor(true_boxes[b,t,0]*grid_shapes[l][1]).astype('int32')\n                    j = np.floor(true_boxes[b,t,1]*grid_shapes[l][0]).astype('int32')\n                    k = anchor_mask[l].index(n)\n                    c = true_boxes[b,t, 4].astype('int32')\n                    y_true[l][b, j, i, k, 0:4] = true_boxes[b,t, 0:4]\n                    y_true[l][b, j, i, k, 4] = 1\n                    y_true[l][b, j, i, k, 5+c] = 1\n\n    return y_true\n\n\ndef box_iou(b1, b2):\n    '''Return iou tensor\n\n    Parameters\n    ----------\n    b1: tensor, shape=(i1,...,iN, 4), xywh\n    b2: tensor, shape=(j, 4), xywh\n\n    Returns\n    -------\n    iou: tensor, shape=(i1,...,iN, j)\n\n    '''\n\n    # Expand dim to apply broadcasting.\n    b1 = K.expand_dims(b1, -2)\n    b1_xy = b1[..., :2]\n    b1_wh = b1[..., 2:4]\n    b1_wh_half = b1_wh/2.\n    b1_mins = b1_xy - b1_wh_half\n    b1_maxes = b1_xy + b1_wh_half\n\n    # Expand dim to apply broadcasting.\n    b2 = K.expand_dims(b2, 0)\n    b2_xy = b2[..., :2]\n    b2_wh = b2[..., 2:4]\n    b2_wh_half = b2_wh/2.\n    b2_mins = b2_xy - b2_wh_half\n    b2_maxes = b2_xy + b2_wh_half\n\n    intersect_mins = K.maximum(b1_mins, b2_mins)\n    intersect_maxes = K.minimum(b1_maxes, b2_maxes)\n    intersect_wh = K.maximum(intersect_maxes - intersect_mins, 0.)\n    intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]\n    b1_area = b1_wh[..., 0] * b1_wh[..., 1]\n    b2_area = b2_wh[..., 0] * b2_wh[..., 1]\n    iou = intersect_area / (b1_area + b2_area - intersect_area)\n\n    return iou\n\n\ndef yolo_loss(args, anchors, num_classes, ignore_thresh=.5, print_loss=False):\n    '''Return yolo_loss tensor\n\n    Parameters\n    ----------\n    yolo_outputs: list of tensor, the output of yolo_body or tiny_yolo_body\n    y_true: list of array, the output of preprocess_true_boxes\n    anchors: array, shape=(N, 2), wh\n    num_classes: integer\n    ignore_thresh: float, the iou threshold whether to ignore object confidence loss\n\n    Returns\n    -------\n    loss: tensor, shape=(1,)\n\n    '''\n    num_layers = len(anchors)//3 # default setting\n    yolo_outputs = args[:num_layers]\n    y_true = args[num_layers:]\n    anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]\n    input_shape = K.cast(K.shape(yolo_outputs[0])[1:3] * 32, K.dtype(y_true[0]))\n    grid_shapes = [K.cast(K.shape(yolo_outputs[l])[1:3], K.dtype(y_true[0])) for l in range(num_layers)]\n    loss = 0\n    m = K.shape(yolo_outputs[0])[0] # batch size, tensor\n    mf = K.cast(m, K.dtype(yolo_outputs[0]))\n\n    for l in range(num_layers):\n        object_mask = y_true[l][..., 4:5]\n        true_class_probs = y_true[l][..., 5:]\n\n        grid, raw_pred, pred_xy, pred_wh = yolo_head(yolo_outputs[l],\n             anchors[anchor_mask[l]], num_classes, input_shape, calc_loss=True)\n        pred_box = K.concatenate([pred_xy, pred_wh])\n\n        # Darknet raw box to calculate loss.\n        raw_true_xy = y_true[l][..., :2]*grid_shapes[l][::-1] - grid\n        raw_true_wh = K.log(y_true[l][..., 2:4] / anchors[anchor_mask[l]] * input_shape[::-1])\n        raw_true_wh = K.switch(object_mask, raw_true_wh, K.zeros_like(raw_true_wh)) # avoid log(0)=-inf\n        box_loss_scale = 2 - y_true[l][...,2:3]*y_true[l][...,3:4]\n\n        # Find ignore mask, iterate over each of batch.\n        ignore_mask = tf.TensorArray(K.dtype(y_true[0]), size=1, dynamic_size=True)\n        object_mask_bool = K.cast(object_mask, 'bool')\n        def loop_body(b, ignore_mask):\n            true_box = tf.boolean_mask(y_true[l][b,...,0:4], object_mask_bool[b,...,0])\n            iou = box_iou(pred_box[b], true_box)\n            best_iou = K.max(iou, axis=-1)\n            ignore_mask = ignore_mask.write(b, K.cast(best_iou<ignore_thresh, K.dtype(true_box)))\n            return b+1, ignore_mask\n        _, ignore_mask = K.control_flow_ops.while_loop(lambda b,*args: b<m, loop_body, [0, ignore_mask])\n        ignore_mask = ignore_mask.stack()\n        ignore_mask = K.expand_dims(ignore_mask, -1)\n\n        # K.binary_crossentropy is helpful to avoid exp overflow.\n        xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(raw_true_xy, raw_pred[...,0:2], from_logits=True)\n        wh_loss = object_mask * box_loss_scale * 0.5 * K.square(raw_true_wh-raw_pred[...,2:4])\n        confidence_loss = object_mask * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True)+ \\\n            (1-object_mask) * K.binary_crossentropy(object_mask, raw_pred[...,4:5], from_logits=True) * ignore_mask\n        class_loss = object_mask * K.binary_crossentropy(true_class_probs, raw_pred[...,5:], from_logits=True)\n\n        xy_loss = K.sum(xy_loss) / mf\n        wh_loss = K.sum(wh_loss) / mf\n        confidence_loss = K.sum(confidence_loss) / mf\n        class_loss = K.sum(class_loss) / mf\n        loss += xy_loss + wh_loss + confidence_loss + class_loss\n        if print_loss:\n            loss = tf.Print(loss, [loss, xy_loss, wh_loss, confidence_loss, class_loss, K.sum(ignore_mask)], message='loss: ')\n    return loss\n"
  },
  {
    "path": "yolo3/utils.py",
    "content": "\"\"\"Miscellaneous utility functions.\"\"\"\n\nfrom functools import reduce\n\nfrom PIL import Image\nimport numpy as np\nfrom matplotlib.colors import rgb_to_hsv, hsv_to_rgb\n\ndef compose(*funcs):\n    \"\"\"Compose arbitrarily many functions, evaluated left to right.\n\n    Reference: https://mathieularose.com/function-composition-in-python/\n    \"\"\"\n    # return lambda x: reduce(lambda v, f: f(v), funcs, x)\n    if funcs:\n        return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)\n    else:\n        raise ValueError('Composition of empty sequence not supported.')\n\ndef letterbox_image(image, size):\n    '''resize image with unchanged aspect ratio using padding'''\n    iw, ih = image.size\n    w, h = size\n    scale = min(w/iw, h/ih)\n    nw = int(iw*scale)\n    nh = int(ih*scale)\n\n    image = image.resize((nw,nh), Image.BICUBIC)\n    new_image = Image.new('RGB', size, (128,128,128))\n    new_image.paste(image, ((w-nw)//2, (h-nh)//2))\n    return new_image\n\ndef rand(a=0, b=1):\n    return np.random.rand()*(b-a) + a\n\ndef get_random_data(annotation_line, input_shape, random=True, max_boxes=20, jitter=.3, hue=.1, sat=1.5, val=1.5, proc_img=True):\n    '''random preprocessing for real-time data augmentation'''\n    line = annotation_line.split()\n    image = Image.open(line[0])\n    iw, ih = image.size\n    h, w = input_shape\n    box = np.array([np.array(list(map(int,box.split(',')))) for box in line[1:]])\n\n    if not random:\n        # resize image\n        scale = min(w/iw, h/ih)\n        nw = int(iw*scale)\n        nh = int(ih*scale)\n        dx = (w-nw)//2\n        dy = (h-nh)//2\n        image_data=0\n        if proc_img:\n            image = image.resize((nw,nh), Image.BICUBIC)\n            new_image = Image.new('RGB', (w,h), (128,128,128))\n            new_image.paste(image, (dx, dy))\n            image_data = np.array(new_image)/255.\n\n        # correct boxes\n        box_data = np.zeros((max_boxes,5))\n        if len(box)>0:\n            np.random.shuffle(box)\n            if len(box)>max_boxes: box = box[:max_boxes]\n            box[:, [0,2]] = box[:, [0,2]]*scale + dx\n            box[:, [1,3]] = box[:, [1,3]]*scale + dy\n            box_data[:len(box)] = box\n\n        return image_data, box_data\n\n    # resize image\n    new_ar = w/h * rand(1-jitter,1+jitter)/rand(1-jitter,1+jitter)\n    scale = rand(.25, 2)\n    if new_ar < 1:\n        nh = int(scale*h)\n        nw = int(nh*new_ar)\n    else:\n        nw = int(scale*w)\n        nh = int(nw/new_ar)\n    image = image.resize((nw,nh), Image.BICUBIC)\n\n    # place image\n    dx = int(rand(0, w-nw))\n    dy = int(rand(0, h-nh))\n    new_image = Image.new('RGB', (w,h), (128,128,128))\n    new_image.paste(image, (dx, dy))\n    image = new_image\n\n    # flip image or not\n    flip = rand()<.5\n    if flip: image = image.transpose(Image.FLIP_LEFT_RIGHT)\n\n    # distort image\n    hue = rand(-hue, hue)\n    sat = rand(1, sat) if rand()<.5 else 1/rand(1, sat)\n    val = rand(1, val) if rand()<.5 else 1/rand(1, val)\n    x = rgb_to_hsv(np.array(image)/255.)\n    x[..., 0] += hue\n    x[..., 0][x[..., 0]>1] -= 1\n    x[..., 0][x[..., 0]<0] += 1\n    x[..., 1] *= sat\n    x[..., 2] *= val\n    x[x>1] = 1\n    x[x<0] = 0\n    image_data = hsv_to_rgb(x) # numpy array, 0 to 1\n\n    # correct boxes\n    box_data = np.zeros((max_boxes,5))\n    if len(box)>0:\n        np.random.shuffle(box)\n        box[:, [0,2]] = box[:, [0,2]]*nw/iw + dx\n        box[:, [1,3]] = box[:, [1,3]]*nh/ih + dy\n        if flip: box[:, [0,2]] = w - box[:, [2,0]]\n        box[:, 0:2][box[:, 0:2]<0] = 0\n        box[:, 2][box[:, 2]>w] = w\n        box[:, 3][box[:, 3]>h] = h\n        box_w = box[:, 2] - box[:, 0]\n        box_h = box[:, 3] - box[:, 1]\n        box = box[np.logical_and(box_w>1, box_h>1)] # discard invalid box\n        if len(box)>max_boxes: box = box[:max_boxes]\n        box_data[:len(box)] = box\n\n    return image_data, box_data\n"
  },
  {
    "path": "yolo_video.py",
    "content": "import sys\nimport argparse\nfrom yolo import YOLO, detect_video\nfrom PIL import Image\n\ndef detect_img(yolo):\n    while True:\n        img = input('Input image filename:')\n        try:\n            image = Image.open(img)\n        except:\n            print('Open Error! Try again!')\n            continue\n        else:\n            r_image = yolo.detect_image(image)\n            r_image.show()\n    yolo.close_session()\n\nFLAGS = None\n\nif __name__ == '__main__':\n    # class YOLO defines the default value, so suppress any default here\n    parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)\n    '''\n    Command line options\n    '''\n    parser.add_argument(\n        '--model', type=str,\n        help='path to model weight file, default ' + YOLO.get_defaults(\"model_path\")\n    )\n\n    parser.add_argument(\n        '--anchors', type=str,\n        help='path to anchor definitions, default ' + YOLO.get_defaults(\"anchors_path\")\n    )\n\n    parser.add_argument(\n        '--classes', type=str,\n        help='path to class definitions, default ' + YOLO.get_defaults(\"classes_path\")\n    )\n\n    parser.add_argument(\n        '--gpu_num', type=int,\n        help='Number of GPU to use, default ' + str(YOLO.get_defaults(\"gpu_num\"))\n    )\n\n    parser.add_argument(\n        '--image', default=False, action=\"store_true\",\n        help='Image detection mode, will ignore all positional arguments'\n    )\n    '''\n    Command line positional arguments -- for video detection mode\n    '''\n    parser.add_argument(\n        \"--input\", nargs='?', type=str,required=False,default='./path2your_video',\n        help = \"Video input path\"\n    )\n\n    parser.add_argument(\n        \"--output\", nargs='?', type=str, default=\"\",\n        help = \"[Optional] Video output path\"\n    )\n\n    FLAGS = parser.parse_args()\n\n    if FLAGS.image:\n        \"\"\"\n        Image detection mode, disregard any remaining command line arguments\n        \"\"\"\n        print(\"Image detection mode\")\n        if \"input\" in FLAGS:\n            print(\" Ignoring remaining command line arguments: \" + FLAGS.input + \",\" + FLAGS.output)\n        detect_img(YOLO(**vars(FLAGS)))\n    elif \"input\" in FLAGS:\n        detect_video(YOLO(**vars(FLAGS)), FLAGS.input, FLAGS.output)\n    else:\n        print(\"Must specify at least video_input_path.  See usage with --help.\")\n"
  },
  {
    "path": "yolov3-tiny.cfg",
    "content": "[net]\n# Testing\nbatch=1\nsubdivisions=1\n# Training\n# batch=64\n# subdivisions=2\nwidth=416\nheight=416\nchannels=3\nmomentum=0.9\ndecay=0.0005\nangle=0\nsaturation = 1.5\nexposure = 1.5\nhue=.1\n\nlearning_rate=0.001\nburn_in=1000\nmax_batches = 500200\npolicy=steps\nsteps=400000,450000\nscales=.1,.1\n\n[convolutional]\nbatch_normalize=1\nfilters=16\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[maxpool]\nsize=2\nstride=2\n\n[convolutional]\nbatch_normalize=1\nfilters=32\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[maxpool]\nsize=2\nstride=2\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[maxpool]\nsize=2\nstride=2\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[maxpool]\nsize=2\nstride=2\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[maxpool]\nsize=2\nstride=2\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[maxpool]\nsize=2\nstride=1\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n###########\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nsize=1\nstride=1\npad=1\nfilters=255\nactivation=linear\n\n\n\n[yolo]\nmask = 3,4,5\nanchors = 10,14,  23,27,  37,58,  81,82,  135,169,  344,319\nclasses=80\nnum=6\njitter=.3\nignore_thresh = .7\ntruth_thresh = 1\nrandom=1\n\n[route]\nlayers = -4\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[upsample]\nstride=2\n\n[route]\nlayers = -1, 8\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nsize=1\nstride=1\npad=1\nfilters=255\nactivation=linear\n\n[yolo]\nmask = 1,2,3\nanchors = 10,14,  23,27,  37,58,  81,82,  135,169,  344,319\nclasses=80\nnum=6\njitter=.3\nignore_thresh = .7\ntruth_thresh = 1\nrandom=1\n"
  },
  {
    "path": "yolov3.cfg",
    "content": "[net]\n# Testing\nbatch=1\nsubdivisions=1\n# Training\n# batch=64\n# subdivisions=16\nwidth=416\nheight=416\nchannels=3\nmomentum=0.9\ndecay=0.0005\nangle=0\nsaturation = 1.5\nexposure = 1.5\nhue=.1\n\nlearning_rate=0.001\nburn_in=1000\nmax_batches = 500200\npolicy=steps\nsteps=400000,450000\nscales=.1,.1\n\n[convolutional]\nbatch_normalize=1\nfilters=32\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=32\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=64\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n# Downsample\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=2\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=1024\nsize=3\nstride=1\npad=1\nactivation=leaky\n\n[shortcut]\nfrom=-3\nactivation=linear\n\n######################\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=1024\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=1024\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=512\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=1024\nactivation=leaky\n\n[convolutional]\nsize=1\nstride=1\npad=1\nfilters=255\nactivation=linear\n\n\n[yolo]\nmask = 6,7,8\nanchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326\nclasses=80\nnum=9\njitter=.3\nignore_thresh = .5\ntruth_thresh = 1\nrandom=1\n\n\n[route]\nlayers = -4\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[upsample]\nstride=2\n\n[route]\nlayers = -1, 61\n\n\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=512\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=512\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=256\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=512\nactivation=leaky\n\n[convolutional]\nsize=1\nstride=1\npad=1\nfilters=255\nactivation=linear\n\n\n[yolo]\nmask = 3,4,5\nanchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326\nclasses=80\nnum=9\njitter=.3\nignore_thresh = .5\ntruth_thresh = 1\nrandom=1\n\n\n\n[route]\nlayers = -4\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[upsample]\nstride=2\n\n[route]\nlayers = -1, 36\n\n\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=256\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=256\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nfilters=128\nsize=1\nstride=1\npad=1\nactivation=leaky\n\n[convolutional]\nbatch_normalize=1\nsize=3\nstride=1\npad=1\nfilters=256\nactivation=leaky\n\n[convolutional]\nsize=1\nstride=1\npad=1\nfilters=255\nactivation=linear\n\n\n[yolo]\nmask = 0,1,2\nanchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326\nclasses=80\nnum=9\njitter=.3\nignore_thresh = .5\ntruth_thresh = 1\nrandom=1\n\n"
  }
]