MultiMatrix commited on
Commit
ae81eb6
·
verified ·
1 Parent(s): b708a95

Upload 4 files

Browse files
dataset/codeformer.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Sequence, Dict, Union, List, Mapping, Any, Optional
2
+ import math
3
+ import time
4
+ import io
5
+ import random
6
+
7
+ import numpy as np
8
+ import cv2
9
+ from PIL import Image
10
+ import torch.utils.data as data
11
+
12
+ from dataset.degradation import (
13
+ random_mixed_kernels,
14
+ random_add_gaussian_noise,
15
+ random_add_jpg_compression
16
+ )
17
+ from dataset.utils import load_file_list, center_crop_arr, random_crop_arr
18
+ from utils.common import instantiate_from_config
19
+
20
+
21
+ class CodeformerDataset(data.Dataset):
22
+
23
+ def __init__(
24
+ self,
25
+ file_list: str,
26
+ file_backend_cfg: Mapping[str, Any],
27
+ out_size: int,
28
+ crop_type: str,
29
+ blur_kernel_size: int,
30
+ kernel_list: Sequence[str],
31
+ kernel_prob: Sequence[float],
32
+ blur_sigma: Sequence[float],
33
+ downsample_range: Sequence[float],
34
+ noise_range: Sequence[float],
35
+ jpeg_range: Sequence[int]
36
+ ) -> "CodeformerDataset":
37
+ super(CodeformerDataset, self).__init__()
38
+ self.file_list = file_list
39
+ self.image_files = load_file_list(file_list)
40
+ self.file_backend = instantiate_from_config(file_backend_cfg)
41
+ self.out_size = out_size
42
+ self.crop_type = crop_type
43
+ assert self.crop_type in ["none", "center", "random"]
44
+ # degradation configurations
45
+ self.blur_kernel_size = blur_kernel_size
46
+ self.kernel_list = kernel_list
47
+ self.kernel_prob = kernel_prob
48
+ self.blur_sigma = blur_sigma
49
+ self.downsample_range = downsample_range
50
+ self.noise_range = noise_range
51
+ self.jpeg_range = jpeg_range
52
+
53
+ def load_gt_image(self, image_path: str, max_retry: int=5) -> Optional[np.ndarray]:
54
+ image_bytes = None
55
+ while image_bytes is None:
56
+ if max_retry == 0:
57
+ return None
58
+ image_bytes = self.file_backend.get(image_path)
59
+ max_retry -= 1
60
+ if image_bytes is None:
61
+ time.sleep(0.5)
62
+ image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
63
+ if self.crop_type != "none":
64
+ if image.height == self.out_size and image.width == self.out_size:
65
+ image = np.array(image)
66
+ else:
67
+ if self.crop_type == "center":
68
+ image = center_crop_arr(image, self.out_size)
69
+ elif self.crop_type == "random":
70
+ image = random_crop_arr(image, self.out_size, min_crop_frac=0.7)
71
+ else:
72
+ assert image.height == self.out_size and image.width == self.out_size
73
+ image = np.array(image)
74
+ # hwc, rgb, 0,255, uint8
75
+ return image
76
+
77
+ def __getitem__(self, index: int) -> Dict[str, Union[np.ndarray, str]]:
78
+ # load gt image
79
+ img_gt = None
80
+ while img_gt is None:
81
+ # load meta file
82
+ image_file = self.image_files[index]
83
+ gt_path = image_file["image_path"]
84
+ prompt = image_file["prompt"]
85
+ img_gt = self.load_gt_image(gt_path)
86
+ if img_gt is None:
87
+ print(f"filed to load {gt_path}, try another image")
88
+ index = random.randint(0, len(self) - 1)
89
+
90
+ # Shape: (h, w, c); channel order: BGR; image range: [0, 1], float32.
91
+ img_gt = (img_gt[..., ::-1] / 255.0).astype(np.float32)
92
+ h, w, _ = img_gt.shape
93
+ if np.random.uniform() < 0.5:
94
+ prompt = ""
95
+
96
+ # ------------------------ generate lq image ------------------------ #
97
+ # blur
98
+ kernel = random_mixed_kernels(
99
+ self.kernel_list,
100
+ self.kernel_prob,
101
+ self.blur_kernel_size,
102
+ self.blur_sigma,
103
+ self.blur_sigma,
104
+ [-math.pi, math.pi],
105
+ noise_range=None
106
+ )
107
+ img_lq = cv2.filter2D(img_gt, -1, kernel)
108
+ # downsample
109
+ scale = np.random.uniform(self.downsample_range[0], self.downsample_range[1])
110
+ img_lq = cv2.resize(img_lq, (int(w // scale), int(h // scale)), interpolation=cv2.INTER_LINEAR)
111
+ # noise
112
+ if self.noise_range is not None:
113
+ img_lq = random_add_gaussian_noise(img_lq, self.noise_range)
114
+ # jpeg compression
115
+ if self.jpeg_range is not None:
116
+ img_lq = random_add_jpg_compression(img_lq, self.jpeg_range)
117
+
118
+ # resize to original size
119
+ img_lq = cv2.resize(img_lq, (w, h), interpolation=cv2.INTER_LINEAR)
120
+
121
+ # BGR to RGB, [-1, 1]
122
+ gt = (img_gt[..., ::-1] * 2 - 1).astype(np.float32)
123
+ # BGR to RGB, [0, 1]
124
+ lq = img_lq[..., ::-1].astype(np.float32)
125
+
126
+ return gt, lq, prompt
127
+
128
+ def __len__(self) -> int:
129
+ return len(self.image_files)
dataset/degradation.py ADDED
@@ -0,0 +1,766 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://github.com/XPixelGroup/BasicSR/blob/master/basicsr/data/degradations.py
2
+ import cv2
3
+ import math
4
+ import numpy as np
5
+ import random
6
+ import torch
7
+ from scipy import special
8
+ from scipy.stats import multivariate_normal
9
+ # from torchvision.transforms.functional_tensor import rgb_to_grayscale
10
+ from torchvision.transforms._functional_tensor import rgb_to_grayscale
11
+
12
+ # -------------------------------------------------------------------- #
13
+ # --------------------------- blur kernels --------------------------- #
14
+ # -------------------------------------------------------------------- #
15
+
16
+
17
+ # --------------------------- util functions --------------------------- #
18
+ def sigma_matrix2(sig_x, sig_y, theta):
19
+ """Calculate the rotated sigma matrix (two dimensional matrix).
20
+
21
+ Args:
22
+ sig_x (float):
23
+ sig_y (float):
24
+ theta (float): Radian measurement.
25
+
26
+ Returns:
27
+ ndarray: Rotated sigma matrix.
28
+ """
29
+ d_matrix = np.array([[sig_x**2, 0], [0, sig_y**2]])
30
+ u_matrix = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])
31
+ return np.dot(u_matrix, np.dot(d_matrix, u_matrix.T))
32
+
33
+
34
+ def mesh_grid(kernel_size):
35
+ """Generate the mesh grid, centering at zero.
36
+
37
+ Args:
38
+ kernel_size (int):
39
+
40
+ Returns:
41
+ xy (ndarray): with the shape (kernel_size, kernel_size, 2)
42
+ xx (ndarray): with the shape (kernel_size, kernel_size)
43
+ yy (ndarray): with the shape (kernel_size, kernel_size)
44
+ """
45
+ ax = np.arange(-kernel_size // 2 + 1., kernel_size // 2 + 1.)
46
+ xx, yy = np.meshgrid(ax, ax)
47
+ xy = np.hstack((xx.reshape((kernel_size * kernel_size, 1)), yy.reshape(kernel_size * kernel_size,
48
+ 1))).reshape(kernel_size, kernel_size, 2)
49
+ return xy, xx, yy
50
+
51
+
52
+ def pdf2(sigma_matrix, grid):
53
+ """Calculate PDF of the bivariate Gaussian distribution.
54
+
55
+ Args:
56
+ sigma_matrix (ndarray): with the shape (2, 2)
57
+ grid (ndarray): generated by :func:`mesh_grid`,
58
+ with the shape (K, K, 2), K is the kernel size.
59
+
60
+ Returns:
61
+ kernel (ndarrray): un-normalized kernel.
62
+ """
63
+ inverse_sigma = np.linalg.inv(sigma_matrix)
64
+ kernel = np.exp(-0.5 * np.sum(np.dot(grid, inverse_sigma) * grid, 2))
65
+ return kernel
66
+
67
+
68
+ def cdf2(d_matrix, grid):
69
+ """Calculate the CDF of the standard bivariate Gaussian distribution.
70
+ Used in skewed Gaussian distribution.
71
+
72
+ Args:
73
+ d_matrix (ndarrasy): skew matrix.
74
+ grid (ndarray): generated by :func:`mesh_grid`,
75
+ with the shape (K, K, 2), K is the kernel size.
76
+
77
+ Returns:
78
+ cdf (ndarray): skewed cdf.
79
+ """
80
+ rv = multivariate_normal([0, 0], [[1, 0], [0, 1]])
81
+ grid = np.dot(grid, d_matrix)
82
+ cdf = rv.cdf(grid)
83
+ return cdf
84
+
85
+
86
+ def bivariate_Gaussian(kernel_size, sig_x, sig_y, theta, grid=None, isotropic=True):
87
+ """Generate a bivariate isotropic or anisotropic Gaussian kernel.
88
+
89
+ In the isotropic mode, only `sig_x` is used. `sig_y` and `theta` is ignored.
90
+
91
+ Args:
92
+ kernel_size (int):
93
+ sig_x (float):
94
+ sig_y (float):
95
+ theta (float): Radian measurement.
96
+ grid (ndarray, optional): generated by :func:`mesh_grid`,
97
+ with the shape (K, K, 2), K is the kernel size. Default: None
98
+ isotropic (bool):
99
+
100
+ Returns:
101
+ kernel (ndarray): normalized kernel.
102
+ """
103
+ if grid is None:
104
+ grid, _, _ = mesh_grid(kernel_size)
105
+ if isotropic:
106
+ sigma_matrix = np.array([[sig_x**2, 0], [0, sig_x**2]])
107
+ else:
108
+ sigma_matrix = sigma_matrix2(sig_x, sig_y, theta)
109
+ kernel = pdf2(sigma_matrix, grid)
110
+ kernel = kernel / np.sum(kernel)
111
+ return kernel
112
+
113
+
114
+ def bivariate_generalized_Gaussian(kernel_size, sig_x, sig_y, theta, beta, grid=None, isotropic=True):
115
+ """Generate a bivariate generalized Gaussian kernel.
116
+
117
+ ``Paper: Parameter Estimation For Multivariate Generalized Gaussian Distributions``
118
+
119
+ In the isotropic mode, only `sig_x` is used. `sig_y` and `theta` is ignored.
120
+
121
+ Args:
122
+ kernel_size (int):
123
+ sig_x (float):
124
+ sig_y (float):
125
+ theta (float): Radian measurement.
126
+ beta (float): shape parameter, beta = 1 is the normal distribution.
127
+ grid (ndarray, optional): generated by :func:`mesh_grid`,
128
+ with the shape (K, K, 2), K is the kernel size. Default: None
129
+
130
+ Returns:
131
+ kernel (ndarray): normalized kernel.
132
+ """
133
+ if grid is None:
134
+ grid, _, _ = mesh_grid(kernel_size)
135
+ if isotropic:
136
+ sigma_matrix = np.array([[sig_x**2, 0], [0, sig_x**2]])
137
+ else:
138
+ sigma_matrix = sigma_matrix2(sig_x, sig_y, theta)
139
+ inverse_sigma = np.linalg.inv(sigma_matrix)
140
+ kernel = np.exp(-0.5 * np.power(np.sum(np.dot(grid, inverse_sigma) * grid, 2), beta))
141
+ kernel = kernel / np.sum(kernel)
142
+ return kernel
143
+
144
+
145
+ def bivariate_plateau(kernel_size, sig_x, sig_y, theta, beta, grid=None, isotropic=True):
146
+ """Generate a plateau-like anisotropic kernel.
147
+
148
+ 1 / (1+x^(beta))
149
+
150
+ Reference: https://stats.stackexchange.com/questions/203629/is-there-a-plateau-shaped-distribution
151
+
152
+ In the isotropic mode, only `sig_x` is used. `sig_y` and `theta` is ignored.
153
+
154
+ Args:
155
+ kernel_size (int):
156
+ sig_x (float):
157
+ sig_y (float):
158
+ theta (float): Radian measurement.
159
+ beta (float): shape parameter, beta = 1 is the normal distribution.
160
+ grid (ndarray, optional): generated by :func:`mesh_grid`,
161
+ with the shape (K, K, 2), K is the kernel size. Default: None
162
+
163
+ Returns:
164
+ kernel (ndarray): normalized kernel.
165
+ """
166
+ if grid is None:
167
+ grid, _, _ = mesh_grid(kernel_size)
168
+ if isotropic:
169
+ sigma_matrix = np.array([[sig_x**2, 0], [0, sig_x**2]])
170
+ else:
171
+ sigma_matrix = sigma_matrix2(sig_x, sig_y, theta)
172
+ inverse_sigma = np.linalg.inv(sigma_matrix)
173
+ kernel = np.reciprocal(np.power(np.sum(np.dot(grid, inverse_sigma) * grid, 2), beta) + 1)
174
+ kernel = kernel / np.sum(kernel)
175
+ return kernel
176
+
177
+
178
+ def random_bivariate_Gaussian(kernel_size,
179
+ sigma_x_range,
180
+ sigma_y_range,
181
+ rotation_range,
182
+ noise_range=None,
183
+ isotropic=True):
184
+ """Randomly generate bivariate isotropic or anisotropic Gaussian kernels.
185
+
186
+ In the isotropic mode, only `sigma_x_range` is used. `sigma_y_range` and `rotation_range` is ignored.
187
+
188
+ Args:
189
+ kernel_size (int):
190
+ sigma_x_range (tuple): [0.6, 5]
191
+ sigma_y_range (tuple): [0.6, 5]
192
+ rotation range (tuple): [-math.pi, math.pi]
193
+ noise_range(tuple, optional): multiplicative kernel noise,
194
+ [0.75, 1.25]. Default: None
195
+
196
+ Returns:
197
+ kernel (ndarray):
198
+ """
199
+ assert kernel_size % 2 == 1, 'Kernel size must be an odd number.'
200
+ assert sigma_x_range[0] < sigma_x_range[1], 'Wrong sigma_x_range.'
201
+ sigma_x = np.random.uniform(sigma_x_range[0], sigma_x_range[1])
202
+ if isotropic is False:
203
+ assert sigma_y_range[0] < sigma_y_range[1], 'Wrong sigma_y_range.'
204
+ assert rotation_range[0] < rotation_range[1], 'Wrong rotation_range.'
205
+ sigma_y = np.random.uniform(sigma_y_range[0], sigma_y_range[1])
206
+ rotation = np.random.uniform(rotation_range[0], rotation_range[1])
207
+ else:
208
+ sigma_y = sigma_x
209
+ rotation = 0
210
+
211
+ kernel = bivariate_Gaussian(kernel_size, sigma_x, sigma_y, rotation, isotropic=isotropic)
212
+
213
+ # add multiplicative noise
214
+ if noise_range is not None:
215
+ assert noise_range[0] < noise_range[1], 'Wrong noise range.'
216
+ noise = np.random.uniform(noise_range[0], noise_range[1], size=kernel.shape)
217
+ kernel = kernel * noise
218
+ kernel = kernel / np.sum(kernel)
219
+ return kernel
220
+
221
+
222
+ def random_bivariate_generalized_Gaussian(kernel_size,
223
+ sigma_x_range,
224
+ sigma_y_range,
225
+ rotation_range,
226
+ beta_range,
227
+ noise_range=None,
228
+ isotropic=True):
229
+ """Randomly generate bivariate generalized Gaussian kernels.
230
+
231
+ In the isotropic mode, only `sigma_x_range` is used. `sigma_y_range` and `rotation_range` is ignored.
232
+
233
+ Args:
234
+ kernel_size (int):
235
+ sigma_x_range (tuple): [0.6, 5]
236
+ sigma_y_range (tuple): [0.6, 5]
237
+ rotation range (tuple): [-math.pi, math.pi]
238
+ beta_range (tuple): [0.5, 8]
239
+ noise_range(tuple, optional): multiplicative kernel noise,
240
+ [0.75, 1.25]. Default: None
241
+
242
+ Returns:
243
+ kernel (ndarray):
244
+ """
245
+ assert kernel_size % 2 == 1, 'Kernel size must be an odd number.'
246
+ assert sigma_x_range[0] < sigma_x_range[1], 'Wrong sigma_x_range.'
247
+ sigma_x = np.random.uniform(sigma_x_range[0], sigma_x_range[1])
248
+ if isotropic is False:
249
+ assert sigma_y_range[0] < sigma_y_range[1], 'Wrong sigma_y_range.'
250
+ assert rotation_range[0] < rotation_range[1], 'Wrong rotation_range.'
251
+ sigma_y = np.random.uniform(sigma_y_range[0], sigma_y_range[1])
252
+ rotation = np.random.uniform(rotation_range[0], rotation_range[1])
253
+ else:
254
+ sigma_y = sigma_x
255
+ rotation = 0
256
+
257
+ # assume beta_range[0] < 1 < beta_range[1]
258
+ if np.random.uniform() < 0.5:
259
+ beta = np.random.uniform(beta_range[0], 1)
260
+ else:
261
+ beta = np.random.uniform(1, beta_range[1])
262
+
263
+ kernel = bivariate_generalized_Gaussian(kernel_size, sigma_x, sigma_y, rotation, beta, isotropic=isotropic)
264
+
265
+ # add multiplicative noise
266
+ if noise_range is not None:
267
+ assert noise_range[0] < noise_range[1], 'Wrong noise range.'
268
+ noise = np.random.uniform(noise_range[0], noise_range[1], size=kernel.shape)
269
+ kernel = kernel * noise
270
+ kernel = kernel / np.sum(kernel)
271
+ return kernel
272
+
273
+
274
+ def random_bivariate_plateau(kernel_size,
275
+ sigma_x_range,
276
+ sigma_y_range,
277
+ rotation_range,
278
+ beta_range,
279
+ noise_range=None,
280
+ isotropic=True):
281
+ """Randomly generate bivariate plateau kernels.
282
+
283
+ In the isotropic mode, only `sigma_x_range` is used. `sigma_y_range` and `rotation_range` is ignored.
284
+
285
+ Args:
286
+ kernel_size (int):
287
+ sigma_x_range (tuple): [0.6, 5]
288
+ sigma_y_range (tuple): [0.6, 5]
289
+ rotation range (tuple): [-math.pi/2, math.pi/2]
290
+ beta_range (tuple): [1, 4]
291
+ noise_range(tuple, optional): multiplicative kernel noise,
292
+ [0.75, 1.25]. Default: None
293
+
294
+ Returns:
295
+ kernel (ndarray):
296
+ """
297
+ assert kernel_size % 2 == 1, 'Kernel size must be an odd number.'
298
+ assert sigma_x_range[0] < sigma_x_range[1], 'Wrong sigma_x_range.'
299
+ sigma_x = np.random.uniform(sigma_x_range[0], sigma_x_range[1])
300
+ if isotropic is False:
301
+ assert sigma_y_range[0] < sigma_y_range[1], 'Wrong sigma_y_range.'
302
+ assert rotation_range[0] < rotation_range[1], 'Wrong rotation_range.'
303
+ sigma_y = np.random.uniform(sigma_y_range[0], sigma_y_range[1])
304
+ rotation = np.random.uniform(rotation_range[0], rotation_range[1])
305
+ else:
306
+ sigma_y = sigma_x
307
+ rotation = 0
308
+
309
+ # TODO: this may be not proper
310
+ if np.random.uniform() < 0.5:
311
+ beta = np.random.uniform(beta_range[0], 1)
312
+ else:
313
+ beta = np.random.uniform(1, beta_range[1])
314
+
315
+ kernel = bivariate_plateau(kernel_size, sigma_x, sigma_y, rotation, beta, isotropic=isotropic)
316
+ # add multiplicative noise
317
+ if noise_range is not None:
318
+ assert noise_range[0] < noise_range[1], 'Wrong noise range.'
319
+ noise = np.random.uniform(noise_range[0], noise_range[1], size=kernel.shape)
320
+ kernel = kernel * noise
321
+ kernel = kernel / np.sum(kernel)
322
+
323
+ return kernel
324
+
325
+
326
+ def random_mixed_kernels(kernel_list,
327
+ kernel_prob,
328
+ kernel_size=21,
329
+ sigma_x_range=(0.6, 5),
330
+ sigma_y_range=(0.6, 5),
331
+ rotation_range=(-math.pi, math.pi),
332
+ betag_range=(0.5, 8),
333
+ betap_range=(0.5, 8),
334
+ noise_range=None):
335
+ """Randomly generate mixed kernels.
336
+
337
+ Args:
338
+ kernel_list (tuple): a list name of kernel types,
339
+ support ['iso', 'aniso', 'skew', 'generalized', 'plateau_iso',
340
+ 'plateau_aniso']
341
+ kernel_prob (tuple): corresponding kernel probability for each
342
+ kernel type
343
+ kernel_size (int):
344
+ sigma_x_range (tuple): [0.6, 5]
345
+ sigma_y_range (tuple): [0.6, 5]
346
+ rotation range (tuple): [-math.pi, math.pi]
347
+ beta_range (tuple): [0.5, 8]
348
+ noise_range(tuple, optional): multiplicative kernel noise,
349
+ [0.75, 1.25]. Default: None
350
+
351
+ Returns:
352
+ kernel (ndarray):
353
+ """
354
+ kernel_type = random.choices(kernel_list, kernel_prob)[0]
355
+ if kernel_type == 'iso':
356
+ kernel = random_bivariate_Gaussian(
357
+ kernel_size, sigma_x_range, sigma_y_range, rotation_range, noise_range=noise_range, isotropic=True)
358
+ elif kernel_type == 'aniso':
359
+ kernel = random_bivariate_Gaussian(
360
+ kernel_size, sigma_x_range, sigma_y_range, rotation_range, noise_range=noise_range, isotropic=False)
361
+ elif kernel_type == 'generalized_iso':
362
+ kernel = random_bivariate_generalized_Gaussian(
363
+ kernel_size,
364
+ sigma_x_range,
365
+ sigma_y_range,
366
+ rotation_range,
367
+ betag_range,
368
+ noise_range=noise_range,
369
+ isotropic=True)
370
+ elif kernel_type == 'generalized_aniso':
371
+ kernel = random_bivariate_generalized_Gaussian(
372
+ kernel_size,
373
+ sigma_x_range,
374
+ sigma_y_range,
375
+ rotation_range,
376
+ betag_range,
377
+ noise_range=noise_range,
378
+ isotropic=False)
379
+ elif kernel_type == 'plateau_iso':
380
+ kernel = random_bivariate_plateau(
381
+ kernel_size, sigma_x_range, sigma_y_range, rotation_range, betap_range, noise_range=None, isotropic=True)
382
+ elif kernel_type == 'plateau_aniso':
383
+ kernel = random_bivariate_plateau(
384
+ kernel_size, sigma_x_range, sigma_y_range, rotation_range, betap_range, noise_range=None, isotropic=False)
385
+ return kernel
386
+
387
+
388
+ np.seterr(divide='ignore', invalid='ignore')
389
+
390
+
391
+ def circular_lowpass_kernel(cutoff, kernel_size, pad_to=0):
392
+ """2D sinc filter
393
+
394
+ Reference: https://dsp.stackexchange.com/questions/58301/2-d-circularly-symmetric-low-pass-filter
395
+
396
+ Args:
397
+ cutoff (float): cutoff frequency in radians (pi is max)
398
+ kernel_size (int): horizontal and vertical size, must be odd.
399
+ pad_to (int): pad kernel size to desired size, must be odd or zero.
400
+ """
401
+ assert kernel_size % 2 == 1, 'Kernel size must be an odd number.'
402
+ kernel = np.fromfunction(
403
+ lambda x, y: cutoff * special.j1(cutoff * np.sqrt(
404
+ (x - (kernel_size - 1) / 2)**2 + (y - (kernel_size - 1) / 2)**2)) / (2 * np.pi * np.sqrt(
405
+ (x - (kernel_size - 1) / 2)**2 + (y - (kernel_size - 1) / 2)**2)), [kernel_size, kernel_size])
406
+ kernel[(kernel_size - 1) // 2, (kernel_size - 1) // 2] = cutoff**2 / (4 * np.pi)
407
+ kernel = kernel / np.sum(kernel)
408
+ if pad_to > kernel_size:
409
+ pad_size = (pad_to - kernel_size) // 2
410
+ kernel = np.pad(kernel, ((pad_size, pad_size), (pad_size, pad_size)))
411
+ return kernel
412
+
413
+
414
+ # ------------------------------------------------------------- #
415
+ # --------------------------- noise --------------------------- #
416
+ # ------------------------------------------------------------- #
417
+
418
+ # ----------------------- Gaussian Noise ----------------------- #
419
+
420
+
421
+ def generate_gaussian_noise(img, sigma=10, gray_noise=False):
422
+ """Generate Gaussian noise.
423
+
424
+ Args:
425
+ img (Numpy array): Input image, shape (h, w, c), range [0, 1], float32.
426
+ sigma (float): Noise scale (measured in range 255). Default: 10.
427
+
428
+ Returns:
429
+ (Numpy array): Returned noisy image, shape (h, w, c), range[0, 1],
430
+ float32.
431
+ """
432
+ if gray_noise:
433
+ noise = np.float32(np.random.randn(*(img.shape[0:2]))) * sigma / 255.
434
+ noise = np.expand_dims(noise, axis=2).repeat(3, axis=2)
435
+ else:
436
+ noise = np.float32(np.random.randn(*(img.shape))) * sigma / 255.
437
+ return noise
438
+
439
+
440
+ def add_gaussian_noise(img, sigma=10, clip=True, rounds=False, gray_noise=False):
441
+ """Add Gaussian noise.
442
+
443
+ Args:
444
+ img (Numpy array): Input image, shape (h, w, c), range [0, 1], float32.
445
+ sigma (float): Noise scale (measured in range 255). Default: 10.
446
+
447
+ Returns:
448
+ (Numpy array): Returned noisy image, shape (h, w, c), range[0, 1],
449
+ float32.
450
+ """
451
+ noise = generate_gaussian_noise(img, sigma, gray_noise)
452
+ out = img + noise
453
+ if clip and rounds:
454
+ out = np.clip((out * 255.0).round(), 0, 255) / 255.
455
+ elif clip:
456
+ out = np.clip(out, 0, 1)
457
+ elif rounds:
458
+ out = (out * 255.0).round() / 255.
459
+ return out
460
+
461
+
462
+ def generate_gaussian_noise_pt(img, sigma=10, gray_noise=0):
463
+ """Add Gaussian noise (PyTorch version).
464
+
465
+ Args:
466
+ img (Tensor): Shape (b, c, h, w), range[0, 1], float32.
467
+ scale (float | Tensor): Noise scale. Default: 1.0.
468
+
469
+ Returns:
470
+ (Tensor): Returned noisy image, shape (b, c, h, w), range[0, 1],
471
+ float32.
472
+ """
473
+ b, _, h, w = img.size()
474
+ if not isinstance(sigma, (float, int)):
475
+ sigma = sigma.view(img.size(0), 1, 1, 1)
476
+ if isinstance(gray_noise, (float, int)):
477
+ cal_gray_noise = gray_noise > 0
478
+ else:
479
+ gray_noise = gray_noise.view(b, 1, 1, 1)
480
+ cal_gray_noise = torch.sum(gray_noise) > 0
481
+
482
+ if cal_gray_noise:
483
+ noise_gray = torch.randn(*img.size()[2:4], dtype=img.dtype, device=img.device) * sigma / 255.
484
+ noise_gray = noise_gray.view(b, 1, h, w)
485
+
486
+ # always calculate color noise
487
+ noise = torch.randn(*img.size(), dtype=img.dtype, device=img.device) * sigma / 255.
488
+
489
+ if cal_gray_noise:
490
+ noise = noise * (1 - gray_noise) + noise_gray * gray_noise
491
+ return noise
492
+
493
+
494
+ def add_gaussian_noise_pt(img, sigma=10, gray_noise=0, clip=True, rounds=False):
495
+ """Add Gaussian noise (PyTorch version).
496
+
497
+ Args:
498
+ img (Tensor): Shape (b, c, h, w), range[0, 1], float32.
499
+ scale (float | Tensor): Noise scale. Default: 1.0.
500
+
501
+ Returns:
502
+ (Tensor): Returned noisy image, shape (b, c, h, w), range[0, 1],
503
+ float32.
504
+ """
505
+ noise = generate_gaussian_noise_pt(img, sigma, gray_noise)
506
+ out = img + noise
507
+ if clip and rounds:
508
+ out = torch.clamp((out * 255.0).round(), 0, 255) / 255.
509
+ elif clip:
510
+ out = torch.clamp(out, 0, 1)
511
+ elif rounds:
512
+ out = (out * 255.0).round() / 255.
513
+ return out
514
+
515
+
516
+ # ----------------------- Random Gaussian Noise ----------------------- #
517
+ def random_generate_gaussian_noise(img, sigma_range=(0, 10), gray_prob=0):
518
+ sigma = np.random.uniform(sigma_range[0], sigma_range[1])
519
+ if np.random.uniform() < gray_prob:
520
+ gray_noise = True
521
+ else:
522
+ gray_noise = False
523
+ return generate_gaussian_noise(img, sigma, gray_noise)
524
+
525
+
526
+ def random_add_gaussian_noise(img, sigma_range=(0, 1.0), gray_prob=0, clip=True, rounds=False):
527
+ noise = random_generate_gaussian_noise(img, sigma_range, gray_prob)
528
+ out = img + noise
529
+ if clip and rounds:
530
+ out = np.clip((out * 255.0).round(), 0, 255) / 255.
531
+ elif clip:
532
+ out = np.clip(out, 0, 1)
533
+ elif rounds:
534
+ out = (out * 255.0).round() / 255.
535
+ return out
536
+
537
+
538
+ def random_generate_gaussian_noise_pt(img, sigma_range=(0, 10), gray_prob=0):
539
+ sigma = torch.rand(
540
+ img.size(0), dtype=img.dtype, device=img.device) * (sigma_range[1] - sigma_range[0]) + sigma_range[0]
541
+ gray_noise = torch.rand(img.size(0), dtype=img.dtype, device=img.device)
542
+ gray_noise = (gray_noise < gray_prob).float()
543
+ return generate_gaussian_noise_pt(img, sigma, gray_noise)
544
+
545
+
546
+ def random_add_gaussian_noise_pt(img, sigma_range=(0, 1.0), gray_prob=0, clip=True, rounds=False):
547
+ noise = random_generate_gaussian_noise_pt(img, sigma_range, gray_prob)
548
+ out = img + noise
549
+ if clip and rounds:
550
+ out = torch.clamp((out * 255.0).round(), 0, 255) / 255.
551
+ elif clip:
552
+ out = torch.clamp(out, 0, 1)
553
+ elif rounds:
554
+ out = (out * 255.0).round() / 255.
555
+ return out
556
+
557
+
558
+ # ----------------------- Poisson (Shot) Noise ----------------------- #
559
+
560
+
561
+ def generate_poisson_noise(img, scale=1.0, gray_noise=False):
562
+ """Generate poisson noise.
563
+
564
+ Reference: https://github.com/scikit-image/scikit-image/blob/main/skimage/util/noise.py#L37-L219
565
+
566
+ Args:
567
+ img (Numpy array): Input image, shape (h, w, c), range [0, 1], float32.
568
+ scale (float): Noise scale. Default: 1.0.
569
+ gray_noise (bool): Whether generate gray noise. Default: False.
570
+
571
+ Returns:
572
+ (Numpy array): Returned noisy image, shape (h, w, c), range[0, 1],
573
+ float32.
574
+ """
575
+ if gray_noise:
576
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
577
+ # round and clip image for counting vals correctly
578
+ img = np.clip((img * 255.0).round(), 0, 255) / 255.
579
+ vals = len(np.unique(img))
580
+ vals = 2**np.ceil(np.log2(vals))
581
+ out = np.float32(np.random.poisson(img * vals) / float(vals))
582
+ noise = out - img
583
+ if gray_noise:
584
+ noise = np.repeat(noise[:, :, np.newaxis], 3, axis=2)
585
+ return noise * scale
586
+
587
+
588
+ def add_poisson_noise(img, scale=1.0, clip=True, rounds=False, gray_noise=False):
589
+ """Add poisson noise.
590
+
591
+ Args:
592
+ img (Numpy array): Input image, shape (h, w, c), range [0, 1], float32.
593
+ scale (float): Noise scale. Default: 1.0.
594
+ gray_noise (bool): Whether generate gray noise. Default: False.
595
+
596
+ Returns:
597
+ (Numpy array): Returned noisy image, shape (h, w, c), range[0, 1],
598
+ float32.
599
+ """
600
+ noise = generate_poisson_noise(img, scale, gray_noise)
601
+ out = img + noise
602
+ if clip and rounds:
603
+ out = np.clip((out * 255.0).round(), 0, 255) / 255.
604
+ elif clip:
605
+ out = np.clip(out, 0, 1)
606
+ elif rounds:
607
+ out = (out * 255.0).round() / 255.
608
+ return out
609
+
610
+
611
+ def generate_poisson_noise_pt(img, scale=1.0, gray_noise=0):
612
+ """Generate a batch of poisson noise (PyTorch version)
613
+
614
+ Args:
615
+ img (Tensor): Input image, shape (b, c, h, w), range [0, 1], float32.
616
+ scale (float | Tensor): Noise scale. Number or Tensor with shape (b).
617
+ Default: 1.0.
618
+ gray_noise (float | Tensor): 0-1 number or Tensor with shape (b).
619
+ 0 for False, 1 for True. Default: 0.
620
+
621
+ Returns:
622
+ (Tensor): Returned noisy image, shape (b, c, h, w), range[0, 1],
623
+ float32.
624
+ """
625
+ b, _, h, w = img.size()
626
+ if isinstance(gray_noise, (float, int)):
627
+ cal_gray_noise = gray_noise > 0
628
+ else:
629
+ gray_noise = gray_noise.view(b, 1, 1, 1)
630
+ cal_gray_noise = torch.sum(gray_noise) > 0
631
+ if cal_gray_noise:
632
+ img_gray = rgb_to_grayscale(img, num_output_channels=1)
633
+ # round and clip image for counting vals correctly
634
+ img_gray = torch.clamp((img_gray * 255.0).round(), 0, 255) / 255.
635
+ # use for-loop to get the unique values for each sample
636
+ vals_list = [len(torch.unique(img_gray[i, :, :, :])) for i in range(b)]
637
+ vals_list = [2**np.ceil(np.log2(vals)) for vals in vals_list]
638
+ vals = img_gray.new_tensor(vals_list).view(b, 1, 1, 1)
639
+ out = torch.poisson(img_gray * vals) / vals
640
+ noise_gray = out - img_gray
641
+ noise_gray = noise_gray.expand(b, 3, h, w)
642
+
643
+ # always calculate color noise
644
+ # round and clip image for counting vals correctly
645
+ img = torch.clamp((img * 255.0).round(), 0, 255) / 255.
646
+ # use for-loop to get the unique values for each sample
647
+ vals_list = [len(torch.unique(img[i, :, :, :])) for i in range(b)]
648
+ vals_list = [2**np.ceil(np.log2(vals)) for vals in vals_list]
649
+ vals = img.new_tensor(vals_list).view(b, 1, 1, 1)
650
+ out = torch.poisson(img * vals) / vals
651
+ noise = out - img
652
+ if cal_gray_noise:
653
+ noise = noise * (1 - gray_noise) + noise_gray * gray_noise
654
+ if not isinstance(scale, (float, int)):
655
+ scale = scale.view(b, 1, 1, 1)
656
+ return noise * scale
657
+
658
+
659
+ def add_poisson_noise_pt(img, scale=1.0, clip=True, rounds=False, gray_noise=0):
660
+ """Add poisson noise to a batch of images (PyTorch version).
661
+
662
+ Args:
663
+ img (Tensor): Input image, shape (b, c, h, w), range [0, 1], float32.
664
+ scale (float | Tensor): Noise scale. Number or Tensor with shape (b).
665
+ Default: 1.0.
666
+ gray_noise (float | Tensor): 0-1 number or Tensor with shape (b).
667
+ 0 for False, 1 for True. Default: 0.
668
+
669
+ Returns:
670
+ (Tensor): Returned noisy image, shape (b, c, h, w), range[0, 1],
671
+ float32.
672
+ """
673
+ noise = generate_poisson_noise_pt(img, scale, gray_noise)
674
+ out = img + noise
675
+ if clip and rounds:
676
+ out = torch.clamp((out * 255.0).round(), 0, 255) / 255.
677
+ elif clip:
678
+ out = torch.clamp(out, 0, 1)
679
+ elif rounds:
680
+ out = (out * 255.0).round() / 255.
681
+ return out
682
+
683
+
684
+ # ----------------------- Random Poisson (Shot) Noise ----------------------- #
685
+
686
+
687
+ def random_generate_poisson_noise(img, scale_range=(0, 1.0), gray_prob=0):
688
+ scale = np.random.uniform(scale_range[0], scale_range[1])
689
+ if np.random.uniform() < gray_prob:
690
+ gray_noise = True
691
+ else:
692
+ gray_noise = False
693
+ return generate_poisson_noise(img, scale, gray_noise)
694
+
695
+
696
+ def random_add_poisson_noise(img, scale_range=(0, 1.0), gray_prob=0, clip=True, rounds=False):
697
+ noise = random_generate_poisson_noise(img, scale_range, gray_prob)
698
+ out = img + noise
699
+ if clip and rounds:
700
+ out = np.clip((out * 255.0).round(), 0, 255) / 255.
701
+ elif clip:
702
+ out = np.clip(out, 0, 1)
703
+ elif rounds:
704
+ out = (out * 255.0).round() / 255.
705
+ return out
706
+
707
+
708
+ def random_generate_poisson_noise_pt(img, scale_range=(0, 1.0), gray_prob=0):
709
+ scale = torch.rand(
710
+ img.size(0), dtype=img.dtype, device=img.device) * (scale_range[1] - scale_range[0]) + scale_range[0]
711
+ gray_noise = torch.rand(img.size(0), dtype=img.dtype, device=img.device)
712
+ gray_noise = (gray_noise < gray_prob).float()
713
+ return generate_poisson_noise_pt(img, scale, gray_noise)
714
+
715
+
716
+ def random_add_poisson_noise_pt(img, scale_range=(0, 1.0), gray_prob=0, clip=True, rounds=False):
717
+ noise = random_generate_poisson_noise_pt(img, scale_range, gray_prob)
718
+ out = img + noise
719
+ if clip and rounds:
720
+ out = torch.clamp((out * 255.0).round(), 0, 255) / 255.
721
+ elif clip:
722
+ out = torch.clamp(out, 0, 1)
723
+ elif rounds:
724
+ out = (out * 255.0).round() / 255.
725
+ return out
726
+
727
+
728
+ # ------------------------------------------------------------------------ #
729
+ # --------------------------- JPEG compression --------------------------- #
730
+ # ------------------------------------------------------------------------ #
731
+
732
+
733
+ def add_jpg_compression(img, quality=90):
734
+ """Add JPG compression artifacts.
735
+
736
+ Args:
737
+ img (Numpy array): Input image, shape (h, w, c), range [0, 1], float32.
738
+ quality (float): JPG compression quality. 0 for lowest quality, 100 for
739
+ best quality. Default: 90.
740
+
741
+ Returns:
742
+ (Numpy array): Returned image after JPG, shape (h, w, c), range[0, 1],
743
+ float32.
744
+ """
745
+ img = np.clip(img, 0, 1)
746
+ encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality]
747
+ _, encimg = cv2.imencode('.jpg', img * 255., encode_param)
748
+ img = np.float32(cv2.imdecode(encimg, 1)) / 255.
749
+ return img
750
+
751
+
752
+ def random_add_jpg_compression(img, quality_range=(90, 100)):
753
+ """Randomly add JPG compression artifacts.
754
+
755
+ Args:
756
+ img (Numpy array): Input image, shape (h, w, c), range [0, 1], float32.
757
+ quality_range (tuple[float] | list[float]): JPG compression quality
758
+ range. 0 for lowest quality, 100 for best quality.
759
+ Default: (90, 100).
760
+
761
+ Returns:
762
+ (Numpy array): Returned image after JPG, shape (h, w, c), range[0, 1],
763
+ float32.
764
+ """
765
+ quality = np.random.uniform(quality_range[0], quality_range[1])
766
+ return add_jpg_compression(img, int(quality))
dataset/file_backend.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) OpenMMLab. All rights reserved.
2
+ # https://github.com/open-mmlab/mmcv/blob/master/mmcv/fileio/file_client.py
3
+ import re
4
+ from abc import ABCMeta, abstractmethod
5
+ from pathlib import Path
6
+ from typing import Optional, Union
7
+
8
+
9
+ class BaseStorageBackend(metaclass=ABCMeta):
10
+ """Abstract class of storage backends.
11
+
12
+ All backends need to implement two apis: ``get()`` and ``get_text()``.
13
+ ``get()`` reads the file as a byte stream and ``get_text()`` reads the file
14
+ as texts.
15
+ """
16
+
17
+ @property
18
+ def name(self) -> str:
19
+ return self.__class__.__name__
20
+
21
+ @abstractmethod
22
+ def get(self, filepath: str) -> bytes:
23
+ pass
24
+
25
+
26
+ class PetrelBackend(BaseStorageBackend):
27
+ """Petrel storage backend (for internal use).
28
+
29
+ PetrelBackend supports reading and writing data to multiple clusters.
30
+ If the file path contains the cluster name, PetrelBackend will read data
31
+ from specified cluster or write data to it. Otherwise, PetrelBackend will
32
+ access the default cluster.
33
+
34
+ Args:
35
+ path_mapping (dict, optional): Path mapping dict from local path to
36
+ Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in
37
+ ``filepath`` will be replaced by ``dst``. Default: None.
38
+ enable_mc (bool, optional): Whether to enable memcached support.
39
+ Default: True.
40
+ conf_path (str, optional): Config path of Petrel client. Default: None.
41
+ `New in version 1.7.1`.
42
+
43
+ Examples:
44
+ >>> filepath1 = 's3://path/of/file'
45
+ >>> filepath2 = 'cluster-name:s3://path/of/file'
46
+ >>> client = PetrelBackend()
47
+ >>> client.get(filepath1) # get data from default cluster
48
+ >>> client.get(filepath2) # get data from 'cluster-name' cluster
49
+ """
50
+
51
+ def __init__(self,
52
+ path_mapping: Optional[dict] = None,
53
+ enable_mc: bool = False,
54
+ conf_path: str = None):
55
+ try:
56
+ from petrel_client import client
57
+ except ImportError:
58
+ raise ImportError('Please install petrel_client to enable '
59
+ 'PetrelBackend.')
60
+
61
+ self._client = client.Client(conf_path=conf_path, enable_mc=enable_mc)
62
+ assert isinstance(path_mapping, dict) or path_mapping is None
63
+ self.path_mapping = path_mapping
64
+
65
+ def _map_path(self, filepath: Union[str, Path]) -> str:
66
+ """Map ``filepath`` to a string path whose prefix will be replaced by
67
+ :attr:`self.path_mapping`.
68
+
69
+ Args:
70
+ filepath (str): Path to be mapped.
71
+ """
72
+ filepath = str(filepath)
73
+ if self.path_mapping is not None:
74
+ for k, v in self.path_mapping.items():
75
+ filepath = filepath.replace(k, v, 1)
76
+ return filepath
77
+
78
+ def _format_path(self, filepath: str) -> str:
79
+ """Convert a ``filepath`` to standard format of petrel oss.
80
+
81
+ If the ``filepath`` is concatenated by ``os.path.join``, in a Windows
82
+ environment, the ``filepath`` will be the format of
83
+ 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the
84
+ above ``filepath`` will be converted to 's3://bucket_name/image.jpg'.
85
+
86
+ Args:
87
+ filepath (str): Path to be formatted.
88
+ """
89
+ return re.sub(r'\\+', '/', filepath)
90
+
91
+ def get(self, filepath: Union[str, Path]) -> bytes:
92
+ """Read data from a given ``filepath`` with 'rb' mode.
93
+
94
+ Args:
95
+ filepath (str or Path): Path to read data.
96
+
97
+ Returns:
98
+ bytes: The loaded bytes.
99
+ """
100
+ filepath = self._map_path(filepath)
101
+ filepath = self._format_path(filepath)
102
+ value = self._client.Get(filepath)
103
+ return value
104
+
105
+
106
+ class HardDiskBackend(BaseStorageBackend):
107
+ """Raw hard disks storage backend."""
108
+
109
+ def get(self, filepath: Union[str, Path]) -> bytes:
110
+ """Read data from a given ``filepath`` with 'rb' mode.
111
+
112
+ Args:
113
+ filepath (str or Path): Path to read data.
114
+
115
+ Returns:
116
+ bytes: Expected bytes object.
117
+ """
118
+ with open(filepath, 'rb') as f:
119
+ value_buf = f.read()
120
+ return value_buf
dataset/utils.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict
2
+ import random
3
+ import math
4
+
5
+ import numpy as np
6
+ from PIL import Image
7
+ import cv2
8
+
9
+
10
+ def load_file_list(file_list_path: str) -> List[Dict[str, str]]:
11
+ files = []
12
+ with open(file_list_path, "r") as fin:
13
+ for line in fin:
14
+ path = line.strip()
15
+ if path:
16
+ files.append({"image_path": path, "prompt": ""})
17
+ return files
18
+
19
+
20
+ # https://github.com/openai/guided-diffusion/blob/main/guided_diffusion/image_datasets.py
21
+ def center_crop_arr(pil_image, image_size):
22
+ # We are not on a new enough PIL to support the `reducing_gap`
23
+ # argument, which uses BOX downsampling at powers of two first.
24
+ # Thus, we do it by hand to improve downsample quality.
25
+ while min(*pil_image.size) >= 2 * image_size:
26
+ pil_image = pil_image.resize(
27
+ tuple(x // 2 for x in pil_image.size), resample=Image.BOX
28
+ )
29
+
30
+ scale = image_size / min(*pil_image.size)
31
+ pil_image = pil_image.resize(
32
+ tuple(round(x * scale) for x in pil_image.size), resample=Image.BICUBIC
33
+ )
34
+
35
+ arr = np.array(pil_image)
36
+ crop_y = (arr.shape[0] - image_size) // 2
37
+ crop_x = (arr.shape[1] - image_size) // 2
38
+ return arr[crop_y : crop_y + image_size, crop_x : crop_x + image_size]
39
+
40
+
41
+ # https://github.com/openai/guided-diffusion/blob/main/guided_diffusion/image_datasets.py
42
+ def random_crop_arr(pil_image, image_size, min_crop_frac=0.8, max_crop_frac=1.0):
43
+ min_smaller_dim_size = math.ceil(image_size / max_crop_frac)
44
+ max_smaller_dim_size = math.ceil(image_size / min_crop_frac)
45
+ smaller_dim_size = random.randrange(min_smaller_dim_size, max_smaller_dim_size + 1)
46
+
47
+ # We are not on a new enough PIL to support the `reducing_gap`
48
+ # argument, which uses BOX downsampling at powers of two first.
49
+ # Thus, we do it by hand to improve downsample quality.
50
+ while min(*pil_image.size) >= 2 * smaller_dim_size:
51
+ pil_image = pil_image.resize(
52
+ tuple(x // 2 for x in pil_image.size), resample=Image.BOX
53
+ )
54
+
55
+ scale = smaller_dim_size / min(*pil_image.size)
56
+ pil_image = pil_image.resize(
57
+ tuple(round(x * scale) for x in pil_image.size), resample=Image.BICUBIC
58
+ )
59
+
60
+ arr = np.array(pil_image)
61
+ crop_y = random.randrange(arr.shape[0] - image_size + 1)
62
+ crop_x = random.randrange(arr.shape[1] - image_size + 1)
63
+ return arr[crop_y : crop_y + image_size, crop_x : crop_x + image_size]