|
''' |
|
https://github.com/zzh8829/yolov3-tf2 |
|
|
|
|
|
MIT License |
|
|
|
Copyright (c) 2019 Zihao Zhang |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
of this software and associated documentation files (the "Software"), to deal |
|
in the Software without restriction, including without limitation the rights |
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
copies of the Software, and to permit persons to whom the Software is |
|
furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included in all |
|
copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
SOFTWARE. |
|
''' |
|
|
|
|
|
from absl import logging |
|
import numpy as np |
|
import tensorflow as tf |
|
import cv2 |
|
|
|
YOLOV3_LAYER_LIST = [ |
|
'yolo_darknet', |
|
'yolo_conv_0', |
|
'yolo_output_0', |
|
'yolo_conv_1', |
|
'yolo_output_1', |
|
'yolo_conv_2', |
|
'yolo_output_2', |
|
] |
|
|
|
YOLOV3_TINY_LAYER_LIST = [ |
|
'yolo_darknet', |
|
'yolo_conv_0', |
|
'yolo_output_0', |
|
'yolo_conv_1', |
|
'yolo_output_1', |
|
] |
|
|
|
|
|
def load_darknet_weights(model, weights_file, tiny=False): |
|
wf = open(weights_file, 'rb') |
|
major, minor, revision, seen, _ = np.fromfile(wf, dtype=np.int32, count=5) |
|
|
|
if tiny: |
|
layers = YOLOV3_TINY_LAYER_LIST |
|
else: |
|
layers = YOLOV3_LAYER_LIST |
|
|
|
for layer_name in layers: |
|
sub_model = model.get_layer(layer_name) |
|
for i, layer in enumerate(sub_model.layers): |
|
if not layer.name.startswith('conv2d'): |
|
continue |
|
batch_norm = None |
|
if i + 1 < len(sub_model.layers) and \ |
|
sub_model.layers[i + 1].name.startswith('batch_norm'): |
|
batch_norm = sub_model.layers[i + 1] |
|
|
|
logging.info("{}/{} {}".format( |
|
sub_model.name, layer.name, 'bn' if batch_norm else 'bias')) |
|
|
|
filters = layer.filters |
|
size = layer.kernel_size[0] |
|
in_dim = layer.get_input_shape_at(0)[-1] |
|
|
|
if batch_norm is None: |
|
conv_bias = np.fromfile(wf, dtype=np.float32, count=filters) |
|
else: |
|
|
|
bn_weights = np.fromfile( |
|
wf, dtype=np.float32, count=4 * filters) |
|
|
|
bn_weights = bn_weights.reshape((4, filters))[[1, 0, 2, 3]] |
|
|
|
|
|
conv_shape = (filters, in_dim, size, size) |
|
conv_weights = np.fromfile( |
|
wf, dtype=np.float32, count=np.product(conv_shape)) |
|
|
|
conv_weights = conv_weights.reshape( |
|
conv_shape).transpose([2, 3, 1, 0]) |
|
|
|
if batch_norm is None: |
|
layer.set_weights([conv_weights, conv_bias]) |
|
else: |
|
layer.set_weights([conv_weights]) |
|
batch_norm.set_weights(bn_weights) |
|
|
|
assert len(wf.read()) == 0, 'failed to read all data' |
|
wf.close() |
|
|
|
|
|
def broadcast_iou(box_1, box_2): |
|
|
|
|
|
|
|
|
|
box_1 = tf.expand_dims(box_1, -2) |
|
box_2 = tf.expand_dims(box_2, 0) |
|
|
|
new_shape = tf.broadcast_dynamic_shape(tf.shape(box_1), tf.shape(box_2)) |
|
box_1 = tf.broadcast_to(box_1, new_shape) |
|
box_2 = tf.broadcast_to(box_2, new_shape) |
|
|
|
int_w = tf.maximum(tf.minimum(box_1[..., 2], box_2[..., 2]) - |
|
tf.maximum(box_1[..., 0], box_2[..., 0]), 0) |
|
int_h = tf.maximum(tf.minimum(box_1[..., 3], box_2[..., 3]) - |
|
tf.maximum(box_1[..., 1], box_2[..., 1]), 0) |
|
int_area = int_w * int_h |
|
box_1_area = (box_1[..., 2] - box_1[..., 0]) * \ |
|
(box_1[..., 3] - box_1[..., 1]) |
|
box_2_area = (box_2[..., 2] - box_2[..., 0]) * \ |
|
(box_2[..., 3] - box_2[..., 1]) |
|
return int_area / (box_1_area + box_2_area - int_area) |
|
|
|
|
|
def draw_outputs(img, outputs, class_names): |
|
boxes, objectness, classes, nums = outputs |
|
boxes, objectness, classes, nums = boxes[0], objectness[0], classes[0], nums[0] |
|
wh = np.flip(img.shape[0:2]) |
|
for i in range(nums): |
|
x1y1 = tuple((np.array(boxes[i][0:2]) * wh).astype(np.int32)) |
|
x2y2 = tuple((np.array(boxes[i][2:4]) * wh).astype(np.int32)) |
|
img = cv2.rectangle(img, x1y1, x2y2, (255, 0, 0), 2) |
|
img = cv2.putText(img, '{} {:.4f}'.format( |
|
class_names[int(classes[i])], objectness[i]), |
|
x1y1, cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 2) |
|
return img |
|
|
|
|
|
def draw_labels(x, y, class_names): |
|
img = x.numpy() |
|
boxes, classes = tf.split(y, (4, 1), axis=-1) |
|
classes = classes[..., 0] |
|
wh = np.flip(img.shape[0:2]) |
|
for i in range(len(boxes)): |
|
x1y1 = tuple((np.array(boxes[i][0:2]) * wh).astype(np.int32)) |
|
x2y2 = tuple((np.array(boxes[i][2:4]) * wh).astype(np.int32)) |
|
img = cv2.rectangle(img, x1y1, x2y2, (255, 0, 0), 2) |
|
img = cv2.putText(img, class_names[classes[i]], |
|
x1y1, cv2.FONT_HERSHEY_COMPLEX_SMALL, |
|
1, (0, 0, 255), 2) |
|
return img |
|
|
|
|
|
def freeze_all(model, frozen=True): |
|
model.trainable = not frozen |
|
if isinstance(model, tf.keras.Model): |
|
for l in model.layers: |
|
freeze_all(l, frozen) |
|
|