|
- # Copyright 2019 Huawei Technologies Co., Ltd
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """
- Image transform
- """
- import os
- import math
- import numpy as np
- import cv2
- from perlin_numpy import generate_fractal_noise_2d
-
- from mindarmour.utils._check_param import check_param_multi_types, check_param_in_range, check_numpy_param, \
- check_int_positive, check_param_type, check_value_non_negative
- from mindarmour.utils.logger import LogUtil
-
- LOGGER = LogUtil.get_instance()
- TAG = 'Image Transformation'
-
-
- class Contrast:
- """
- Contrast of an image.
-
- Args:
- alpha (Union[float, int]): Control the contrast of an image. Suggested value range in [0.2, 2].
- beta (Union[float, int]): Delta added to alpha. Default: 0.
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> alpha = 0.1
- >>> beta = 1
- >>> trans = Contrast(alpha, beta)
- >>> dst = trans(img)
- """
-
- def __init__(self, alpha=1, beta=0):
- super(Contrast, self).__init__()
- self.alpha = check_param_multi_types('factor', alpha, [int, float])
- self.beta = check_param_multi_types('factor', beta, [int, float])
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, transformed image.
- """
- image = check_numpy_param('image', image)
- new_img = cv2.convertScaleAbs(image, alpha=self.alpha, beta=self.beta)
- return new_img
-
-
- class GaussianBlur:
- """
- Blurs the image using Gaussian blur filter.
-
- Args:
- ksize (Union[list, tuple]): Size of gaussian kernel.
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> ksize = 0.1
- >>> trans = GaussianBlur(ksize)
- >>> dst = trans(img)
- """
-
- def __init__(self, ksize=5):
- super(GaussianBlur, self).__init__()
- ksize = check_int_positive('ksize', ksize)
- self.ksize = (ksize, ksize)
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, transformed image.
- """
- image = check_numpy_param('image', image)
- new_img = cv2.GaussianBlur(image, self.ksize, 0)
- return new_img
-
-
- class SaltAndPepperNoise:
- """
- Add noise of an image.
-
- Args:
- factor (float): Noise density, the proportion of noise points per unit pixel area. Suggested value range in
- [0.001, 0.15].
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> factor = 0.1
- >>> trans = SaltAndPepperNoise(factor)
- >>> dst = trans(img)
- """
-
- def __init__(self, factor=0):
- super(SaltAndPepperNoise, self).__init__()
- self.factor = check_param_multi_types('factor', factor, [int, float])
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, transformed image.
- """
- image = check_numpy_param('image', image)
- ori_dtype = image.dtype
- noise = np.random.uniform(low=-1, high=1, size=(image.shape[0], image.shape[1]))
- trans_image = np.copy(image)
- threshold = 1 - self.factor
- trans_image[noise < -threshold] = (0, 0, 0)
- trans_image[noise > threshold] = (255, 255, 255)
- return trans_image.astype(ori_dtype)
-
-
- class Translate:
- """
- Translate an image.
-
- Args:
- x_bias (Union[int, float]): X-direction translation, x = x + x_bias*image_length. Suggested value range
- in [-0.1, 0.1].
- y_bias (Union[int, float]): Y-direction translation, y = y + y_bias*image_wide. Suggested value range
- in [-0.1, 0.1].
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> x_bias = 0.1
- >>> y_bias = 0.1
- >>> trans = Translate(x_bias, y_bias)
- >>> dst = trans(img)
- """
-
- def __init__(self, x_bias=0, y_bias=0):
- super(Translate, self).__init__()
- self.x_bias = check_param_multi_types('x_bias', x_bias, [int, float])
- self.y_bias = check_param_multi_types('y_bias', y_bias, [int, float])
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, transformed image.
- """
- image = check_numpy_param('image', image)
- h, w = image.shape[:2]
- matrix = np.array([[1, 0, self.x_bias * w], [0, 1, self.y_bias * h]], dtype=np.float)
- new_img = cv2.warpAffine(image, matrix, (w, h))
- return new_img
-
-
- class Scale:
- """
- Scale an image in the middle.
-
- Args:
- factor_x (Union[float, int]): Rescale in X-direction, x=factor_x*x. Suggested value range in [0.5, 1] and
- abs(factor_y - factor_x) < 0.5.
- factor_y (Union[float, int]): Rescale in Y-direction, y=factor_y*y. Suggested value range in [0.5, 1] and
- abs(factor_y - factor_x) < 0.5.
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> factor_x = 0.7
- >>> factor_y = 0.6
- >>> trans = Scale(factor_x, factor_y)
- >>> dst = trans(img)
- """
-
- def __init__(self, factor_x=1, factor_y=1):
- super(Scale, self).__init__()
- self.factor_x = check_param_multi_types('factor_x', factor_x, [int, float])
- self.factor_y = check_param_multi_types('factor_y', factor_y, [int, float])
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, transformed image.
- """
- image = check_numpy_param('image', image)
- h, w = image.shape[:2]
- matrix = np.array([[self.factor_x, 0, 0], [0, self.factor_y, 0]], dtype=np.float)
- new_img = cv2.warpAffine(image, matrix, (w, h))
- return new_img
-
-
- class Shear:
- """
- Shear an image, for each pixel (x, y) in the sheared image, the new value is taken from a position
- (x+factor_x*y, factor_y*x+y) in the origin image. Then the sheared image will be rescaled to fit original size.
-
- Args:
- factor (Union[float, int]): Shear rate in shear direction. Suggested value range in [0.05, 0.5].
- direction (str): Direction of deformation. Optional value is 'vertical' or 'horizontal'.
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> factor = 0.2
- >>> trans = Shear(factor, direction='horizontal')
- >>> dst = trans(img)
- """
-
- def __init__(self, factor, direction='horizontal'):
- super(Shear, self).__init__()
- self.factor = check_param_multi_types('factor', factor, [int, float])
- if direction not in ['horizontal', 'vertical']:
- msg = "'direction must be in ['horizontal', 'vertical'], but got {}".format(direction)
- raise ValueError(msg)
- self.direction = direction
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, transformed image.
- """
- h, w = image.shape[:2]
- if self.direction == 'horizontal':
- matrix = np.array([[1, self.factor, 0], [0, 1, 0]], dtype=np.float)
- nw = int(w + self.factor * h)
- nh = h
- else:
- matrix = np.array([[1, 0, 0], [self.factor, 1, 0]], dtype=np.float)
- nw = w
- nh = int(h + self.factor * w)
- new_img = cv2.warpAffine(image, matrix, (nw, nh))
- new_img = cv2.resize(new_img, (w, h))
- return new_img
-
-
- class Rotate:
- """
- Rotate an image of counter clockwise around its center.
-
- Args:
- angle (Union[float, int]): Degrees of counter clockwise. Suggested value range in [-60, 60].
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> angle = 20
- >>> trans = Rotate(angle)
- >>> dst = trans(img)
- """
- def __init__(self, angle=20):
- super(Rotate, self).__init__()
- self.angle = check_param_multi_types('angle', angle, [int, float])
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, rotated image.
- """
- image = check_numpy_param('image', image)
- h, w = image.shape[:2]
- center = (w // 2, h // 2)
- matrix = cv2.getRotationMatrix2D(center, -self.angle, 1.0)
- cos = np.abs(matrix[0, 0])
- sin = np.abs(matrix[0, 1])
-
- # Calculate new edge after rotated
- nw = int((h * sin) + (w * cos))
- nh = int((h * cos) + (w * sin))
- # Adjust move distance of rotate matrix.
- matrix[0, 2] += (nw / 2) - center[0]
- matrix[1, 2] += (nh / 2) - center[1]
- rotate = cv2.warpAffine(image, matrix, (nw, nh))
- rotate = cv2.resize(rotate, (w, h))
- return rotate
-
-
- class Perspective:
- """
- Perform perspective transformation on a given picture.
-
- Args:
- ori_pos (list): Four points in original image.
- dst_pos (list): The point coordinates of the 4 points in ori_pos after perspective transformation.
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> ori_pos = [[0, 0], [0, 800], [800, 0], [800, 800]]
- >>> dst_pos = [[50, 0], [0, 800], [780, 0], [800, 800]]
- >>> trans = Perspective(ori_pos, dst_pos)
- >>> dst = trans(img)
- """
-
- def __init__(self, ori_pos, dst_pos):
- super(Perspective, self).__init__()
- ori_pos = check_param_type('ori_pos', ori_pos, list)
- dst_pos = check_param_type('dst_pos', dst_pos, list)
- self.ori_pos = np.float32(ori_pos)
- self.dst_pos = np.float32(dst_pos)
-
- def __call__(self, image):
- """
- Transform the image.
-
- Args:
- image (numpy.ndarray): Original image to be transformed.
-
- Returns:
- numpy.ndarray, transformed image.
- """
- image = check_numpy_param('image', image)
- h, w = image.shape[:2]
- matrix = cv2.getPerspectiveTransform(self.ori_pos, self.dst_pos)
- new_img = cv2.warpPerspective(image, matrix, (w, h))
- return new_img
-
-
- class MotionBlur:
- """
- Motion blur for a given image.
-
- Args:
- degree (int): Degree of blur. This value must be positive. Suggested value range in [1, 15].
- angle: (union[float, int]): Direction of motion blur. Angle=0 means up and down motion blur. Angle is
- counterclockwise.
-
- Example:
- >>> img = cv2.imread('1.png')
- >>> img = np.array(img)
- >>> angle = 0
- >>> degree = 5
- >>> trans = MotionBlur(degree=degree, angle=angle)
- >>> new_img = trans(img)
- """
-
- def __init__(self, degree=5, angle=45):
- super(MotionBlur, self).__init__()
- self.degree = check_int_positive('degree', degree)
- self.degree = check_param_multi_types('degree', degree, [float, int])
- self.angle = angle - 45
-
- def __call__(self, image):
- """
- Motion blur for a given image.
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, image after motion blur.
- """
- image = check_numpy_param('image', image)
- matrix = cv2.getRotationMatrix2D((self.degree / 2, self.degree / 2), self.angle, 1)
- motion_blur_kernel = np.diag(np.ones(self.degree))
- motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, matrix, (self.degree, self.degree))
- motion_blur_kernel = motion_blur_kernel / self.degree
- blurred = cv2.filter2D(image, -1, motion_blur_kernel)
- # convert to uint8
- cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
- blurred = np.array(blurred, dtype=np.uint8)
- return blurred
-
-
- class GradientBlur:
- """
- Gradient blur.
-
- Args:
- point (union[tuple, list]): 2D coordinate of the Blur center point.
- kernel_num (int): Number of blur kernels. Suggested value range in [1, 8].
- center (bool): Blurred or clear at the center of a specified point.
-
- Example:
- >>> img = cv2.imread('xx.png')
- >>> img = np.array(img)
- >>> number = 5
- >>> h, w = img.shape[:2]
- >>> point = (int(h / 5), int(w / 5))
- >>> center = True
- >>> trans = GradientBlur(point, number, center)
- >>> new_img = trans(img)
- """
-
- def __init__(self, point, kernel_num=3, center=True):
- super(GradientBlur).__init__()
- point = check_param_multi_types('point', point, [list, tuple])
- self.point = tuple(point)
- self.kernel_num = check_int_positive('kernel_num', kernel_num)
- self.center = check_param_type('center', center, bool)
-
- def __call__(self, image):
- """
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, gradient blurred image.
- """
- image = check_numpy_param('image', image)
- w, h = image.shape[:2]
- mask = np.zeros(image.shape, dtype=np.uint8)
- masks = []
- radius = max(w - self.point[0], self.point[0], h - self.point[1], self.point[1])
- radius = int(radius / self.kernel_num)
- for i in range(self.kernel_num):
- circle = cv2.circle(mask.copy(), self.point, radius * (1 + i), (1, 1, 1), -1)
- masks.append(circle)
- blurs = []
- for i in range(3, 3 + 2 * self.kernel_num, 2):
- ksize = (i, i)
- blur = cv2.GaussianBlur(image, ksize, 0)
- blurs.append(blur)
-
- dst = image.copy()
- if self.center:
- for i in range(self.kernel_num):
- dst = masks[i] * dst + (1 - masks[i]) * blurs[i]
- else:
- for i in range(self.kernel_num - 1, -1, -1):
- dst = masks[i] * blurs[self.kernel_num - 1 - i] + (1 - masks[i]) * dst
- return dst
-
-
- def _circle_gradient_mask(img_src, color_start, color_end, scope=0.5, point=None):
- """
- Generate circle gradient mask.
-
- Args:
- img_src (numpy.ndarray): Source image.
- color_start (union([tuple, list])): Color of circle gradient center.
- color_end (union([tuple, list])): Color of circle gradient edge.
- scope (float): Range of the gradient. A larger value indicates a larger gradient range.
- point (union([tuple, list]): Gradient center point.
-
- Returns:
- numpy.ndarray, gradients mask.
- """
- if not isinstance(img_src, np.ndarray):
- raise TypeError('`src` must be numpy.ndarray type, but got {0}.'.format(type(img_src)))
-
- height, width = img_src.shape[:2]
-
- if point is None:
- point = (height // 2, width // 2)
- x, y = point
-
- # upper left
- bound_upper_left = math.ceil(math.sqrt(x ** 2 + y ** 2))
- # upper right
- bound_upper_right = math.ceil(math.sqrt(height ** 2 + (width - y) ** 2))
- # lower left
- bound_lower_left = math.ceil(math.sqrt((height - x) ** 2 + y ** 2))
- # lower right
- bound_lower_right = math.ceil(math.sqrt((height - x) ** 2 + (width - y) ** 2))
-
- radius = max(bound_lower_left, bound_lower_right, bound_upper_left, bound_upper_right) * scope
-
- img_grad = np.ones_like(img_src, dtype=np.uint8) * max(color_end)
- # opencv use BGR format
- grad_b = float(color_end[0] - color_start[0]) / radius
- grad_g = float(color_end[1] - color_start[1]) / radius
- grad_r = float(color_end[2] - color_start[2]) / radius
-
- for i in range(height):
- for j in range(width):
- distance = math.ceil(math.sqrt((x - i) ** 2 + (y - j) ** 2))
- if distance >= radius:
- continue
- img_grad[i, j, 0] = color_start[0] + distance * grad_b
- img_grad[i, j, 1] = color_start[1] + distance * grad_g
- img_grad[i, j, 2] = color_start[2] + distance * grad_r
-
- return img_grad
-
-
- def _line_gradient_mask(image, start_pos=None, start_color=(0, 0, 0), end_color=(255, 255, 255), mode='horizontal'):
- """
- Generate liner gradient mask.
-
- Args:
- image (numpy.ndarray): Original image.
- start_pos (union[tuple, list]): 2D coordinate of gradient center.
- start_color (union([tuple, list])): Color of circle gradient center.
- end_color (union([tuple, list])): Color of circle gradient edge.
- mode (str): Direction of gradient. Optional value is 'vertical' or 'horizontal'.
-
- Returns:
- numpy.ndarray, gradients mask.
- """
- h, w = image.shape[:2]
- if start_pos is None:
- start_pos = 0.5
- else:
- if mode == 'horizontal':
- start_pos = start_pos[0] / h
- else:
- start_pos = start_pos[1] / w
- start_color = np.array(start_color)
- end_color = np.array(end_color)
- if mode == 'horizontal':
- w_l = int(w * start_pos)
- w_r = w - w_l
- if w_l > w_r:
- r_end_color = (end_color - start_color) / start_pos * (1 - start_pos) + start_color
- left = np.linspace(end_color, start_color, w_l)
- right = np.linspace(start_color, r_end_color, w_r)
- else:
- l_end_color = (end_color - start_color) / (1 - start_pos) * start_pos + start_color
- left = np.linspace(l_end_color, start_color, w_l)
- right = np.linspace(start_color, end_color, w_r)
- line = np.concatenate((left, right), axis=0)
- mask = np.reshape(np.tile(line, (h, 1)), (h, w, 3))
- mask = np.array(mask, dtype=np.uint8)
- else:
- # 'vertical'
- h_t = int(h * start_pos)
- h_b = h - h_t
- if h_t > h_b:
- b_end_color = (end_color - start_color) / start_pos * (1 - start_pos) + start_color
- top = np.linspace(end_color, start_color, h_t)
- bottom = np.linspace(start_color, b_end_color, h_b)
- else:
- t_end_color = (end_color - start_color) / (1 - start_pos) * start_pos + start_color
- top = np.linspace(t_end_color, start_color, h_t)
- bottom = np.linspace(start_color, end_color, h_b)
- line = np.concatenate((top, bottom), axis=0)
- mask = np.reshape(np.tile(line, (w, 1)), (w, h, 3))
- mask = np.transpose(mask, [1, 0, 2])
- mask = np.array(mask, dtype=np.uint8)
- return mask
-
-
- class GradientLuminance:
- """
- Gradient adjusts the luminance of picture.
-
- Args:
- color_start (union[tuple, list]): Color of gradient center. Default:(0, 0, 0).
- color_end (union[tuple, list]): Color of gradient edge. Default:(255, 255, 255).
- start_point (union[tuple, list]): 2D coordinate of gradient center.
- scope (float): Range of the gradient. A larger value indicates a larger gradient range. Default: 0.3.
- pattern (str): Dark or light, this value must be in ['light', 'dark'].
- bright_rate (float): Control brightness of . A larger value indicates a larger gradient range. If parameter
- 'pattern' is 'light', Suggested value range in [0.1, 0.7], if parameter 'pattern' is 'dark', Suggested value
- range in [0.1, 0.9].
- mode (str): Gradient mode, value must be in ['circle', 'horizontal', 'vertical'].
-
- Examples:
- >>> img = cv2.imread('x.png')
- >>> height, width = img.shape[:2]
- >>> point = (height // 4, width // 2)
- >>> start = (255, 255, 255)
- >>> end = (0, 0, 0)
- >>> scope = 0.3
- >>> pattern='light'
- >>> bright_rate = 0.3
- >>> trans = GradientLuminance(start, end, point, scope, pattern, bright_rate, mode='circle')
- >>> img_new = trans(img)
- """
-
- def __init__(self, color_start, color_end, start_point, scope=0.5, pattern='light', bright_rate=0.3, mode='circle'):
- self.color_start = check_param_multi_types('color_start', color_start, [list, tuple])
- self.color_end = check_param_multi_types('color_end', color_end, [list, tuple])
- self.start_point = check_param_multi_types('start_point', start_point, [list, tuple])
- self.scope = check_value_non_negative('scope', scope)
- self.bright_rate = check_param_type('bright_rate', bright_rate, float)
- self.bright_rate = check_param_in_range('bright_rate', bright_rate, 0, 1)
-
- if pattern in ['light', 'dark']:
- self.pattern = pattern
- else:
- msg = "Value of param pattern must be in ['light', 'dark']"
- LOGGER.error(TAG, msg)
- raise ValueError(msg)
- if mode in ['circle', 'horizontal', 'vertical']:
- self.mode = mode
- else:
- msg = "Value of param mode must be in ['circle', 'horizontal', 'vertical']"
- LOGGER.error(TAG, msg)
- raise ValueError(msg)
-
- def __call__(self, image):
- """
- Gradient adjusts the luminance of picture.
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, image with perlin noise.
- """
- image = check_numpy_param('image', image)
- if self.mode == 'circle':
- mask = _circle_gradient_mask(image, self.color_start, self.color_end, self.scope, self.start_point)
- else:
- mask = _line_gradient_mask(image, self.start_point, self.color_start, self.color_end, mode=self.mode)
-
- if self.pattern == 'light':
- img_new = cv2.addWeighted(image, 1, mask, self.bright_rate, 0.0)
- else:
- img_new = cv2.addWeighted(image, self.bright_rate, mask, 1 - self.bright_rate, 0.0)
- return img_new
-
-
- class Perlin:
- """
- Add perlin noise to given image.
-
- Args:
- ratio (float): Noise density. Suggested value range in [0.05, 0.9].
- shade (float): The degree of background shade color. Suggested value range in [0.1, 0.5].
-
- Examples:
- >>> img = cv2.imread('xx.png')
- >>> img = np.array(img)
- >>> ratio = 0.2
- >>> shade = 0.1
- >>> trans = Perlin(ratio, shade)
- >>> new_img = trans(img)
- """
-
- def __init__(self, ratio, shade=0.1):
- super(Perlin).__init__()
- ratio = check_param_type('ratio', ratio, float)
- ratio = check_param_in_range('ratio', ratio, 0, 1)
- if ratio > 0.7:
- self.ratio = 7
- else:
- self.ratio = int(ratio * 10)
- shade = check_param_type('shade', shade, float)
- self.shade = check_param_in_range('shade', shade, 0, 1)
-
- def __call__(self, image):
- """
- Add perlin noise to given image.
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, image with perlin noise.
- """
- image = check_numpy_param('image', image)
- noise = generate_fractal_noise_2d((1024, 1024), (2 ** self.ratio, 2 ** self.ratio), 4)
- noise[noise < 0] = 0
- noise[noise > 1] = 1
- back = np.array((1 - noise) * 255, dtype=np.uint8)
- back = cv2.resize(back, (image.shape[1], image.shape[0]))
- back = np.resize(np.repeat(back, 3), image.shape)
- dst = cv2.addWeighted(image, 1 - self.shade, back, self.shade, 0)
- return dst
-
-
- class BackShadow:
- """
- Add background picture to given image.
-
- Args:
- template_path (str): Path of template pictures file.
- shade (float): The weight of background. Suggested value range in [0.1, 0.7].
-
- Examples:
- >>> img = cv2.imread('xx.png')
- >>> img = np.array(img)
- >>> template_path = 'template/leaf'
- >>> shade = 0.2
- >>> trans = BackShadow(template_path, shade=shade)
- >>> new_img = trans(img)
- """
-
- def __init__(self, template_path, shade=0.1):
- super(BackShadow).__init__()
- if os.path.exists(template_path):
- self.template_path = template_path
- else:
- msg = "Template_path is not exist"
- LOGGER.error(TAG, msg)
- raise ValueError(msg)
- shade = check_param_type('shade', shade, float)
- self.shade = check_param_in_range('shade', shade, 0, 1)
-
- def __call__(self, image):
- """
- Add background picture to given image.
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, image with background shadow.
- """
- image = check_numpy_param('image', image)
- file = os.listdir(self.template_path)
- file_path = os.path.join(self.template_path, np.random.choice(file))
- shadow = cv2.imread(file_path)
- shadow = cv2.resize(shadow, (image.shape[1], image.shape[0]))
- dst = cv2.addWeighted(image, 1 - self.shade, shadow, self.shade, 0)
- return dst
-
-
- class NaturalNoise:
- """
- Add natural noise to an image.
-
- Args:
- ratio (float): Noise density, the proportion of noise blocks per unit pixel area. Suggested value range in
- [0.00001, 0.001].
- k_x_range (union[list, tuple]): Value range of the noise block length.
- k_y_range (union[list, tuple]): Value range of the noise block width.
-
- Examples:
- >>> img = cv2.imread('xx.png')
- >>> img = np.array(img)
- >>> ratio = 0.0002
- >>> k_x_range = (1, 5)
- >>> k_y_range = (3, 25)
- >>> trans = NaturalNoise(ratio, k_x_range, k_y_range)
- >>> new_img = trans(img)
- """
-
- def __init__(self, ratio=0.0002, k_x_range=(1, 5), k_y_range=(3, 25)):
- super(NaturalNoise).__init__()
- self.ratio = check_param_type('ratio', ratio, float)
- k_x_range = check_param_multi_types('k_x_range', k_x_range, [list, tuple])
- k_y_range = check_param_multi_types('k_y_range', k_y_range, [list, tuple])
- self.k_x_range = tuple(k_x_range)
- self.k_y_range = tuple(k_y_range)
-
- def __call__(self, image):
- """
- Add natural noise to given image.
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, image with natural noise.
- """
- image = check_numpy_param('image', image)
- randon_range = 100
- w, h = image.shape[:2]
- dst = np.ones((w, h, 3), dtype=np.uint8) * 255
- for _ in range(5):
- noise = np.ones((w, h, 3), dtype=np.uint8) * 255
- rate = self.ratio / 5
- mask = np.random.uniform(size=(w, h)) < rate
- noise[mask] = np.random.randint(0, randon_range)
-
- k_x, k_y = np.random.randint(*self.k_x_range), np.random.randint(*self.k_y_range)
- kernel = np.ones((k_x, k_y), np.uint8)
- erode = cv2.erode(noise, kernel, iterations=1)
- dst = erode * (erode < randon_range) + dst * (1 - erode < randon_range)
- # Add black point
- for _ in range(np.random.randint(k_x * k_y / 2)):
- x = np.random.randint(-k_x, k_x)
- y = np.random.randint(-k_y, k_y)
- matrix = np.array([[1, 0, y], [0, 1, x]], dtype=np.float)
- affine = cv2.warpAffine(noise, matrix, (h, w))
- dst = affine * (affine < randon_range) + dst * (1 - affine < randon_range)
- # Add white point
- for _ in range(int(k_x * k_y / 2)):
- x = np.random.randint(-k_x / 2 - 1, k_x / 2 + 1)
- y = np.random.randint(-k_y / 2 - 1, k_y / 2 + 1)
- matrix = np.array([[1, 0, y], [0, 1, x]], dtype=np.float)
- affine = cv2.warpAffine(noise, matrix, (h, w))
- white = affine < randon_range
- dst[white] = 255
-
- mask = dst < randon_range
- dst = image * (1 - mask) + dst * mask
- dst = np.array(dst, dtype=np.uint8)
- return dst
-
-
- class Curve:
- """
- Curve picture using sin method.
-
- Args:
- curves (union[float, int]): Divide width to curves of `2*math.pi`, which means how many curve cycles. Suggested
- value range in [0.1. 5].
- depth (union[float, int]): Amplitude of sin method. Suggested value not exceed 1/10 of the length of the picture.
- mode (str): Direction of deformation. Optional value is 'vertical' or 'horizontal'.
-
- Examples:
- >>> img = cv2.imread('x.png')
- >>> curves =1
- >>> depth = 10
- >>> trans = Curve(curves, depth, mode='vertical')
- >>> img_new = trans(img)
- """
-
- def __init__(self, curves=10, depth=10, mode='vertical'):
- super(Curve).__init__()
- self.curves = check_value_non_negative('curves', curves)
- self.depth = check_value_non_negative('depth', depth)
- if mode in ['vertical', 'horizontal']:
- self.mode = mode
- else:
- msg = "Value of param mode must be in ['vertical', 'horizontal']"
- LOGGER.error(TAG, msg)
- raise ValueError(msg)
-
- def __call__(self, image):
- """
- Curve picture using sin method.
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, curved image.
- """
- image = check_numpy_param('image', image)
- if self.mode == 'vertical':
- image = np.transpose(image, [1, 0, 2])
- heights, widths = image.shape[:2]
-
- src_x = np.zeros((heights, widths), np.float32)
- src_y = np.zeros((heights, widths), np.float32)
-
- for y in range(heights):
- for x in range(widths):
- src_x[y, x] = x
- src_y[y, x] = y + self.depth * math.sin(x / (widths / self.curves / 2 / math.pi))
- img_new = cv2.remap(image, src_x, src_y, cv2.INTER_LINEAR)
- if self.mode == 'vertical':
- img_new = np.transpose(img_new, [1, 0, 2])
- return img_new
-
-
- class BackgroundWord:
- """
- Overlay the background image on the original image.
-
- Args:
- shade (float): The weight of background. Suggested value range in [0.05, 0.3].
- back (numpy.ndarray): Background Image. If none, mean background image is the as original image.
-
- Examples:
- >>> img = cv2.imread('x.png')
- >>> back = cv2.imread('x.png')
- >>> shade=0.2
- >>> trans = BackgroundWord(shade, back)
- >>> img_new = trans(img)
- """
-
- def __init__(self, shade=0.1, back=None):
- super(BackgroundWord).__init__()
- self.shade = shade
- self.back = back
-
- def __call__(self, image):
- """
- Overlay the background image on the original image.
-
- Args:
- image (numpy.ndarray): Original image.
-
- Returns:
- numpy.ndarray, curved image.
- """
- image = check_numpy_param('image', image)
- width, height = image.shape[:2]
- x = np.random.randint(0, int(width / 5))
- y = np.random.randint(0, int(height / 5))
- matrix = np.array([[1, 0, y], [0, 1, x]], dtype=np.float)
- affine = cv2.warpAffine(image.copy(), matrix, (height, width))
- back = image.copy()
- back[x:, y:] = affine[x:, y:]
- dst = cv2.addWeighted(image, 1 - self.shade, back, self.shade, 0)
- return dst
|