raven / raven_utils /image.py
Jakub Kwiatkowski
Refactor.
38f87b5
raw
history blame
15 kB
import math
import os
from itertools import groupby
import numpy as np
from matplotlib import pyplot as plt, colors
from skimage.transform import resize
# todo different backend maybe diffrent files thata have partial
# todo depend on mode different min and max
from raven_utils.tools import is_num, il, image_type, lw, is_int, dict_from_list, if_images, download_file, \
dir_decorator, list_dir
TRAIN = "train"
VAL = "val"
TEST = "test"
COLOR = "color"
SIZE = "size"
EXIST = "exist"
COR = "cor"
TARGET_SIZE = "target_size"
# https://github.com/qubvel/efficientnet/blob/master/efficientnet/preprocessing.py
MAP_INTERPOLATION_TO_ORDER = {
"nearest": 0,
"bilinear": 1,
"biquadratic": 2,
"bicubic": 3,
}
def center_crop_and_resize(image, image_size, crop_padding=32, interpolation="bicubic"):
assert image.ndim in {2, 3}
assert interpolation in MAP_INTERPOLATION_TO_ORDER.keys()
h, w = image.shape[:2]
padded_center_crop_size = int(
(image_size / (image_size + crop_padding)) * min(h, w)
)
offset_height = ((h - padded_center_crop_size) + 1) // 2
offset_width = ((w - padded_center_crop_size) + 1) // 2
image_crop = image[
offset_height: padded_center_crop_size + offset_height,
offset_width: padded_center_crop_size + offset_width,
]
resized_image = resize(
image_crop,
(image_size, image_size),
order=MAP_INTERPOLATION_TO_ORDER[interpolation],
preserve_range=True,
)
return resized_image
def im_conv(image):
return image.astype(np.float32) / 255.
def add_dim(x):
return x[..., None]
def im_conv_black(image):
return image[..., np.newaxis].astype(np.float32) / 255.
def im_resolve(image):
return (image * 255).astype(np.uint8)
def minmax(data, axis=None, scale=1.0):
return ((data - np.min(data, axis=axis)) / (np.max(data, axis=axis) - np.min(data, axis=axis))) * scale
def inverse(x):
return 1.0 - x
def standarize(img, scaler=0.15, batch=True):
indexes = tuple(range(1 if batch else 0, img.ndim))
img = np.array(img)
img -= np.mean(img, axis=indexes)[tuple([slice(None)] + [None] * len(indexes))]
img /= np.std(img, axis=indexes)[tuple([slice(None)] + [None] * len(indexes))] + 1e-5
img *= scaler
return img
def image_standardization(img, scaler=0.15):
indexes = tuple(range(1, 4))
img -= np.mean(img, axis=indexes)
img /= np.std(img, axis=indexes) + 1e-5
img *= scaler
return img
def clip_standardized(img, min=0, max=1):
img += (max - min) / 2
return np.clip(img, min, max)
def deprocess_image(img, clip=True):
# Normalize array: center on 0., ensure variance is 0.15
img = image_standardization(img)
if clip:
img = clip_standardized(img)
return img
# def im_conv_tf(image,dtype=tf.float32):
# return tf.image.convert_image_dtype(image,dtype)
# todo Grid on edges
# todo different channels
def draw_images(data, col=-1, row=-1, grid=True, border=None, mark=None, grid_args={}, border_kwargs={}, *args,
**kwargs):
figure, shape = create_grid(data, row=row, col=col, *args, **kwargs)
if border is not None:
add_border(figure, border, **border_kwargs)
figure = create_image_from_grid(figure, *args, **kwargs)
if grid:
figure = add_grid(figure, shape[-3:], **grid_args)
return figure
def draw_images2(data, col=-1, row=-1, grid=True, color=1, border=None, border_kwargs={}, *args,
**kwargs):
figure, shape = create_grid(data, row=row, col=col, *args, **kwargs)
if border is not None:
add_border(figure, border, **border_kwargs)
figure = fill_canvas(figure, color=color, grid=grid)
return figure
def draw_images3(data, col=-1, row=-1, grid=True, border=None, mark=None, grid_args={}, border_kwargs={}, *args,
**kwargs):
if len(np.asarray(data).shape) < 4:
return data
return draw_images(data=data,
col=col,
row=row,
grid=grid,
border=border,
mark=mark,
grid_args=grid_args,
border_kwargs=border_kwargs,
*args,
**kwargs
)
def fill_canvas(a, color=1, grid=10):
if not isinstance(a, np.ndarray):
a = np.asarray(a)
if grid is True:
grid = 10
shape = a.shape
move_r = shape[2] + grid
move_c = shape[3] + grid
if color is None:
color = 0
if is_num(color):
color = np.full((move_r * shape[0], move_c * shape[1]) + shape[-1:], fill_value=color, dtype=a.dtype)
for r in range(shape[0]):
for c in range(shape[1]):
color[move_r * r:move_r * r + shape[2], move_c * c:move_c * c + shape[3]] = a[r, c]
return color
def create_grid(
data,
row=-1,
col=-1,
mode="auto",
# Method for creating grid if row and col not set. Even evenly for row and cows. Shape take shape from input data. Auto use shape for input data that have more then 4 dimention, otherwise even.
swap=False, # swaping row and columns
*args, **kwargs):
if il(data):
for i, d in enumerate(data):
if len(d.shape) > 4:
data[i] = draw_images(d)
elif len(d.shape) < 3:
data[i] = d[..., np.newaxis]
shapes = [d.shape for d in data]
# padding to make all images same size
if not all_equal(shapes):
mh = np.max([sh[-3] for sh in shapes])
mw = np.max([sh[-2] for sh in shapes])
canvas = np.zeros((len(data), mh, mw, shapes[0][-1]))
for j, d in enumerate(data):
canvas[j, :d.shape[-3], :d.shape[-2]] = d
data = canvas
data = np.asarray(data)
shape = data.shape
# check in case of no channels
if shape[-1] > 4:
data = data[..., np.newaxis]
shape = data.shape
if swap and len(shape) > 4:
data = data.swapaxes(-4, -5)
h = shape[-3]
w = shape[-2]
c = shape[-1]
if len(shape) > 4:
im_no = shape[-5] * shape[-4]
elif len(shape) == 4:
im_no = shape[-4]
else:
return data, shape
if row == -1 and col == -1:
if mode == "auto":
mode = "shape" if len(shape) > 4 else "even"
if mode == "shape":
row = shape[-5] if len(shape) > 4 else shape[-4]
elif mode == "even":
row = math.ceil(np.sqrt(im_no))
else:
row = shape[-5] if len(shape) > 4 else math.ceil(np.sqrt(im_no))
# eh = -1 if row == -1 else row * h
# ew = -1 if column == -1 else column * w
# if number of images is less then available slots in grid add blank images
if row == -1:
eh = -1
m = im_no % col
if m != 0:
data = np.concatenate([data, np.zeros((col - m,) + shape[-3:])], axis=-4)
else:
eh = row * h
if col == -1:
ew = -1
m = im_no % row
if m != 0:
data = np.concatenate([data, np.zeros((row - m,) + shape[-3:])], axis=-4)
else:
ew = col * h
figure = data.reshape(row, col, h, w, c)
import time
time.sleep(0.1)
return figure, shape
def create_image_from_grid(grid, *args, **kwargs):
shape = grid.shape
figure = grid.swapaxes(1, 2).reshape(shape[0] * shape[2], shape[1] * shape[3], shape[4])
return figure
# todo For more dimention
def add_grid(figure, shape, left=True, right=True, color=1.00, hor=True, ver=True):
revers = False
if len(figure.shape) == 2:
figure = figure[..., None]
reverse = True
if hor:
figure[shape[0]::shape[0], :, :] = color
if ver:
figure[:, shape[1]::shape[1], :] = color
if left:
figure[0, :, :] = color
figure[0, :] = color
if right:
figure[figure.shape[0] - 1, :, :] = color
figure[:, ::figure.shape[1] - 1, :] = color
if revers:
figure = figure[..., 0]
return figure
def add_border(figure, cor, exist=True, size=4, color="auto"):
if color == "auto":
info = image_type(figure)
if info.mode == "RGB":
color = np.array(colors.to_rgb("red")) * info.range_[1]
else:
color = info.range_[1]
if info.reverse:
color = info.range_[1] - color
shape = figure.shape
for c in lw(cor):
if not isinstance(c, dict):
c = dict_from_list([COR, EXIST, SIZE, COLOR], c)
c = {
**{
EXIST: exist if il(exist) else [exist] * 4,
SIZE: size,
COLOR: color
},
**c
}
if is_int(lw(c[EXIST])[0]):
c[EXIST] = K.mask(4, c[EXIST])
if is_int(c[COR]):
c[COR] = c[COR] // shape[1], c[COR] % shape[1]
isinstance(lw(c[EXIST])[0], np.integer)
cr = c[COR]
if c[EXIST][0]:
figure[cr[0], cr[1], 0:c[SIZE], :] = c[COLOR]
if c[EXIST][1]:
figure[cr[0], cr[1], :, shape[3] - c[SIZE]:shape[3]] = c[COLOR]
if c[EXIST][2]:
figure[cr[0], cr[1], shape[2] - c[SIZE]:shape[2], :] = c[COLOR]
if c[EXIST][3]:
figure[cr[0], cr[1], :, 0:c[SIZE]] = c[COLOR]
return figure
# todo hgih tmp fix
def get_digitilizer(min=0, max=1, steps=10, mode="numpy"):
bins = np.linspace(min, max, steps)
bins = bins + (bins[1] - bins[0]) / 2
# if mode == "tf":
# from tensorflow.ops import math_ops
# func = math_ops._bucketize
# bins = list(bins)
# else:
func = np.digitize
def digitilizer(x):
return func(x, bins)
return digitilizer
def save_image_pil(path, data, *args, **kwargs):
from PIL import Image
if not isinstance(data, np.ndarray):
data = np.array(data)
if if_images(data):
data = draw_images2(data, *args, **kwargs)
if np.max(data) <= 1:
data = im_resolve(data)
if np.shape(data)[-1] == 1:
data = data[..., 0]
img = Image.fromarray(np.array(data, dtype="uint8"))
# test if black or wihte
if data.shape[-1] not in [3, 4]:
img = img.convert('L')
# todo maybe just check type for np.float
if path is None:
img.show()
else:
img.save(path)
def save_image_pil2(path, data, *args, **kwargs):
from PIL import Image
if np.max(data) <= 1:
data = im_resolve(data)
if len(np.shape(data)) == 3 and np.shape(data)[-1] == 1:
data = data[..., 0]
img = Image.fromarray(data)
if len(data.shape) == 2 or (len(data.shape == 3) and data.shape[2] == 1):
img = img.convert('L')
# todo maybe just check type for np.float
if path is None:
img.show()
else:
img.save(path)
#
# https://towardsdatascience.com/how-to-create-a-gif-from-matplotlib-plots-in-python-6bec6c0c952c
# duration, fps, loop
def save_gif(path, data,sort_fn="first", *args, **kwargs):
data = get_paths_(data, sort_fn=sort_fn)
import imageio
with imageio.get_writer(path, mode='I', *args, **kwargs) as writer:
for filename in data:
image = imageio.imread(filename)
writer.append_data(image)
# https://stackoverflow.com/questions/44947505/how-to-make-a-movie-out-of-images-in-python
# https://docs.opencv.org/4.x/dd/d9e/classcv_1_1VideoWriter.html#ad59c61d8881ba2b2da22cff5487465b5
# https://github.com/ContinuumIO/anaconda-issues/issues/223
# https://softron.zendesk.com/hc/en-us/articles/207695697-List-of-FourCC-codes-for-video-codecs
def save_video(path, data, *args, fps=1, fourcc="MJPG",sort_fn="first", **kwargs):
data = get_paths_(data, sort_fn=sort_fn)
import cv2
frame = cv2.imread(data[0])
height, width, layers = frame.shape
if isinstance(fourcc, str):
fourcc = cv2.VideoWriter_fourcc(*fourcc)
video = cv2.VideoWriter(path, fourcc, fps, (width, height))
for image in data:
video.write(cv2.imread(image))
cv2.destroyAllWindows()
video.release()
def get_paths_(data, sort_fn="first"):
if isinstance(data, str):
data = list_dir(data, sort_fn=sort_fn)
return data
def add_text(ax, text, pos=None, star_x=25, start_y=25, row_size=30):
text = lw(text)
if pos is None:
pos = [(star_x, start_y + i * row_size) for i in range(len(text))]
pos = lw(pos)
for i, t in enumerate(text):
# draw.text(pos[i], t,fill="black")
ax.text(pos[i][0], pos[i][1], t)
@dir_decorator
def save_image(path, data, description=None, size=None, dpi=None, *args, **kwargs):
if not isinstance(data, np.ndarray):
data = np.array(data)
fig = plt.figure()
if size is not None:
fig.set_size_inches(size)
# if title is not None:
# plt.title(title)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
if len(data.shape) == 2 or data.shape[-1] == 1:
# plt.set_cmap('Greys_r')
plt.set_cmap('Greys')
else:
plt.set_cmap('hot')
if if_images(data):
data = draw_images(data, *args, **kwargs)
ax.imshow(data, aspect='equal')
if description is not None:
add_text(ax, description, star_x=data.shape[1])
params = {
"fname": path,
"bbox_inches": 'tight',
"pad_inches": 0
}
if dpi is not None:
params['dpi'] = dpi
if path is None:
plt.show()
else:
plt.savefig(**params)
plt.clf()
SAMPLE_IMAGES = {
"panda": "~/all/dataset/examples/panda.jpg",
"cat": "~/all/dataset/examples/cat.jpg",
}
def load_image(path=None, dtype="uint8", *args, **kwargs):
from tensorflow.keras.preprocessing import image
if path is None:
path = os.path.expanduser(SAMPLE_IMAGES["panda"])
if not os.path.isfile(path):
os.makedirs(os.path.dirname(path), exist_ok=True)
download_file("https://upload.wikimedia.org/wikipedia/commons/f/fe/Giant_Panda_in_Beijing_Zoo_1.JPG", path)
if not TARGET_SIZE in kwargs:
kwargs[TARGET_SIZE] = (224, 224)
elif path == "cat":
path = os.path.expanduser(SAMPLE_IMAGES["cat"])
kwargs[TARGET_SIZE] = (224, 224)
img = image.load_img(path, *args, **kwargs)
img = image.img_to_array(img, dtype=dtype)
return img
def to_rgb(img, add_dim=True, check_dim=True):
shape = np.shape(img)
if add_dim and shape[-1] > 4:
img = img[..., None]
shape = np.shape(img)
if check_dim and shape[-1] != 1:
return img
return np.tile(img, reps=(1, 1, 3))
# https://stackoverflow.com/questions/3844801/check-if-all-elements-in-a-list-are-identical
def all_equal(iterable, key=None):
g = groupby(iterable, key=key)
return next(g, True) and not next(g, False)