Source code for util.misc

"""
General purpose utility functions.

"""

# Utils
import logging
import os
import os.path
import shutil
import string
import colorsys


import numpy as np
import torch
from PIL import Image

def _prettyprint_logging_label(logging_label):
    """Format the logging label in a pretty manner.

    Parameters
    ----------
    logging_label : str
        The label used in logging

    Returns
    -------
    logging_label : str
        Correctly formatted logging label.

    """
    if len(logging_label) < 5:
        for i in range(5 - len(logging_label)):
            logging_label = logging_label + ' '
    return logging_label


[docs]class AverageMeter(object): """Computes and stores the average and current value From https://github.com/pytorch/examples/blob/master/imagenet/main.py """ def __init__(self): self.reset()
[docs] def reset(self): self.val = 0 self.avg = 0 self.sum = 0 self.count = 0
[docs] def update(self, val, n=1): self.val = val self.sum += val * n self.count += n self.avg = self.sum / self.count
""" Computes the accuracy@K for the specified values of K From https://github.com/pytorch/examples/blob/master/imagenet/main.py Parameters ---------- :param output: The output of the model :param target: The GT for the corresponding output :param topk: Top@k return value. It can be a tuple (1,5) and it return Top1 and Top5 :return: Top@k accuracy """
[docs]def adjust_learning_rate(lr, optimizer, epoch, decay_lr_epochs, **kwargs): """Sets the learning rate to the initial LR decayed by 10 every N epochs. Adapted from https://github.com/pytorch/examples/blob/master/imagenet/main.py Parameters ---------- lr : float Learning rate. optimizer : torch.optim object The optimizer used for training the network. epoch : int Current training epoch. decay_lr_epochs : int Change the learning rate every N epochs. Returns ------- None """ lr = lr * (0.1 ** (epoch // decay_lr_epochs)) for param_group in optimizer.param_groups: param_group['lr'] = lr return
[docs]def checkpoint(epoch, new_value, best_value, model, optimizer, log_dir, invert_best=False, checkpoint_all_epochs=False, **kwargs): """Saves the current training checkpoint and the best valued checkpoint to file. Parameters ---------- epoch : int Current epoch, for logging purpose only. new_value : float Current value achieved by the model at this epoch. To be compared with 'best_value'. best_value : float Best value ever obtained (so the last checkpointed model). To be compared with 'new_value'. model : torch.nn.module object The model we are checkpointing, this can be saved on file if necessary. optimizer : The optimizer that is being used to train this model. It is necessary if we were to resume the training from a checkpoint. log_dir : str Output folder where to put the model. invert_best : bool Changes the scale such that smaller values are better than bigger values (useful when metric evaluted is error rate) checkpoint_all_epochs : bool If enabled, save checkpoint after every epoch. kwargs : dict Any additional arguments. Returns ------- best_value : float Best value ever obtained. """ if invert_best: is_best = new_value < best_value best_value = min(new_value, best_value) else: is_best = new_value > best_value best_value = max(new_value, best_value) filename = os.path.join(log_dir, 'checkpoint.pth.tar') torch.save({ 'epoch': epoch + 1, 'arch': str(type(model)), 'state_dict': model.state_dict(), 'best_value': best_value, 'optimizer': optimizer.state_dict(), }, filename) if is_best: shutil.copyfile(filename, os.path.join(os.path.split(filename)[0], 'model_best.pth.tar')) # If enabled, save all checkpoints with epoch number. if checkpoint_all_epochs == True: shutil.move(filename, os.path.join(os.path.split(filename)[0], 'checkpoint_{}.pth.tar'.format(epoch))) return best_value
[docs]def to_capital_camel_case(s): """Converts a string to camel case. Parameters ---------- s : str Input string. Returns ------- str Input string `s` converted to camel case. """ return s[0].capitalize() + string.capwords(s, sep='_').replace('_', '')[1:] if s else s
[docs]def get_all_files_in_folders_and_subfolders(root_dir=None): """Get all the files in a folder and sub-folders. Parameters ---------- root_dir : str All files in this directory and it's sub-folders will be returned by this method. Returns ------- paths : list of str List of paths to all files in this folder and it's subfolders. """ paths = [] for path, subdirs, files in os.walk(root_dir): for name in files: paths.append(os.path.join(path, name)) return paths
[docs]def tensor_to_image(image): """ Tries to reshape, convert and do operations necessary to bring the image in a format friendly to be saved and logged to Tensorboard by save_image_and_log_to_tensorboard() Parameters ---------- image : ? Image to be converted Returns ------- image : ndarray [W x H x C] Image, as format friendly to be saved and logged to Tensorboard. """ # Check if the data is still a Variable() if 'variable' in str(type(image)): image = image.data # Check if the data is still on CUDA if 'cuda' in str(type(image)): image = image.cpu() # Check if the data is still on a Tensor if 'Tensor' in str(type(image)): image = image.numpy() assert ('ndarray' in str(type(image))) # Its an ndarray # Check that it does not have anymore the 4th dimension (from the mini-batch) if len(image.shape) > 3: assert (len(image.shape) == 4) image = np.squeeze(image) assert (len(image.shape) == 3) # 3D matrix (W x H x C) # Check that the last channel is of size 3 for RGB if image.shape[2] != 3: assert (image.shape[0] == 3) image = np.transpose(image, (1, 2, 0)) assert (image.shape[2] == 3) # Last channel is of size 3 for RGB # Check that the range is [0:255] image = (image - image.min()) / (image.max() - image.min()) image = image * 255 assert (image.min() >= 0) # Data should be in range [0:255] return image.astype(np.uint8)
[docs]def save_image_and_log_to_tensorboard(writer=None, tag=None, image=None, global_step=None): """Utility function to save image in the output folder and also log it to Tensorboard. Parameters ---------- writer : tensorboardX.writer.SummaryWriter object The writer object for Tensorboard tag : str Name of the image. image : ndarray [W x H x C] Image to be saved and logged to Tensorboard. global_step : int Epoch/Mini-batch counter. Returns ------- None """ # Log image to Tensorboard writer.add_image(tag=tag, img_tensor=image, global_step=global_step, dataformats='HWC') # Get output folder using the FileHandler from the logger. # (Assumes the file handler is the last one) output_folder = os.path.dirname(logging.getLogger().handlers[-1].baseFilename) if global_step is not None: dest_filename = os.path.join(output_folder, 'images', tag + '_{}.png'.format(global_step)) else: dest_filename = os.path.join(output_folder, 'images', tag + '.png') if not os.path.exists(os.path.dirname(dest_filename)): os.makedirs(os.path.dirname(dest_filename)) # Ensuring the data passed as parameter is healthy image = tensor_to_image(image) # Write image to output folder save_numpy_image(dest_filename, image) return
[docs]def save_numpy_image(dest_filename, image): img = Image.fromarray(image) img.save(dest_filename) return
[docs]def load_numpy_image(dest_filename): img = Image.open(dest_filename).convert('RGB') img = np.array(img) return img
[docs]def has_extension(filename, extensions): """Checks if a file is an allowed extension. Adapted from https://github.com/pytorch/vision/blob/master/torchvision/datasets/folder.py. Parameters ---------- filename : string path to a file extensions : list extensions to match against Returns ------- bool True if the filename ends with one of given extensions, false otherwise. """ filename_lower = filename.lower() return any(filename_lower.endswith(ext) for ext in extensions)
[docs]def make_folder_if_not_exists(path): if not os.path.exists(path): os.makedirs(path)
[docs]def pil_loader(path): # open path as file to avoid ResourceWarning (https://github.com/python-pillow/Pillow/issues/835) with open(path, 'rb') as f: with Image.open(f) as img: return img.convert('RGB')
# functions added for HisDB classification
[docs]def int_to_one_hot(x, n_classes): """ Read out class encoding from blue channel bit-encoding (1 to [0,0,0,1] -> length determined by the number of classes) Parameters ---------- x: int (pixel value of Blue channel from RGB image) n_classes: int number of class labels Returns ------- list (multi) one-hot encoded list for integer """ s = '{0:0' + str(n_classes) + 'b}' return list(map(int, list(s.format(x))))
[docs]def multi_label_img_to_multi_hot(np_array): """ TODO: There must be a faster way of doing this + ajust to correct input format (see gt_tensor_to_one_hot) Convert ground truth label image to multi-one-hot encoded matrix of size image height x image width x #classes Parameters ------- np_array: numpy array RGB image [W x H x C] Returns ------- numpy array of size [#C x W x H] sparse one-hot encoded multi-class matrix, where #C is the number of classes """ im_np = np_array[:, :, 2].astype(np.int8) nb_classes = len(int_to_one_hot(im_np.max(), '')) class_dict = {x: int_to_one_hot(x, nb_classes) for x in np.unique(im_np)} # create the one hot matrix one_hot_matrix = np.asanyarray( [[class_dict[im_np[i, j]] for j in range(im_np.shape[1])] for i in range(im_np.shape[0])]) return np.rollaxis(one_hot_matrix.astype(np.uint8), 2, 0)
[docs]def multi_one_hot_to_output(matrix): """ This function converts the multi-one-hot encoded matrix to an image like it was provided in the ground truth Parameters ------- tensor of size [#C x W x H] sparse one-hot encoded multi-class matrix, where #C is the number of classes Returns ------- np_array: numpy array RGB image [C x W x H] """ # TODO: fix input and output dims (see one_hot_to_output) # create RGB matrix = np.rollaxis(np.char.mod('%d', matrix.numpy()), 0, 3) zeros = (32 - matrix.shape[2]) * '0' B = np.array([[int('{}{}'.format(zeros, ''.join(matrix[i][j])), 2) for j in range(matrix.shape[1])] for i in range(matrix.shape[0])]) RGB = np.dstack((np.zeros(shape=(matrix.shape[0], matrix.shape[1], 2), dtype=np.int8), B)) return RGB
[docs]def HSVToRGB(h, s, v): (r, g, b) = colorsys.hsv_to_rgb(h, s, v) return (int(255 * r), int(255 * g), int(255 * b))
[docs]def get_distinct_colors(n): huePartition = 1.0 / (n + 1) return [HSVToRGB(huePartition * value, 1.0, 1.0) for value in range(0, n)]
[docs]def make_colour_legend_image(img_name, colour_encoding): import matplotlib.pyplot as plt labels = sorted(colour_encoding.keys()) colors = [tuple(np.array(colour_encoding[k])/255) for k in labels] f = lambda m, c: plt.plot([], [], marker=m, color=c, ls="none")[0] handles = [f("s", c) for c in colors] legend = plt.legend(handles, labels, loc=3, framealpha=1, frameon=False) fig = legend.figure fig.canvas.draw() bbox = legend.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) fig.savefig(img_name, dpi=1000, bbox_inches=bbox)