import random
import cv2
import numba as nb
import numpy as np
from numba import config
from numba import jit
from sklearn.datasets import make_blobs
[docs]
class NoiseGenerator:
"""Core object to generate mask of noise.
:param noise_type: Types of generated noise.
1 = default, sklearn.datasets' make_blobs noise
2 = gaussian noise
3 = perlin noise
4 = worley noise
5 = rectangular pattern noise
:type noise_type: int, optional
:param noise_side: Location of generated noise. Choose from:
"none", "all", "left", "right", "top", "bottom","top_left", "top_right", "bottom_left", "bottom_right".
:type noise_side: string, optional
:param numba_jit: The flag to enable numba jit to speed up the processing in the augmentation.
:type numba_jit: int, optional
"""
def __init__(self, noise_type=1, noise_side=None, numba_jit=1):
self.noise_type = noise_type
self.noise_side = noise_side
self.numba_jit = numba_jit
self.sides = [
"none",
"all",
"left",
"right",
"top",
"bottom",
"top_left",
"top_right",
"bottom_left",
"bottom_right",
]
config.DISABLE_JIT = bool(1 - numba_jit)
[docs]
def compute_perlin(self, x, y, permutation_table):
"""Calculates Perlin noise values for each point on the 2D array using linear interpolation and gradient selection.
:param x: Grid x coordinates of the Perlin noise values.
:type x: numpy array
:param y: Grid y coordinates of the Perlin noise values.
:type y: numpy array
:param permutation_table: Permutation table to shuffe and select the gradient vectors during the Perlin noise generation process.
:type permutation_table: numpy array
"""
xi, yi = x.astype(int), y.astype(int)
xg, yg = x - xi, y - yi
xf, yf = self.compute_fade(xg), self.compute_fade(yg)
p00 = permutation_table[permutation_table[xi] + yi]
p01 = permutation_table[permutation_table[xi] + yi + 1]
p10 = permutation_table[permutation_table[xi + 1] + yi]
p11 = permutation_table[permutation_table[xi + 1] + yi + 1]
n00 = self.compute_gradient(p00, xg, yg)
n01 = self.compute_gradient(p01, xg, yg - 1)
n10 = self.compute_gradient(p10, xg - 1, yg)
n11 = self.compute_gradient(p11, xg - 1, yg - 1)
x1 = self.compute_lerp(n00, n10, xf)
x2 = self.compute_lerp(n01, n11, xf)
return self.compute_lerp(x1, x2, yf)
[docs]
@staticmethod
@jit(nopython=True, cache=True)
def compute_lerp(a, b, x):
"""Performs linear interpolation between two values based on a interpolation parameter.
:param a: Starting value of the linear interpolation process.
:type a: numpy array
:param b: End value of the linear interpolation process.
:type b: numpy array
:param x: The weight of the interpolation process.
:type x: numpy array
"""
return a + x * (b - a)
[docs]
@staticmethod
@jit(nopython=True, cache=True)
def compute_fade(f):
"""Computes fade values for interpolation purpose.
:param f: Input fractional value for interpolation purpose.
:type f: numpy array
"""
return 6 * f**5 - 15 * f**4 + 10 * f**3
[docs]
@staticmethod
@jit(nopython=True, cache=True, parallel=True)
def compute_gradient(c, x, y):
"""Computes the gradient vector calculates the dot product.
:param c: Input array that determines the selection of the gradient vector.
:type c: numpy array
:param x: The x coordinates of the points on the 2D array.
:type x: numpy array
:param y: The y coordinates of the points on the 2D array.
:type y: numpy array
"""
vectors = np.array([[0, 1], [0, -1], [1, 0], [-1, 0]])
rows, cols = c.shape
result = np.empty_like(x)
for i in nb.prange(rows):
for j in nb.prange(cols):
c_remainder = c[i, j] % 4
gradient_co = vectors[c_remainder]
result[i, j] = gradient_co[0] * x[i, j] + gradient_co[1] * y[i, j]
return result
[docs]
def generate_perlin_noise(self, width, height, points=(5, 20)):
"""Generates Perlin noise for a 2D array of specified width and height.
:param width: Width of the generated 2d perlin noise array.
:type width: int
:param height: Height of the generated 2d perlin noise array.
:type height: int
:param points: Tuple of ints determining number of points in each x and y direction within the array.
:type points: tuple
"""
lin_array_x = np.linspace(0, np.random.randint(points[0], points[1]), width, endpoint=False)
lin_array_y = np.linspace(0, np.random.randint(points[0], points[1]), height, endpoint=False)
x, y = np.meshgrid(lin_array_x, lin_array_y)
permutation_table = np.arange(256, dtype=int)
np.random.shuffle(permutation_table)
permutation_table = np.stack([permutation_table, permutation_table]).flatten()
image_perlin = self.compute_perlin(x, y, permutation_table)
return image_perlin
[docs]
@staticmethod
@jit(nopython=True, cache=True, parallel=True)
def generate_worley_noise(width, height, npoints, option, noise_background):
"""Generates Worley noise, also known as cellular noise or Voronoi noise.
Worley noise creates a pattern of irregularly shaped cells,
where each cell is centered around a randomly placed point.
:param width: Width of the generated 2d worley noise array.
:type width: int
:param height: Height of the generated 2d worley noise array.
:type height: int
:param npoints: Number of randomly placed points that will define the Voronoi cells.
:param npoints: int
:param option: An option to choose which distance value to calculate within each cell.
0 is the closest point, 1 is the second closest point and so on.
:param option: int
:param noise_background: Background value of mask.
:type noise_background: int
"""
points = [(random.randint(0, width), random.randint(0, height)) for _ in range(npoints)]
image_worley = np.full((height, width), fill_value=noise_background, dtype=np.float64)
for y in nb.prange(height):
for x in nb.prange(width):
distances = [np.sqrt((p[0] - x) ** 2 + (p[1] - y) ** 2) for p in points]
image_worley[y, x] = sorted(distances)[option]
return image_worley
[docs]
def generate_points(self, n_samples_array, std, center_x, center_y, xsize, ysize):
"""Generate x&y coordinates of noise.
:param n_samples_array: List contains number of points sample for each cluster.
:type n_samples_array: list
:param std: Standard deviation to determine sparseness of generated points.
:type std: int
:param center_x: Center x of the generated noises.
:type center_x: int
:param center_y: Center y of the generated noises.
:type center_y: int
:param xsize: Width of image.
:type xsize: int
:param ysize: Height of image.
:type ysize: int
"""
# generate clusters of blobs
generated_points_x, _ = make_blobs(
n_samples=n_samples_array,
center_box=center_x,
cluster_std=std,
n_features=1,
)
generated_points_y, _ = make_blobs(
n_samples=n_samples_array,
center_box=center_y,
cluster_std=std,
n_features=1,
)
# remove decimals
generated_points_x = generated_points_x.astype("int")
generated_points_y = generated_points_y.astype("int")
ind_delete = np.logical_or.reduce(
(
generated_points_x < 0,
generated_points_y < 0,
generated_points_x > xsize - 1,
generated_points_y > ysize - 1,
),
)
generated_points_y = np.delete(generated_points_y, ind_delete.reshape(ind_delete.shape[0]), axis=0)
generated_points_x = np.delete(generated_points_x, ind_delete.reshape(ind_delete.shape[0]), axis=0)
return generated_points_x, generated_points_y
[docs]
def generate_mask(
self,
noise_background,
noise_value,
generated_points_x,
generated_points_y,
xsize,
ysize,
):
"""Generate mask of noise.
:param noise_background: Tuple of ints to determine background value of mask.
:type noise_background: tuple
:param noise_value: Tuple of ints to determine value of noise.
:type noise_value: tuple
:param generated_points_x: x point value of noise.
:type generated_points_x: numpy array
:param generated_points_y: y point value of noise.
:type generated_points_y: numpy array
:param xsize: Width of image.
:type xsize: int
:param ysize: Height of image.
:type ysize: int
"""
# background of noise mask
img_mask = np.random.randint(
noise_background[0],
noise_background[1] + 1,
(ysize, xsize),
)
# mask of random value
img_mask_random = np.random.randint(
low=noise_value[0],
high=noise_value[1] + 1,
size=(ysize, xsize),
)
# insert random value into background
img_mask[generated_points_y, generated_points_x] = img_mask_random[generated_points_y, generated_points_x]
return img_mask.astype("uint8")
[docs]
def generate_mask_main(
self,
noise_type,
noise_side,
noise_value,
noise_background,
noise_sparsity,
noise_concentration,
xsize,
ysize,
):
"""Main function to generate mask of noise in each iteration.
:param noise_type: Types of generated noise.
:type noise_type: int
:param noise_side: Location of generated noise.
:type noise_side: string
:param noise_value: Tuple of ints to determine value of noise.
:type noise_value: tuple
:param noise_background: Tuple of ints to determine background value of mask.
:type noise_background: tuple
:param noise_sparsity: Pair of floats determining sparseness of noise.
:type noise_sparsity: tuple, optional
:param noise_concentration: Pair of floats determining concentration of noise.
:type noise_concentration: tuple, optional
:param xsize: Width of image.
:type xsize: int
:param ysize: Height of image.
:type ysize: int
"""
# generate sparsity
sparsity = random.uniform(noise_sparsity[0], noise_sparsity[1])
# sklearn.datasets' make_blobs noise
if noise_type == 1:
# get max of y or x size
max_size = max(xsize, ysize)
n_clusters = (
int((noise_concentration[0]) * max_size),
int((noise_concentration[1]) * max_size),
)
n_samples = (
int((noise_concentration[0]) * max_size),
int((noise_concentration[1]) * max_size),
)
# prevent 0 cluster or 0 sample
n_clusters = (max(1, n_clusters[0]), max(1, n_clusters[1]))
n_samples = (max(10, n_samples[0]), max(10, n_samples[1]))
# generate array of samples
n_samples_array = [
random.randint(n_samples[0], n_samples[1]) for _ in range(random.randint(n_clusters[0], n_clusters[1]))
]
# generate std for clusters
std_range = (
int((noise_sparsity[0]) * (max_size)),
int((noise_sparsity[1]) * (max_size)),
)
std = max(50, int(random.randint(std_range[0], std_range[1]) / 5))
center_x = (-std, xsize + std)
center_y = (-std, ysize + std)
# generate coordinates for clusters of blobs
generated_points_x, generated_points_y = self.generate_points(
n_samples_array,
std,
center_x,
center_y,
xsize,
ysize,
)
# generate mask
img_mask = self.generate_mask(
noise_background,
noise_value,
generated_points_x,
generated_points_y,
xsize,
ysize,
)
# gaussian noise
if noise_type == 2:
img_mask = np.full((ysize, xsize), fill_value=255, dtype="float")
iterations = random.randint(2, 3)
for _ in range(iterations):
# random parameters for gaussian noise
mean = random.randint(0, 255)
sigma = random.randint(0, 255)
ratio = random.randint(5, 10)
# generate random gaussian noise
img_gaussian = np.random.normal(mean, sigma, (int(xsize / ratio), int(ysize / ratio)))
img_gaussian = cv2.resize(img_gaussian, (xsize, ysize), interpolation=cv2.INTER_LINEAR)
# add gaussian noise
img_mask += img_gaussian
# change image to uint8 after the summation
img_mask = img_mask.astype("uint8")
# threshold to increase or decrease the noise concentration
noise_threshold = int(random.uniform(noise_concentration[0], noise_concentration[1]) * 255)
# mask of background
img_background = np.random.randint(
noise_background[0],
noise_background[1] + 1,
size=(ysize, xsize),
dtype="uint8",
)
indices_background = img_mask >= noise_threshold
# temporary assignment
min_value = np.min(img_mask)
img_mask[indices_background] = min_value
# update noise value
current_min = np.min(img_mask)
current_max = np.max(img_mask)
img_mask = (
((img_mask.astype("float") - current_min) / (current_max - current_min))
* (noise_value[1] - noise_value[0])
+ noise_value[0]
).astype("uint8")
# update background value
img_mask[indices_background] = img_background[indices_background]
# perlin noise
elif noise_type == 3:
points0 = int((noise_concentration[0] * 10) + (noise_sparsity[0] * 10))
points1 = int((noise_concentration[1] * 10) + (noise_sparsity[1] * 10) + 1)
# scale down size to increase processing speed
img_mask = self.generate_perlin_noise(int(xsize / 2), int(ysize / 2), (points0, points1))
# scale up to original size
img_mask = cv2.resize(img_mask, (xsize, ysize), interpolation=cv2.INTER_LINEAR)
# update noise value to given range
current_min = np.min(img_mask)
current_max = np.max(img_mask)
img_mask = (
((img_mask - current_min) / (current_max - current_min)) * (noise_value[1] - noise_value[0])
+ noise_value[0]
).astype("uint8")
# worley noise
elif noise_type == 4:
npoints = random.uniform(noise_concentration[0], noise_concentration[1]) * 250
npoints += random.uniform(noise_sparsity[0], noise_sparsity[1]) * 250
# scale down size to increase processing speed
img_mask = self.generate_worley_noise(
int(xsize / 2),
int(ysize / 2),
npoints=npoints,
option=0,
noise_background=random.randint(noise_background[0], noise_background[1]),
)
# scale up to original size
img_mask = cv2.resize(img_mask, (xsize, ysize), interpolation=cv2.INTER_LINEAR)
# update noise value to given range
current_min = np.min(img_mask)
current_max = np.max(img_mask)
img_mask = (
((img_mask - current_min) / (current_max - current_min)) * (noise_value[1] - noise_value[0])
+ noise_value[0]
).astype("uint8")
# noise with consistent rectangular pattern
elif noise_type == 5:
# create even spread of noise across whole page
if noise_side == "none":
end_y = ysize
# noise spread depends on sparsity
else:
end_y = int(ysize * sparsity)
end_x = xsize
# get max of y or x size
max_size = max(xsize, end_y)
std_range = (
int((noise_sparsity[0]) * (max_size / 5)),
int((noise_sparsity[1]) * (max_size / 5)),
)
std = int(random.randint(std_range[0], std_range[1]) / 25)
center_x = (-std, xsize + std)
center_y = (-std, end_y + std)
# generate number of clusters and number of samples in each cluster
n_clusters = (
int((noise_concentration[0]) * max_size),
int((noise_concentration[1]) * max_size),
)
n_samples = (
int((noise_concentration[0]) * max_size),
int((noise_concentration[1]) * max_size),
)
# prevent 0 cluster or 0 sample
n_clusters = (max(1, n_clusters[0]), max(1, n_clusters[1]))
n_samples = (max(10, n_samples[0]), max(10, n_samples[1]))
# generate array of samples
n_samples_array = [
random.randint(n_samples[0], n_samples[1]) for _ in range(random.randint(n_clusters[0], n_clusters[1]))
]
n_step_x = int(xsize / random.randint(3, 6))
n_step_y = int(end_y / random.randint(8, 16))
# initialize points array
generated_points_x = np.array([[-1]], dtype="int")
generated_points_y = np.array([[-1]], dtype="int")
# initial noise location
ccenter_y = (0, 0)
ccenter_x = (0, 0)
# reduction ratio to determine the density of noise
samples_index = max(1, int(len(n_samples_array) * 0.05))
while ccenter_y[0] < end_y:
# create even spread of noise
if noise_side == "none":
new_samples_index = max(1, int(samples_index * random.uniform(0.5, 1.0)))
n_samples_array = n_samples_array[:new_samples_index]
# reduce samples to create noise gradient
else:
n_samples_array = n_samples_array[:samples_index]
samples_index = max(1, int(samples_index * 0.8))
# varying y
ccenter_y = (ccenter_y[1], ccenter_y[1] + n_step_y)
ccenter_x = (0, 0)
while ccenter_x[0] < end_x:
# varying x
ccenter_x = (ccenter_x[0], ccenter_x[1] + n_step_x)
# generate coordinates for clusters of blobs
cgenerated_points_x, cgenerated_points_y = self.generate_points(
n_samples_array,
std,
ccenter_x,
ccenter_y,
xsize,
ysize,
)
# combine coordinates
generated_points_x = np.concatenate(
[generated_points_x, cgenerated_points_x],
)
generated_points_y = np.concatenate(
[generated_points_y, cgenerated_points_y],
)
# space between next noise patch
add_space = random.randint(5, 10)
ccenter_x = (
ccenter_x[0] + n_step_x + add_space,
ccenter_x[1] + n_step_x + add_space,
)
# space between next noise patch
add_space = random.randint(5, 15)
ccenter_y = (ccenter_y[1] + add_space, ccenter_y[1] + add_space)
# generate mask
img_mask = self.generate_mask(
noise_background,
noise_value,
generated_points_x,
generated_points_y,
xsize,
ysize,
)
if noise_side == "left":
img_mask = np.rot90(img_mask, 1)
elif noise_side == "bottom" or noise_side == "bottom_left" or noise_side == "bottom_right":
img_mask = np.rot90(img_mask, 2)
elif noise_side == "right":
img_mask = np.rot90(img_mask, 3)
elif noise_side == "all":
img_mask = np.minimum(
img_mask,
cv2.resize(np.rot90(img_mask, 1), (xsize, ysize), interpolation=cv2.INTER_LINEAR),
)
img_mask = np.minimum(
img_mask,
cv2.resize(np.rot90(img_mask, 2), (xsize, ysize), interpolation=cv2.INTER_LINEAR),
)
img_mask = np.minimum(
img_mask,
cv2.resize(np.rot90(img_mask, 3), (xsize, ysize), interpolation=cv2.INTER_LINEAR),
)
img_mask = cv2.resize(img_mask, (xsize, ysize), interpolation=cv2.INTER_LINEAR)
# reduce noise area based on sparsity value
if noise_side != "none":
img_sparsity_value = np.arange(-0.3, 1, 1 / int(ysize * sparsity), dtype="float")
length = len(img_sparsity_value)
img_sparsity_value = img_sparsity_value.reshape(length, 1).repeat(xsize, 1)
img_sparsity_value[img_sparsity_value < 0] = 0
img_sparsity_value = cv2.resize(
img_sparsity_value,
(xsize, int(ysize * sparsity)),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_value *= 255
img_sparsity_mask = np.full((ysize, xsize), fill_value=255, dtype="float")
# map noise image back to mask based on noise side
if noise_side == "all":
# get each side
img_sparsity_value_left = cv2.resize(
np.rot90(img_sparsity_value, 1),
(int(xsize * sparsity), ysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_value_right = cv2.resize(
np.rot90(img_sparsity_value, 3),
(int(xsize * sparsity), ysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_value_bottom = np.flipud(img_sparsity_value)
# get all 4 borders of top, left, right, bottom
img_sparsity_mask[: int(ysize * sparsity), :] = np.minimum(
img_sparsity_mask[: int(ysize * sparsity), :],
img_sparsity_value,
)
img_sparsity_mask[:, : int(xsize * sparsity)] = np.minimum(
img_sparsity_mask[:, : int(xsize * sparsity)],
img_sparsity_value_left,
)
img_sparsity_mask[:, -int(xsize * sparsity) :] = np.minimum(
img_sparsity_mask[:, -int(xsize * sparsity) :],
img_sparsity_value_right,
)
img_sparsity_mask[-int(ysize * sparsity) :, :] = np.minimum(
img_sparsity_mask[-int(ysize * sparsity) :, :],
img_sparsity_value_bottom,
)
elif noise_side == "top":
img_sparsity_mask[: int(ysize * sparsity), :] = img_sparsity_value
elif noise_side == "left":
img_sparsity_value = cv2.resize(
np.rot90(img_sparsity_value, 1),
(int(xsize * sparsity), ysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_mask[:, : int(xsize * sparsity)] = img_sparsity_value
elif noise_side == "right":
img_sparsity_value = cv2.resize(
np.rot90(img_sparsity_value, 3),
(int(xsize * sparsity), ysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_mask[:, -int(xsize * sparsity) :] = img_sparsity_value
elif noise_side == "bottom":
img_sparsity_value = np.flipud(img_sparsity_value)
img_sparsity_mask[-int(ysize * sparsity) :, :] = img_sparsity_value
elif noise_side == "top_left":
cysize, cxsize = img_sparsity_value.shape[:2]
img_sparsity_value_rot = cv2.resize(
np.rot90(img_sparsity_value, 3),
(cxsize, cysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_value = img_sparsity_value * img_sparsity_value_rot
img_sparsity_value = (img_sparsity_value - np.min(img_sparsity_value)) / (
np.max(img_sparsity_value) - np.min(img_sparsity_value)
)
img_sparsity_value = 255 - (img_sparsity_value * 255)
img_sparsity_mask[: int(ysize * sparsity), :] = np.flipud(img_sparsity_value)
elif noise_side == "top_right":
cysize, cxsize = img_sparsity_value.shape[:2]
img_sparsity_value_rot = cv2.resize(
np.rot90(img_sparsity_value, 1),
(cxsize, cysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_value = img_sparsity_value * img_sparsity_value_rot
img_sparsity_value = (img_sparsity_value - np.min(img_sparsity_value)) / (
np.max(img_sparsity_value) - np.min(img_sparsity_value)
)
img_sparsity_value = 255 - (img_sparsity_value * 255)
img_sparsity_mask[: int(ysize * sparsity), :] = np.flipud(img_sparsity_value)
elif noise_side == "bottom_left":
cysize, cxsize = img_sparsity_value.shape[:2]
img_sparsity_value_rot = cv2.resize(
np.rot90(img_sparsity_value, 3),
(cxsize, cysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_value = img_sparsity_value * img_sparsity_value_rot
img_sparsity_value = (img_sparsity_value - np.min(img_sparsity_value)) / (
np.max(img_sparsity_value) - np.min(img_sparsity_value)
)
img_sparsity_value = 255 - (img_sparsity_value * 255)
img_sparsity_mask[-int(ysize * sparsity) :, :] = img_sparsity_value
elif noise_side == "bottom_right":
cysize, cxsize = img_sparsity_value.shape[:2]
img_sparsity_value_rot = cv2.resize(
np.rot90(img_sparsity_value, 1),
(cxsize, cysize),
interpolation=cv2.INTER_LINEAR,
)
img_sparsity_value = img_sparsity_value * img_sparsity_value_rot
img_sparsity_value = (img_sparsity_value - np.min(img_sparsity_value)) / (
np.max(img_sparsity_value) - np.min(img_sparsity_value)
)
img_sparsity_value = 255 - (img_sparsity_value * 255)
img_sparsity_mask[-int(ysize * sparsity) :, :] = img_sparsity_value
# multiply noise with sparsity mask
img_mask = 255 - cv2.multiply(255 - img_mask, 255 - img_sparsity_mask.astype("uint8"), scale=1 / 255)
return img_mask
[docs]
def generate_noise(
self,
noise_iteration=(1, 1),
noise_size=(1, 1),
noise_value=(0, 128),
noise_background=(255, 255),
noise_sparsity=(0.4, 0.6),
noise_concentration=(0.4, 0.6),
xsize=1500,
ysize=1500,
):
"""Main function to generate noise.
:param noise_iteration: Pair of ints to determine number of iterations to apply noise in the mask.
:type noise_type: tuple, optional
:param noise_size: Pair of ints to determine scale of noise in the mask.
:type noise_size: tuple, optional
:param noise_value: Pair of ints determining value of noise.
:type noise_value: tuple, optional
:param noise_background: Pair of ints determining value of noise background.
:type noise_background: tuple, optional
:param noise_sparsity: Pair of floats determining sparseness of noise.
:type noise_sparsity: tuple, optional
:param noise_concentration: Pair of floats determining concentration of noise.
:type noise_concentration: tuple, optional
:param xsize: Number of columns in generated mask of noise.
:type xsize: int, optional
:param ysize: Number of rows in generated mask of noise.
:type ysize: int, optional
"""
# generate random iterations
iterations = random.randint(noise_iteration[0], noise_iteration[1])
# generate background value
background_value = random.randint(noise_background[0], noise_background[1])
# initialize blank noise mask
img_mask = np.full((ysize, xsize), fill_value=background_value, dtype="int")
# any invalid noise type will reset noise type to 0
if self.noise_type not in [1, 2, 3, 4, 5]:
noise_type = random.randint(1, 5)
else:
noise_type = self.noise_type
# random location with no sides if no side is chosen
if self.noise_side not in self.sides:
noise_side = random.choice(self.sides)
else:
noise_side = self.noise_side
# loop each iterations
for _ in range(iterations):
# divider to rescale noise mask to larger size
y_divider = random.randint(noise_size[0], noise_size[1])
x_divider = random.randint(noise_size[0], noise_size[1])
# generate noise mask for current iteration
img_mask_temporary = self.generate_mask_main(
noise_type,
noise_side,
noise_value,
noise_background,
noise_sparsity,
noise_concentration,
int(xsize / x_divider),
int(ysize / y_divider),
)
img_mask_temporary = cv2.resize(
img_mask_temporary.astype("uint8"),
(xsize, ysize),
interpolation=cv2.INTER_CUBIC,
)
# merge noise mask in each iteration by getting their min value
img_mask = np.minimum(img_mask_temporary, img_mask)
# output needs uint8 type
img_mask = img_mask.astype("uint8")
return img_mask