2019-07-04 17:03:47 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Copyright 2018 Timon Brüning, Inga Kempfert, Anne Kunstmann, Jim Martens,
|
|
|
|
# Marius Pierenkemper, Yanneck Reiss
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
"""
|
|
|
|
Handle debug functionality.
|
|
|
|
|
|
|
|
Functions:
|
2019-07-10 12:12:13 +02:00
|
|
|
save_ssd_train_images(...):
|
2019-07-04 17:03:47 +02:00
|
|
|
saves the first batch of SSD train images with overlaid ground truth bounding boxes
|
|
|
|
"""
|
2019-09-16 11:02:08 +02:00
|
|
|
import functools
|
2019-07-04 17:03:47 +02:00
|
|
|
import os
|
2019-09-16 11:02:08 +02:00
|
|
|
from typing import Dict
|
|
|
|
from typing import Tuple
|
2019-07-15 13:25:47 +02:00
|
|
|
from typing import Union, Sequence
|
2019-07-04 17:03:47 +02:00
|
|
|
|
|
|
|
import math
|
|
|
|
import numpy as np
|
|
|
|
from matplotlib import pyplot
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
2019-07-15 13:25:47 +02:00
|
|
|
def save_ssd_train_images(images: Union[np.ndarray, Sequence[str]], labels: np.ndarray,
|
2019-07-10 12:12:13 +02:00
|
|
|
output_path: str, coco_path: str,
|
|
|
|
image_size: int, get_coco_cat_maps_func: callable,
|
|
|
|
custom_string: str = None) -> None:
|
|
|
|
"""
|
|
|
|
Saves given images and labels to given output path.
|
|
|
|
|
|
|
|
The images are saved both in a raw version and with bounding boxes printed on them.
|
|
|
|
|
|
|
|
Args:
|
2019-07-15 13:25:47 +02:00
|
|
|
images: a NumPy array of images or a list of filenames
|
2019-07-10 12:12:13 +02:00
|
|
|
labels: a NumPy array of labels
|
|
|
|
output_path: path to save the images in
|
|
|
|
coco_path: path to the COCO data set
|
|
|
|
image_size: size of the resized images
|
|
|
|
get_coco_cat_maps_func: callable that returns the COCO category maps for a given annotation file
|
|
|
|
custom_string: optional custom string that is prepended to file names
|
|
|
|
"""
|
|
|
|
|
2019-09-04 11:39:15 +02:00
|
|
|
annotation_file_train = f"{coco_path}/annotations/instances_minival2014.json"
|
2019-07-10 12:12:13 +02:00
|
|
|
_, _, _, classes_to_names = get_coco_cat_maps_func(annotation_file_train)
|
2019-08-11 15:32:59 +02:00
|
|
|
colors = pyplot.cm.hsv(np.linspace(0, 1, 61)).tolist()
|
2019-07-04 17:03:47 +02:00
|
|
|
os.makedirs(output_path, exist_ok=True)
|
|
|
|
|
|
|
|
nr_images = len(images)
|
|
|
|
nr_digits = math.ceil(math.log10(nr_images))
|
2019-07-10 12:02:10 +02:00
|
|
|
custom_string = f"{custom_string}_" if custom_string is not None else ""
|
2019-07-04 17:03:47 +02:00
|
|
|
|
|
|
|
for i, train_image in enumerate(images):
|
|
|
|
instances = labels[i]
|
2019-07-15 13:25:47 +02:00
|
|
|
if type(train_image) is str:
|
|
|
|
with Image.open(train_image) as _image:
|
2019-07-15 13:38:19 +02:00
|
|
|
train_image = np.array(_image, dtype=np.uint8)
|
|
|
|
image = Image.fromarray(train_image)
|
2019-07-10 11:59:34 +02:00
|
|
|
image.save(f"{output_path}/"
|
2019-07-10 12:02:10 +02:00
|
|
|
f"{custom_string}train_image{str(i).zfill(nr_digits)}.png")
|
2019-09-16 11:02:08 +02:00
|
|
|
figure_filename = f"{output_path}/{custom_string}bboxes{str(i).zfill(nr_digits)}.png"
|
2019-07-04 17:03:47 +02:00
|
|
|
|
2019-09-16 11:02:08 +02:00
|
|
|
_draw_bbox_image(image=image,
|
|
|
|
filename=figure_filename,
|
|
|
|
draw_func=functools.partial(_draw_bboxes,
|
|
|
|
image_size=image_size,
|
|
|
|
classes_to_names=classes_to_names),
|
|
|
|
drawables=[
|
|
|
|
(colors, instances)
|
|
|
|
])
|
|
|
|
|
|
|
|
|
2019-09-16 11:39:31 +02:00
|
|
|
def draw_bbox_figure(image_filename: str, labels: Sequence[np.ndarray],
|
|
|
|
instances: Sequence[Sequence[np.ndarray]],
|
|
|
|
image_size: int,
|
|
|
|
output_path: str, coco_path: str,
|
|
|
|
get_coco_cat_maps_func: callable) -> None:
|
|
|
|
"""
|
|
|
|
Draws a bounding box figure and saves it under the image file name under the output path.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
image_filename: complete path to image file
|
|
|
|
labels: ground truth labels for image
|
|
|
|
instances: list of predictions to be compared against each other
|
|
|
|
image_size: size of the resized images
|
|
|
|
output_path: path to save the images in
|
|
|
|
coco_path: path to the COCO data set
|
|
|
|
get_coco_cat_maps_func: callable that returns the COCO category maps for a given annotation file
|
|
|
|
"""
|
|
|
|
annotation_file_train = f"{coco_path}/annotations/instances_minival2014.json"
|
|
|
|
_, _, _, classes_to_names = get_coco_cat_maps_func(annotation_file_train)
|
|
|
|
|
2019-09-16 12:10:34 +02:00
|
|
|
colors = pyplot.cm.hsv(np.linspace(0, 1, len(instances) + 10)).tolist()
|
2019-09-16 11:39:31 +02:00
|
|
|
os.makedirs(output_path, exist_ok=True)
|
|
|
|
with Image.open(image_filename) as _image:
|
|
|
|
np_image = np.array(_image, dtype=np.uint8)
|
|
|
|
image = Image.fromarray(np_image)
|
|
|
|
figure_filename = f"{output_path}/{os.path.basename(image_filename)}_bboxes.png"
|
|
|
|
drawables = [(colors[i], _instances) for i, _instances in enumerate(instances)]
|
2019-09-16 12:10:34 +02:00
|
|
|
drawables.append((colors[-9], labels))
|
2019-09-16 11:39:31 +02:00
|
|
|
_draw_bbox_image(image=image,
|
|
|
|
filename=figure_filename,
|
|
|
|
draw_func=functools.partial(
|
|
|
|
_draw_bboxes,
|
|
|
|
image_size=image_size,
|
|
|
|
classes_to_names=classes_to_names
|
|
|
|
),
|
|
|
|
drawables=drawables)
|
|
|
|
|
|
|
|
|
2019-09-16 11:02:08 +02:00
|
|
|
def _draw_bbox_image(image: Image,
|
|
|
|
filename: str,
|
|
|
|
draw_func: callable,
|
2019-09-16 11:39:31 +02:00
|
|
|
drawables: Sequence[Tuple[Union[Sequence, float], Sequence[np.ndarray]]]):
|
2019-09-16 11:02:08 +02:00
|
|
|
figure = pyplot.figure(figsize=(6.4, 4.8))
|
|
|
|
pyplot.imshow(image)
|
|
|
|
|
|
|
|
current_axis = pyplot.gca()
|
|
|
|
for colors, instances in drawables:
|
|
|
|
draw_func(instances=instances,
|
|
|
|
axis=current_axis,
|
|
|
|
colors=colors)
|
|
|
|
|
|
|
|
pyplot.savefig(filename)
|
|
|
|
pyplot.close(figure)
|
|
|
|
|
|
|
|
|
|
|
|
def _draw_bboxes(instances: Sequence[np.ndarray], axis: pyplot.Axes,
|
|
|
|
image_size: int,
|
|
|
|
colors: Sequence,
|
|
|
|
classes_to_names: Dict[int, str]) -> None:
|
|
|
|
for instance in instances:
|
|
|
|
if not len(instance):
|
|
|
|
continue
|
|
|
|
else:
|
2019-09-16 11:39:31 +02:00
|
|
|
class_id, confidence, xmin, ymin, xmax, ymax = _get_bbox_info(instance, image_size)
|
2019-07-04 17:03:47 +02:00
|
|
|
|
2019-09-16 11:02:08 +02:00
|
|
|
if class_id == 0:
|
|
|
|
continue
|
2019-07-04 17:03:47 +02:00
|
|
|
|
2019-09-16 12:10:16 +02:00
|
|
|
if len(np.shape(colors)) == 1:
|
2019-09-16 11:39:31 +02:00
|
|
|
color = colors
|
|
|
|
else:
|
|
|
|
color = colors[class_id]
|
|
|
|
label = f"{classes_to_names[class_id]} - {confidence}" \
|
|
|
|
if confidence is not None \
|
|
|
|
else f"{classes_to_names[class_id]}"
|
2019-09-16 11:02:08 +02:00
|
|
|
axis.add_patch(
|
|
|
|
pyplot.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, color=color, fill=False,
|
|
|
|
linewidth=2))
|
|
|
|
axis.text(xmin, ymin, label, size='x-large', color='white',
|
|
|
|
bbox={'facecolor': color, 'alpha': 1.0})
|
|
|
|
|
|
|
|
|
2019-09-16 11:39:31 +02:00
|
|
|
def _get_bbox_info(instance: np.ndarray, image_size: int) -> Tuple[int, Union[float, None], float, float, float, float]:
|
2019-09-16 11:02:08 +02:00
|
|
|
if len(instance) == 5: # ground truth
|
|
|
|
class_id = int(instance[0])
|
2019-09-16 11:39:31 +02:00
|
|
|
confidence = None
|
2019-09-16 11:02:08 +02:00
|
|
|
xmin = instance[1]
|
|
|
|
ymin = instance[2]
|
|
|
|
xmax = instance[3]
|
|
|
|
ymax = instance[4]
|
|
|
|
elif len(instance) == 7: # predictions
|
|
|
|
class_id = int(instance[0])
|
2019-09-16 11:39:31 +02:00
|
|
|
confidence = instance[1]
|
2019-09-16 11:02:08 +02:00
|
|
|
xmin = instance[3]
|
|
|
|
ymin = instance[4]
|
|
|
|
xmax = instance[5]
|
|
|
|
ymax = instance[6]
|
|
|
|
elif len(instance) == 6: # predictions using Caffe method
|
|
|
|
class_id = int(instance[0])
|
2019-09-16 11:39:31 +02:00
|
|
|
confidence = instance[1]
|
2019-09-16 11:02:08 +02:00
|
|
|
xmin = instance[2]
|
|
|
|
ymin = instance[3]
|
|
|
|
xmax = instance[4]
|
|
|
|
ymax = instance[5]
|
|
|
|
else:
|
|
|
|
instance = np.copy(instance)
|
|
|
|
class_id = np.argmax(instance[:-12], axis=0)
|
2019-09-16 11:39:31 +02:00
|
|
|
confidence = np.amax(instance[:-12], axis=0)
|
2019-09-16 11:02:08 +02:00
|
|
|
instance[-12:-8] *= instance[-4:] # multiply with variances
|
|
|
|
instance[[-11, -9]] *= np.expand_dims(instance[-5] - instance[-7], axis=-1)
|
|
|
|
instance[[-12, -10]] *= np.expand_dims(instance[-6] - instance[-8], axis=-1)
|
|
|
|
instance[-12:-8] += instance[-8:-4]
|
|
|
|
instance[-12:-8] *= image_size
|
|
|
|
|
|
|
|
xmin = instance[-12]
|
|
|
|
ymin = instance[-11]
|
|
|
|
xmax = instance[-10]
|
|
|
|
ymax = instance[-9]
|
|
|
|
|
2019-09-16 11:39:31 +02:00
|
|
|
return class_id, confidence, xmin, ymin, xmax, ymax
|