-
-
Notifications
You must be signed in to change notification settings - Fork 30
/
tile.py
134 lines (109 loc) · 3.5 KB
/
tile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import collections
import math
import numpy as np
from .centerize import centerize
from .color import gray2rgb
from .color import rgb2rgba
def _tile(imgs, shape, border=None, border_width=None):
y_num, x_num = shape
tile_h, tile_w, channel = imgs[0].shape
if border is None:
border_width = 0
dst = np.zeros(
(
tile_h * y_num + border_width * (y_num - 1),
tile_w * x_num + border_width * (x_num - 1),
channel,
),
dtype=np.uint8,
)
if border is not None:
dst[...] = border
for y in range(y_num):
for x in range(x_num):
i = x + y * x_num
if i < len(imgs):
y1 = y * tile_h + y * border_width
y2 = y1 + tile_h
x1 = x * tile_w + x * border_width
x2 = x1 + tile_w
dst[y1:y2, x1:x2] = imgs[i]
return dst
def _get_tile_shape(num, hw_ratio=1):
r_num = int(round(math.sqrt(num / hw_ratio))) # weighted by wh_ratio
c_num = 0
while r_num * c_num < num:
c_num += 1
while (r_num - 1) * c_num >= num:
r_num -= 1
return r_num, c_num
def tile(
imgs,
shape=None,
cval=None,
border=None,
border_width=None,
):
"""Tile images.
Parameters
----------
imgs: numpy.ndarray
Image list which should be tiled.
shape: tuple of int
Tile shape.
cval: array-like, optional
Color to fill the background. Default is (0, 0, 0).
border: array-like, optional
Color for the border. If None, the border is not drawn.
border_width: int
Pixel size of the border.
Returns
-------
dst: numpy.ndarray
Tiled image.
"""
imgs = list(imgs) # copy
# get max tile size to which each image should be resized
max_h, max_w = np.array([img.shape[:2] for img in imgs]).max(axis=0)
if shape is None:
shape = _get_tile_shape(len(imgs), hw_ratio=1.0 * max_h / max_w)
else:
assert isinstance(shape, collections.abc.Iterable)
assert len(shape) == 2
assert isinstance(shape[0], int)
assert isinstance(shape[1], int)
if shape[0] < 0 and shape[1] > 0:
shape = (math.ceil(len(imgs) / shape[1]), shape[1])
elif shape[1] < 0 and shape[0] > 0:
shape = (shape[0], math.ceil(len(imgs) / shape[0]))
else:
assert shape[0] > 0 and shape[1] > 0
imgs = imgs[: shape[0] * shape[1]]
if cval is None:
cval = 0
if border is not None:
border = np.asarray(border, dtype=np.uint8)
if border_width is None:
border_width = 3
ndim = max(img.ndim for img in imgs)
if ndim == 3:
channel = max(img.shape[2] for img in imgs if img.ndim == 3)
else:
ndim = 3 # gray images will be converted to rgb
channel = 3 # all gray
assert channel in [3, 4]
# tile images
for i in range(shape[0] * shape[1]):
if i < len(imgs):
img = imgs[i]
assert img.dtype == np.uint8
if ndim == 3 and img.ndim == 2:
img = gray2rgb(img)
if channel == 4 and img.shape[2] == 3:
img = rgb2rgba(img)
img = centerize(src=img, shape=(max_h, max_w, channel), cval=cval)
imgs[i] = img
else:
img = np.full((max_h, max_w, channel), cval, dtype=np.uint8)
imgs.append(img)
return _tile(imgs=imgs, shape=shape, border=border, border_width=border_width)