-
Notifications
You must be signed in to change notification settings - Fork 2
/
numpy_pil_helper.py
112 lines (90 loc) · 4.55 KB
/
numpy_pil_helper.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
import numpy as np
from PIL import Image
Image.MAX_IMAGE_PIXELS = None
PIL_TILE_SIZE = 30000
def numpy_to_pil_binary(img_arr: "NDArray[bool]"):
'''
Convert a 2D numpy boolean array to a mode '1' (binary) PIL image.
Input:
img_arr : 2D numpy boolean array representing a binary image.
Output:
img : PIL mode '1' (binary) image.
'''
assert img_arr.dtype == np.bool, 'Wrong numpy dtype, expected "np.bool" but got "{}"'.format(img_arr.dtype)
assert img_arr.ndim == 2, 'Wrong numpy dimension, expected "2" but got "{}" with shape {}'.format(img_arr.ndim, img_arr.shape)
size = img_arr.shape[::-1]
databytes = np.packbits(img_arr, axis=1)
return Image.frombytes(mode='1', size=size, data=databytes)
def numpy_to_pil_palette(img_arr: "NDArray[np.uint8]"):
'''
Convert a 2D numpy uint8 array to a mode 'P' (palette) PIL image.
Input:
img_arr : 2D numpy uint8 array representing an indexed image.
Output:
img : PIL mode 'P' (palette) image.
'''
assert img_arr.dtype == np.uint8, 'Wrong numpy dtype, expected "np.uint8" but got "{}"'.format(img_arr.dtype)
assert img_arr.ndim == 2, 'Wrong numpy dimension, expected "2" but got "{}" with shape {}'.format(img_arr.ndim, img_arr.shape)
# Iteration of creating PIL image tile by tile
height, width = img_arr.shape
iters = np.uint8(np.ceil([height / PIL_TILE_SIZE, width / PIL_TILE_SIZE]))
img = Image.new('P', (width, height))
for row in range(iters[0]):
for col in range(iters[1]):
# Get start and end pixel location
start_r, start_c = row * PIL_TILE_SIZE, col * PIL_TILE_SIZE
end_r, end_c = min(height, start_r + PIL_TILE_SIZE), min(width, start_c + PIL_TILE_SIZE)
# Paste the tile into image
img_tile = Image.fromarray(img_arr[start_r:end_r, start_c:end_c], 'P')
img.paste(img_tile, (start_c, start_r))
del img_tile
return img
def numpy_to_pil_grayscale(img_arr: "NDArray[np.uint8]"):
'''
Convert a 2D numpy uint8 array to a mode 'L' (grayscale) PIL image.
Input:
img_arr : 2D numpy uint8 array representing a grayscale image.
Output:
img : PIL mode 'L' (grayscale) image.
'''
assert img_arr.dtype == np.uint8, 'Wrong numpy dtype, expected "np.uint8" but got "{}"'.format(img_arr.dtype)
assert img_arr.ndim == 2, 'Wrong numpy dimension, expected "2" but got "{}" with shape {}'.format(img_arr.ndim, img_arr.shape)
# Iteration of creating PIL image tile by tile
height, width = img_arr.shape
iters = np.uint8(np.ceil([height / PIL_TILE_SIZE, width / PIL_TILE_SIZE]))
img = Image.new('L', (width, height))
for row in range(iters[0]):
for col in range(iters[1]):
# Get start and end pixel location
start_r, start_c = row * PIL_TILE_SIZE, col * PIL_TILE_SIZE
end_r, end_c = min(height, start_r + PIL_TILE_SIZE), min(width, start_c + PIL_TILE_SIZE)
# Paste the tile into image
img_tile = Image.fromarray(img_arr[start_r:end_r, start_c:end_c], 'L')
img.paste(img_tile, (start_c, start_r))
del img_tile
return img
def numpy_to_pil_rgb(img_arr: "NDArray[np.uint8]"):
'''
Convert a 3D numpy uint8 array to a mode 'RGB' (8-bit each channel) PIL image.
Input:
img_arr : 3D numpy uint8 array representing an RGB image, (..., ..., 3).
Output:
img : PIL mode 'RGB' (8-bit each channel) image.
'''
assert img_arr.dtype == np.uint8, 'Wrong numpy dtype, expected "np.uint8" but got "{}"'.format(img_arr.dtype)
assert img_arr.ndim == 3, 'Wrong numpy dimension, expected "3" but got "{}" with shape {}'.format(img_arr.ndim, img_arr.shape)
assert img_arr.shape[2] == 3, 'Wrong number of channels, expected "3" but got with shape {}'.format(img_arr.shape)
# Iteration of creating PIL image tile by tile
height, width, _ = img_arr.shape
iters = np.uint8(np.ceil([height / PIL_TILE_SIZE, width / PIL_TILE_SIZE]))
img = Image.new('RGB', (width, height))
for row in range(iters[0]):
for col in range(iters[1]):
# Get start and end pixel location
start_r, start_c = row * PIL_TILE_SIZE, col * PIL_TILE_SIZE
end_r, end_c = min(height, start_r + PIL_TILE_SIZE), min(width, start_c + PIL_TILE_SIZE)
# Paste the tile into image
img_tile = Image.fromarray(img_arr[start_r:end_r, start_c:end_c], 'RGB')
img.paste(img_tile, (start_c, start_r))
del img_tile
return img