Path: blob/master/C4 - Convolutional Neural Networks/Week 3/Car detection for Autonomous Driving/yad2k/utils/utils.py
5158 views
"""Draw predicted or ground truth boxes on input image."""1import imghdr2import colorsys3import random45import numpy as np6from PIL import Image, ImageDraw, ImageFont7from tensorflow.keras import backend as K89from functools import reduce1011def preprocess_image(img_path, model_image_size):12image_type = imghdr.what(img_path)13image = Image.open(img_path)14resized_image = image.resize(tuple(reversed(model_image_size)), Image.BICUBIC)15image_data = np.array(resized_image, dtype='float32')16image_data /= 255.17image_data = np.expand_dims(image_data, 0) # Add batch dimension.18return image, image_data1920def compose(*funcs):21"""Compose arbitrarily many functions, evaluated left to right.2223Reference: https://mathieularose.com/function-composition-in-python/24"""25# return lambda x: reduce(lambda v, f: f(v), funcs, x)26if funcs:27return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)28else:29raise ValueError('Composition of empty sequence not supported.')3031def read_classes(classes_path):32with open(classes_path) as f:33class_names = f.readlines()34class_names = [c.strip() for c in class_names]35return class_names3637def read_anchors(anchors_path):38with open(anchors_path) as f:39anchors = f.readline()40anchors = [float(x) for x in anchors.split(',')]41anchors = np.array(anchors).reshape(-1, 2)42return anchors4344def scale_boxes(boxes, image_shape):45""" Scales the predicted boxes in order to be drawable on the image"""46height = image_shape[0] * 1.047width = image_shape[1] * 1.048image_dims = K.stack([height, width, height, width])49image_dims = K.reshape(image_dims, [1, 4])50boxes = boxes * image_dims51return boxes5253def get_colors_for_classes(num_classes):54"""Return list of random colors for number of classes given."""55# Use previously generated colors if num_classes is the same.56if (hasattr(get_colors_for_classes, "colors") and57len(get_colors_for_classes.colors) == num_classes):58return get_colors_for_classes.colors5960hsv_tuples = [(x / num_classes, 1., 1.) for x in range(num_classes)]61colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))62colors = list(63map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),64colors))65random.seed(10101) # Fixed seed for consistent colors across runs.66random.shuffle(colors) # Shuffle colors to decorrelate adjacent classes.67random.seed(None) # Reset seed to default.68get_colors_for_classes.colors = colors # Save colors for future calls.69return colors7071def textsize(text, font):72im = Image.new(mode="P", size=(0, 0))73draw = ImageDraw.Draw(im)74_, _, width, height = draw.textbbox((0, 0), text=text, font=font)75return width, height7677def draw_boxes(image, boxes, box_classes, class_names, scores=None):78"""Draw bounding boxes on image.7980Draw bounding boxes with class name and optional box score on image.8182Args:83image: An `array` of shape (width, height, 3) with values in [0, 1].84boxes: An `array` of shape (num_boxes, 4) containing box corners as85(y_min, x_min, y_max, x_max).86box_classes: A `list` of indicies into `class_names`.87class_names: A `list` of `string` class names.88`scores`: A `list` of scores for each box.8990Returns:91A copy of `image` modified with given bounding boxes.92"""93#image = Image.fromarray(np.floor(image * 255 + 0.5).astype('uint8'))9495font = ImageFont.truetype(96font='font/FiraMono-Medium.otf',97size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))98thickness = (image.size[0] + image.size[1]) // 30099100colors = get_colors_for_classes(len(class_names))101102for i, c in list(enumerate(box_classes)):103box_class = class_names[c]104box = boxes[i]105106if isinstance(scores.numpy(), np.ndarray):107score = scores.numpy()[i]108label = '{} {:.2f}'.format(box_class, score)109else:110label = '{}'.format(box_class)111112draw = ImageDraw.Draw(image)113label_size = textsize(label, font)114115top, left, bottom, right = box116top = max(0, np.floor(top + 0.5).astype('int32'))117left = max(0, np.floor(left + 0.5).astype('int32'))118bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))119right = min(image.size[0], np.floor(right + 0.5).astype('int32'))120print(label, (left, top), (right, bottom))121122if top - label_size[1] >= 0:123text_origin = np.array([left, top - label_size[1]])124else:125text_origin = np.array([left, top + 1])126127# My kingdom for a good redistributable image drawing library.128for i in range(thickness):129draw.rectangle(130[left + i, top + i, right - i, bottom - i], outline=colors[c])131draw.rectangle(132[tuple(text_origin), tuple(text_origin + label_size)],133fill=colors[c])134draw.text(text_origin, label, fill=(0, 0, 0), font=font)135del draw136137return np.array(image)138139140