Skip to content

Commit

Permalink
Merge pull request #244 from RobertLucian/master
Browse files Browse the repository at this point in the history
add license plate model example
  • Loading branch information
experiencor authored Jan 31, 2020
2 parents 92ea797 + 7859db6 commit 768c524
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
*.jpeg
*.weights
*.h5
*.pyc
*.xml
*.mp4
*.DS_Store
*.bak
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Dataset | mAP | Demo | Config | Model
:---:|:---:|:---:|:---:|:---:
Kangaroo Detection (1 class) (https://github.com/experiencor/kangaroo) | 95% | https://youtu.be/URO3UDHvoLY | check zoo | https://bit.ly/39rLNoE
License Plate Detection (European in Romania) (1 class) (https://github.com/RobertLucian/license-plate-dataset) | 90% | https://youtu.be/HrqzIXFVCRo | check zoo | https://bit.ly/2tIpvPl
Raccoon Detection (1 class) (https://github.com/experiencor/raccoon_dataset) | 98% | https://youtu.be/lxLyLIL7OsU | check zoo | https://bit.ly/39rLNoE
Red Blood Cell Detection (3 classes) (https://github.com/experiencor/BCCD_Dataset) | 84% | https://imgur.com/a/uJl2lRI | check zoo | https://bit.ly/39rLNoE
VOC (20 classes) (http://host.robots.ox.ac.uk/pascal/VOC/voc2012/) | 72% | https://youtu.be/0RmOI6hcfBI | check zoo | https://bit.ly/39rLNoE
Expand All @@ -18,6 +19,16 @@ VOC (20 classes) (http://host.robots.ox.ac.uk/pascal/VOC/voc2012/) | 72% | https
- [ ] Evaluation on COCO
- [ ] MobileNet, DenseNet, ResNet, and VGG backends

## Installing

To install the dependencies, run
```bash
pip install -r requirements.txt
```
And for the GPU to work, make sure you've got the drivers installed beforehand (CUDA).

It has been tested to work with Python 2.7.13 and 3.5.3.

## Detection

Grab the pretrained weights of yolo3 from https://pjreddie.com/media/files/yolov3.weights.
Expand All @@ -42,6 +53,15 @@ Organize the dataset into 4 folders:

There is a one-to-one correspondence by file name between images and annotations. If the validation set is empty, the training set will be automatically splitted into the training set and validation set using the ratio of 0.8.

Also, if you've got the dataset split into 2 folders such as one for images and the other one for annotations and you need to set a custom size for the validation set, use `create_validation_set.sh` script to that. The script expects the following parameters in the following order:
```bash
./create_validation_set.sh $param1 $param2 $param3 $param4
# 1st param - folder where the images are found
# 2nd param - folder where the annotations are found
# 3rd param - number of random choices (aka the size of the validation set in absolute value)
# 4th param - folder where validation images/annots end up (must have images/annots folders inside the given directory as the 4th param)
```

### 2. Edit the configuration file
The configuration file is a json file, which looks like this:

Expand Down Expand Up @@ -106,6 +126,8 @@ By the end of this process, the code will write the weights of the best model to

It carries out detection on the image and write the image with detected bounding boxes to the same folder.

If you wish to change the object threshold or IOU threshold, you can do it by altering `obj_thresh` and `nms_thresh` variables. By default, they are set to `0.5` and `0.45` respectively.

## Evaluation

`python evaluate.py -c config.json`
Expand Down
16 changes: 16 additions & 0 deletions create_validation_set.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash

# Run it from this directory.

# 1st param - folder where images are found
# 2nd param - folder where annotations are found
# 3rd param - number of random choices
# 4th param - folder where validation images/annots end up (must have images/annots folders created)

ls "$1" | sort -R | tail -"$3" | while read image; do
filename=$(basename "$image" .jpg)
annot="$filename.xml"
echo "moving files $image $annot"
mv "$1/$image" "$4/images"
mv "$2/$annot" "$4/annots"
done
24 changes: 24 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
absl-py==0.9.0
astor==0.8.1
gast==0.2.2
google-pasta==0.1.8
grpcio==1.26.0
h5py==2.10.0
Keras==2.3.1
Keras-Applications==1.0.8
Keras-Preprocessing==1.1.0
Markdown==3.1.1
numpy==1.18.1
opencv-contrib-python==4.1.2.30
opt-einsum==3.1.0
protobuf==3.11.2
PyYAML==5.3
scipy==1.4.1
six==1.14.0
tensorboard==1.15.0
tensorflow==1.15.0
tensorflow-estimator==1.15.1
termcolor==1.1.0
tqdm==4.41.1
Werkzeug==0.16.0
wrapt==1.11.2
11 changes: 10 additions & 1 deletion train.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
import keras
from keras.models import load_model


config = tf.compat.v1.ConfigProto(
gpu_options = tf.compat.v1.GPUOptions(per_process_gpu_memory_fraction=0.9)
# device_count = {'GPU': 1}
)
config.gpu_options.allow_growth = True
session = tf.compat.v1.Session(config=config)
tf.compat.v1.keras.backend.set_session(session)

def create_training_instances(
train_annot_folder,
train_image_folder,
Expand Down Expand Up @@ -68,7 +77,7 @@ def create_callbacks(saved_weights_name, tensorboard_logs, model_to_save):
early_stop = EarlyStopping(
monitor = 'loss',
min_delta = 0.01,
patience = 5,
patience = 7,
mode = 'min',
verbose = 1
)
Expand Down
25 changes: 14 additions & 11 deletions yolo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from keras.engine.topology import Layer
import tensorflow as tf

debug = False

class YoloLayer(Layer):
def __init__(self, anchors, max_grid, batch_size, warmup_batches, ignore_thresh,
grid_scale, obj_scale, noobj_scale, xywh_scale, class_scale,
Expand Down Expand Up @@ -176,17 +178,18 @@ def call(self, x):

loss = loss_xy + loss_wh + loss_conf + loss_class

loss = tf.Print(loss, [grid_h, avg_obj], message='avg_obj \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, avg_noobj], message='avg_noobj \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, avg_iou], message='avg_iou \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, avg_cat], message='avg_cat \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, recall50], message='recall50 \t', summarize=1000)
loss = tf.Print(loss, [grid_h, recall75], message='recall75 \t', summarize=1000)
loss = tf.Print(loss, [grid_h, count], message='count \t', summarize=1000)
loss = tf.Print(loss, [grid_h, tf.reduce_sum(loss_xy),
tf.reduce_sum(loss_wh),
tf.reduce_sum(loss_conf),
tf.reduce_sum(loss_class)], message='loss xy, wh, conf, class: \t', summarize=1000)
if debug:
loss = tf.Print(loss, [grid_h, avg_obj], message='avg_obj \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, avg_noobj], message='avg_noobj \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, avg_iou], message='avg_iou \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, avg_cat], message='avg_cat \t\t', summarize=1000)
loss = tf.Print(loss, [grid_h, recall50], message='recall50 \t', summarize=1000)
loss = tf.Print(loss, [grid_h, recall75], message='recall75 \t', summarize=1000)
loss = tf.Print(loss, [grid_h, count], message='count \t', summarize=1000)
loss = tf.Print(loss, [grid_h, tf.reduce_sum(loss_xy),
tf.reduce_sum(loss_wh),
tf.reduce_sum(loss_conf),
tf.reduce_sum(loss_class)], message='loss xy, wh, conf, class: \t', summarize=1000)


return loss*self.grid_scale
Expand Down
42 changes: 42 additions & 0 deletions zoo/config_license_plates.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"model" : {
"min_input_size": 416,
"max_input_size": 416,
"anchors": [15,6, 18,8, 22,9, 27,11, 32,13, 41,17, 54,21, 66,27, 82,33],
"labels": ["license-plate"]
},

"train": {
"train_image_folder": "dataset/train/images/",
"train_annot_folder": "dataset/train/annots/",
"cache_name": "license_plate.pkl",

"pretrained_weights": "pretrained_lp.h5",

"train_times": 4,
"batch_size": 16,
"learning_rate": 1e-4,
"nb_epochs": 100,
"warmup_epochs": 3,
"ignore_thresh": 0.6,
"gpus": "0",

"grid_scales": [1,1,1],
"obj_scale": 5,
"noobj_scale": 1,
"xywh_scale": 1,
"class_scale": 1,

"tensorboard_dir": "logs",
"saved_weights_name": "license_plate.h5",
"debug": true
},

"valid": {
"valid_image_folder": "dataset/valid/images/",
"valid_annot_folder": "dataset/valid/annots/",
"cache_name": "valid.pkl",

"valid_times": 1
}
}

0 comments on commit 768c524

Please sign in to comment.