Create utils.py
Browse files- RealESRGAN/utils.py +135 -0
RealESRGAN/utils.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
from PIL import Image
|
| 4 |
+
import os
|
| 5 |
+
import io
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def pad_reflect(image, pad_size):
|
| 9 |
+
imsize = image.shape
|
| 10 |
+
height, width = imsize[:2]
|
| 11 |
+
new_img = np.zeros([height + pad_size * 2, width + pad_size * 2, imsize[2]]).astype(np.uint8)
|
| 12 |
+
new_img[pad_size:-pad_size, pad_size:-pad_size, :] = image
|
| 13 |
+
|
| 14 |
+
new_img[0:pad_size, pad_size:-pad_size, :] = np.flip(image[0:pad_size, :, :], axis=0) # top
|
| 15 |
+
new_img[-pad_size:, pad_size:-pad_size, :] = np.flip(image[-pad_size:, :, :], axis=0) # bottom
|
| 16 |
+
new_img[:, 0:pad_size, :] = np.flip(new_img[:, pad_size:pad_size * 2, :], axis=1) # left
|
| 17 |
+
new_img[:, -pad_size:, :] = np.flip(new_img[:, -pad_size * 2:-pad_size, :], axis=1) # right
|
| 18 |
+
|
| 19 |
+
return new_img
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def unpad_image(image, pad_size):
|
| 23 |
+
return image[pad_size:-pad_size, pad_size:-pad_size, :]
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def process_array(image_array, expand=True):
|
| 27 |
+
""" Process a 3-dimensional array into a scaled, 4 dimensional batch of size 1. """
|
| 28 |
+
|
| 29 |
+
image_batch = image_array / 255.0
|
| 30 |
+
if expand:
|
| 31 |
+
image_batch = np.expand_dims(image_batch, axis=0)
|
| 32 |
+
return image_batch
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def process_output(output_tensor):
|
| 36 |
+
""" Transforms the 4-dimensional output tensor into a suitable image format. """
|
| 37 |
+
|
| 38 |
+
sr_img = output_tensor.clip(0, 1) * 255
|
| 39 |
+
sr_img = np.uint8(sr_img)
|
| 40 |
+
return sr_img
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def pad_patch(image_patch, padding_size, channel_last=True):
|
| 44 |
+
""" Pads image_patch with with padding_size edge values. """
|
| 45 |
+
|
| 46 |
+
if channel_last:
|
| 47 |
+
return np.pad(
|
| 48 |
+
image_patch,
|
| 49 |
+
((padding_size, padding_size), (padding_size, padding_size), (0, 0)),
|
| 50 |
+
'edge',
|
| 51 |
+
)
|
| 52 |
+
else:
|
| 53 |
+
return np.pad(
|
| 54 |
+
image_patch,
|
| 55 |
+
((0, 0), (padding_size, padding_size), (padding_size, padding_size)),
|
| 56 |
+
'edge',
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def unpad_patches(image_patches, padding_size):
|
| 61 |
+
return image_patches[:, padding_size:-padding_size, padding_size:-padding_size, :]
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def split_image_into_overlapping_patches(image_array, patch_size, padding_size=2):
|
| 65 |
+
""" Splits the image into partially overlapping patches.
|
| 66 |
+
The patches overlap by padding_size pixels.
|
| 67 |
+
Pads the image twice:
|
| 68 |
+
- first to have a size multiple of the patch size,
|
| 69 |
+
- then to have equal padding at the borders.
|
| 70 |
+
Args:
|
| 71 |
+
image_array: numpy array of the input image.
|
| 72 |
+
patch_size: size of the patches from the original image (without padding).
|
| 73 |
+
padding_size: size of the overlapping area.
|
| 74 |
+
"""
|
| 75 |
+
|
| 76 |
+
xmax, ymax, _ = image_array.shape
|
| 77 |
+
x_remainder = xmax % patch_size
|
| 78 |
+
y_remainder = ymax % patch_size
|
| 79 |
+
|
| 80 |
+
# modulo here is to avoid extending of patch_size instead of 0
|
| 81 |
+
x_extend = (patch_size - x_remainder) % patch_size
|
| 82 |
+
y_extend = (patch_size - y_remainder) % patch_size
|
| 83 |
+
|
| 84 |
+
# make sure the image is divisible into regular patches
|
| 85 |
+
extended_image = np.pad(image_array, ((0, x_extend), (0, y_extend), (0, 0)), 'edge')
|
| 86 |
+
|
| 87 |
+
# add padding around the image to simplify computations
|
| 88 |
+
padded_image = pad_patch(extended_image, padding_size, channel_last=True)
|
| 89 |
+
|
| 90 |
+
xmax, ymax, _ = padded_image.shape
|
| 91 |
+
patches = []
|
| 92 |
+
|
| 93 |
+
x_lefts = range(padding_size, xmax - padding_size, patch_size)
|
| 94 |
+
y_tops = range(padding_size, ymax - padding_size, patch_size)
|
| 95 |
+
|
| 96 |
+
for x in x_lefts:
|
| 97 |
+
for y in y_tops:
|
| 98 |
+
x_left = x - padding_size
|
| 99 |
+
y_top = y - padding_size
|
| 100 |
+
x_right = x + patch_size + padding_size
|
| 101 |
+
y_bottom = y + patch_size + padding_size
|
| 102 |
+
patch = padded_image[x_left:x_right, y_top:y_bottom, :]
|
| 103 |
+
patches.append(patch)
|
| 104 |
+
|
| 105 |
+
return np.array(patches), padded_image.shape
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def stich_together(patches, padded_image_shape, target_shape, padding_size=4):
|
| 109 |
+
""" Reconstruct the image from overlapping patches.
|
| 110 |
+
After scaling, shapes and padding should be scaled too.
|
| 111 |
+
Args:
|
| 112 |
+
patches: patches obtained with split_image_into_overlapping_patches
|
| 113 |
+
padded_image_shape: shape of the padded image contructed in split_image_into_overlapping_patches
|
| 114 |
+
target_shape: shape of the final image
|
| 115 |
+
padding_size: size of the overlapping area.
|
| 116 |
+
"""
|
| 117 |
+
|
| 118 |
+
xmax, ymax, _ = padded_image_shape
|
| 119 |
+
patches = unpad_patches(patches, padding_size)
|
| 120 |
+
patch_size = patches.shape[1]
|
| 121 |
+
n_patches_per_row = ymax // patch_size
|
| 122 |
+
|
| 123 |
+
complete_image = np.zeros((xmax, ymax, 3))
|
| 124 |
+
|
| 125 |
+
row = -1
|
| 126 |
+
col = 0
|
| 127 |
+
for i in range(len(patches)):
|
| 128 |
+
if i % n_patches_per_row == 0:
|
| 129 |
+
row += 1
|
| 130 |
+
col = 0
|
| 131 |
+
complete_image[
|
| 132 |
+
row * patch_size: (row + 1) * patch_size, col * patch_size: (col + 1) * patch_size, :
|
| 133 |
+
] = patches[i]
|
| 134 |
+
col += 1
|
| 135 |
+
return complete_image[0: target_shape[0], 0: target_shape[1], :]
|