|
import numpy as np |
|
import torchvision.utils as vutils |
|
|
|
|
|
def get_np_imgrid(array, nrow=3, padding=0, pad_value=0): |
|
''' |
|
achieves the same function of torchvision.utils.make_grid for |
|
numpy array |
|
''' |
|
|
|
n, h, w, c = array.shape |
|
row_num = n // nrow + (n % nrow != 0) |
|
gh, gw = row_num*h + padding*(row_num-1), nrow*w + padding*(nrow - 1) |
|
grid = np.ones((gh, gw, c), dtype=array.dtype) * pad_value |
|
for i in range(n): |
|
grow, gcol = i // nrow, i % nrow |
|
off_y, off_x = grow * (h + padding), gcol * (w + padding) |
|
grid[off_y : off_y + h, off_x : off_x + w] = array[i] |
|
return grid |
|
|
|
|
|
def split_np_imgrid(imgrid, nimg, nrow, padding=0): |
|
''' |
|
reverse operation of make_grid. |
|
args: |
|
imgrid: HWC image grid |
|
nimg: number of images in the grid |
|
nrow: number of columns in image grid |
|
return: |
|
images: list, contains splitted images |
|
''' |
|
row_num = nimg // nrow + (nimg % nrow != 0) |
|
gh, gw, _ = imgrid.shape |
|
h, w = (gh - (row_num-1)*padding)//row_num, (gw - (nrow-1)*padding)//nrow |
|
images = [] |
|
for gid in range(nimg): |
|
grow, gcol = gid // nrow, gid % nrow |
|
off_i, off_j = grow * (h + padding), gcol * (w + padding) |
|
images.append(imgrid[off_i:off_i+h, off_j:off_j+w]) |
|
return images |
|
|
|
|
|
class MDTableConvertor: |
|
|
|
def __init__(self, col_num): |
|
self.col_num = col_num |
|
|
|
def _get_table_row(self, items): |
|
row = '' |
|
for item in items: |
|
row += '| {:s} '.format(item) |
|
row += '|\n' |
|
return row |
|
|
|
def convert(self, item_list, title=None): |
|
''' |
|
args: |
|
item_list: a list of items (str or can be converted to str) |
|
that want to be presented in table. |
|
|
|
title: None, or a list of strings. When set to None, empty title |
|
row is used and column number is determined by col_num; Otherwise, |
|
it will be used as title row, its length will override col_num. |
|
|
|
return: |
|
table: markdown table string. |
|
''' |
|
table = '' |
|
if title: |
|
col_num = len(title) |
|
table += self._get_table_row(title) |
|
else: |
|
col_num=self.col_num |
|
table += self._get_table_row([' ']*col_num) |
|
table += self._get_table_row(['-'] * col_num) |
|
for i in range(0, len(item_list), col_num): |
|
table += self._get_table_row(item_list[i:i+col_num]) |
|
return table |
|
|
|
|
|
def visual_dict_to_imgrid(visual_dict, col_num=4, padding=0): |
|
''' |
|
args: |
|
visual_dict: a dictionary of images of the same size |
|
col_num: number of columns in image grid |
|
padding: number of padding pixels to seperate images |
|
''' |
|
im_names = [] |
|
im_tensors = [] |
|
for name, visual in visual_dict.items(): |
|
im_names.append(name) |
|
im_tensors.append(visual) |
|
im_grid = vutils.make_grid(im_tensors, |
|
nrow=col_num , |
|
padding=0, |
|
pad_value=1.0) |
|
layout = MDTableConvertor(col_num).convert(im_names) |
|
|
|
return im_grid, layout |
|
|
|
|
|
def count_parameters(model, trainable_only=False): |
|
return sum(p.numel() for p in model.parameters()) |
|
|
|
|
|
|
|
class WarmupExpLRScheduler(object): |
|
def __init__(self, lr_start=1e-4, lr_max=4e-4, lr_min=5e-6, rampup_epochs=4, sustain_epochs=0, exp_decay=0.75): |
|
self.lr_start = lr_start |
|
self.lr_max = lr_max |
|
self.lr_min = lr_min |
|
self.rampup_epochs = rampup_epochs |
|
self.sustain_epochs = sustain_epochs |
|
self.exp_decay = exp_decay |
|
|
|
def __call__(self, epoch): |
|
if epoch < self.rampup_epochs: |
|
lr = (self.lr_max - self.lr_start) / self.rampup_epochs * epoch + self.lr_start |
|
elif epoch < self.rampup_epochs + self.sustain_epochs: |
|
lr = self.lr_max |
|
else: |
|
lr = (self.lr_max - self.lr_min) * self.exp_decay**(epoch - self.rampup_epochs - self.sustain_epochs) + self.lr_min |
|
|
|
return lr |