You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

natural_noise.py 33 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. # Copyright 2019 Huawei Technologies Co., Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """
  15. Image transform
  16. """
  17. import os
  18. import math
  19. import numpy as np
  20. import cv2
  21. from perlin_numpy import generate_fractal_noise_2d
  22. from mindarmour.utils._check_param import check_param_multi_types, check_param_in_range, check_numpy_param, \
  23. check_int_positive, check_param_type, check_value_non_negative
  24. from mindarmour.utils.logger import LogUtil
  25. LOGGER = LogUtil.get_instance()
  26. TAG = 'Image Transformation'
  27. class Contrast:
  28. """
  29. Contrast of an image.
  30. Args:
  31. alpha (Union[float, int]): Control the contrast of an image. Suggested value range in [0.2, 2].
  32. beta (Union[float, int]): Delta added to alpha. Default: 0.
  33. Example:
  34. >>> img = cv2.imread('1.png')
  35. >>> img = np.array(img)
  36. >>> alpha = 0.1
  37. >>> beta = 1
  38. >>> trans = Contrast(alpha, beta)
  39. >>> dst = trans(img)
  40. """
  41. def __init__(self, alpha=1, beta=0):
  42. super(Contrast, self).__init__()
  43. self.alpha = check_param_multi_types('factor', alpha, [int, float])
  44. self.beta = check_param_multi_types('factor', beta, [int, float])
  45. def __call__(self, image):
  46. """
  47. Transform the image.
  48. Args:
  49. image (numpy.ndarray): Original image to be transformed.
  50. Returns:
  51. numpy.ndarray, transformed image.
  52. """
  53. image = check_numpy_param('image', image)
  54. new_img = cv2.convertScaleAbs(image, alpha=self.alpha, beta=self.beta)
  55. return new_img
  56. class GaussianBlur:
  57. """
  58. Blurs the image using Gaussian blur filter.
  59. Args:
  60. ksize (Union[list, tuple]): Size of gaussian kernel.
  61. Example:
  62. >>> img = cv2.imread('1.png')
  63. >>> img = np.array(img)
  64. >>> ksize = 0.1
  65. >>> trans = GaussianBlur(ksize)
  66. >>> dst = trans(img)
  67. """
  68. def __init__(self, ksize=5):
  69. super(GaussianBlur, self).__init__()
  70. ksize = check_int_positive('ksize', ksize)
  71. self.ksize = (ksize, ksize)
  72. def __call__(self, image):
  73. """
  74. Transform the image.
  75. Args:
  76. image (numpy.ndarray): Original image to be transformed.
  77. Returns:
  78. numpy.ndarray, transformed image.
  79. """
  80. image = check_numpy_param('image', image)
  81. new_img = cv2.GaussianBlur(image, self.ksize, 0)
  82. return new_img
  83. class SaltAndPepperNoise:
  84. """
  85. Add noise of an image.
  86. Args:
  87. factor (float): Noise density, the proportion of noise points per unit pixel area. Suggested value range in
  88. [0.001, 0.15].
  89. Example:
  90. >>> img = cv2.imread('1.png')
  91. >>> img = np.array(img)
  92. >>> factor = 0.1
  93. >>> trans = SaltAndPepperNoise(factor)
  94. >>> dst = trans(img)
  95. """
  96. def __init__(self, factor=0):
  97. super(SaltAndPepperNoise, self).__init__()
  98. self.factor = check_param_multi_types('factor', factor, [int, float])
  99. def __call__(self, image):
  100. """
  101. Transform the image.
  102. Args:
  103. image (numpy.ndarray): Original image to be transformed.
  104. Returns:
  105. numpy.ndarray, transformed image.
  106. """
  107. image = check_numpy_param('image', image)
  108. ori_dtype = image.dtype
  109. noise = np.random.uniform(low=-1, high=1, size=(image.shape[0], image.shape[1]))
  110. trans_image = np.copy(image)
  111. threshold = 1 - self.factor
  112. trans_image[noise < -threshold] = (0, 0, 0)
  113. trans_image[noise > threshold] = (255, 255, 255)
  114. return trans_image.astype(ori_dtype)
  115. class Translate:
  116. """
  117. Translate an image.
  118. Args:
  119. x_bias (Union[int, float]): X-direction translation, x = x + x_bias*image_length. Suggested value range
  120. in [-0.1, 0.1].
  121. y_bias (Union[int, float]): Y-direction translation, y = y + y_bias*image_wide. Suggested value range
  122. in [-0.1, 0.1].
  123. Example:
  124. >>> img = cv2.imread('1.png')
  125. >>> img = np.array(img)
  126. >>> x_bias = 0.1
  127. >>> y_bias = 0.1
  128. >>> trans = Translate(x_bias, y_bias)
  129. >>> dst = trans(img)
  130. """
  131. def __init__(self, x_bias=0, y_bias=0):
  132. super(Translate, self).__init__()
  133. self.x_bias = check_param_multi_types('x_bias', x_bias, [int, float])
  134. self.y_bias = check_param_multi_types('y_bias', y_bias, [int, float])
  135. def __call__(self, image):
  136. """
  137. Transform the image.
  138. Args:
  139. image (numpy.ndarray): Original image to be transformed.
  140. Returns:
  141. numpy.ndarray, transformed image.
  142. """
  143. image = check_numpy_param('image', image)
  144. h, w = image.shape[:2]
  145. matrix = np.array([[1, 0, self.x_bias * w], [0, 1, self.y_bias * h]], dtype=np.float)
  146. new_img = cv2.warpAffine(image, matrix, (w, h))
  147. return new_img
  148. class Scale:
  149. """
  150. Scale an image in the middle.
  151. Args:
  152. factor_x (Union[float, int]): Rescale in X-direction, x=factor_x*x. Suggested value range in [0.5, 1] and
  153. abs(factor_y - factor_x) < 0.5.
  154. factor_y (Union[float, int]): Rescale in Y-direction, y=factor_y*y. Suggested value range in [0.5, 1] and
  155. abs(factor_y - factor_x) < 0.5.
  156. Example:
  157. >>> img = cv2.imread('1.png')
  158. >>> img = np.array(img)
  159. >>> factor_x = 0.7
  160. >>> factor_y = 0.6
  161. >>> trans = Scale(factor_x, factor_y)
  162. >>> dst = trans(img)
  163. """
  164. def __init__(self, factor_x=1, factor_y=1):
  165. super(Scale, self).__init__()
  166. self.factor_x = check_param_multi_types('factor_x', factor_x, [int, float])
  167. self.factor_y = check_param_multi_types('factor_y', factor_y, [int, float])
  168. def __call__(self, image):
  169. """
  170. Transform the image.
  171. Args:
  172. image (numpy.ndarray): Original image to be transformed.
  173. Returns:
  174. numpy.ndarray, transformed image.
  175. """
  176. image = check_numpy_param('image', image)
  177. h, w = image.shape[:2]
  178. matrix = np.array([[self.factor_x, 0, 0], [0, self.factor_y, 0]], dtype=np.float)
  179. new_img = cv2.warpAffine(image, matrix, (w, h))
  180. return new_img
  181. class Shear:
  182. """
  183. Shear an image, for each pixel (x, y) in the sheared image, the new value is taken from a position
  184. (x+factor_x*y, factor_y*x+y) in the origin image. Then the sheared image will be rescaled to fit original size.
  185. Args:
  186. factor (Union[float, int]): Shear rate in shear direction. Suggested value range in [0.05, 0.5].
  187. direction (str): Direction of deformation. Optional value is 'vertical' or 'horizontal'.
  188. Example:
  189. >>> img = cv2.imread('1.png')
  190. >>> img = np.array(img)
  191. >>> factor = 0.2
  192. >>> trans = Shear(factor, direction='horizontal')
  193. >>> dst = trans(img)
  194. """
  195. def __init__(self, factor, direction='horizontal'):
  196. super(Shear, self).__init__()
  197. self.factor = check_param_multi_types('factor', factor, [int, float])
  198. if direction not in ['horizontal', 'vertical']:
  199. msg = "'direction must be in ['horizontal', 'vertical'], but got {}".format(direction)
  200. raise ValueError(msg)
  201. self.direction = direction
  202. def __call__(self, image):
  203. """
  204. Transform the image.
  205. Args:
  206. image (numpy.ndarray): Original image to be transformed.
  207. Returns:
  208. numpy.ndarray, transformed image.
  209. """
  210. h, w = image.shape[:2]
  211. if self.direction == 'horizontal':
  212. matrix = np.array([[1, self.factor, 0], [0, 1, 0]], dtype=np.float)
  213. nw = int(w + self.factor * h)
  214. nh = h
  215. else:
  216. matrix = np.array([[1, 0, 0], [self.factor, 1, 0]], dtype=np.float)
  217. nw = w
  218. nh = int(h + self.factor * w)
  219. new_img = cv2.warpAffine(image, matrix, (nw, nh))
  220. new_img = cv2.resize(new_img, (w, h))
  221. return new_img
  222. class Rotate:
  223. """
  224. Rotate an image of counter clockwise around its center.
  225. Args:
  226. angle (Union[float, int]): Degrees of counter clockwise. Suggested value range in [-60, 60].
  227. Example:
  228. >>> img = cv2.imread('1.png')
  229. >>> img = np.array(img)
  230. >>> angle = 20
  231. >>> trans = Rotate(angle)
  232. >>> dst = trans(img)
  233. """
  234. def __init__(self, angle=20):
  235. super(Rotate, self).__init__()
  236. self.angle = check_param_multi_types('angle', angle, [int, float])
  237. def __call__(self, image):
  238. """
  239. Transform the image.
  240. Args:
  241. image (numpy.ndarray): Original image to be transformed.
  242. Returns:
  243. numpy.ndarray, rotated image.
  244. """
  245. image = check_numpy_param('image', image)
  246. h, w = image.shape[:2]
  247. center = (w // 2, h // 2)
  248. matrix = cv2.getRotationMatrix2D(center, -self.angle, 1.0)
  249. cos = np.abs(matrix[0, 0])
  250. sin = np.abs(matrix[0, 1])
  251. # Calculate new edge after rotated
  252. nw = int((h * sin) + (w * cos))
  253. nh = int((h * cos) + (w * sin))
  254. # Adjust move distance of rotate matrix.
  255. matrix[0, 2] += (nw / 2) - center[0]
  256. matrix[1, 2] += (nh / 2) - center[1]
  257. rotate = cv2.warpAffine(image, matrix, (nw, nh))
  258. rotate = cv2.resize(rotate, (w, h))
  259. return rotate
  260. class Perspective:
  261. """
  262. Perform perspective transformation on a given picture.
  263. Args:
  264. ori_pos (list): Four points in original image.
  265. dst_pos (list): The point coordinates of the 4 points in ori_pos after perspective transformation.
  266. Example:
  267. >>> img = cv2.imread('1.png')
  268. >>> img = np.array(img)
  269. >>> ori_pos = [[0, 0], [0, 800], [800, 0], [800, 800]]
  270. >>> dst_pos = [[50, 0], [0, 800], [780, 0], [800, 800]]
  271. >>> trans = Perspective(ori_pos, dst_pos)
  272. >>> dst = trans(img)
  273. """
  274. def __init__(self, ori_pos, dst_pos):
  275. super(Perspective, self).__init__()
  276. ori_pos = check_param_type('ori_pos', ori_pos, list)
  277. dst_pos = check_param_type('dst_pos', dst_pos, list)
  278. self.ori_pos = np.float32(ori_pos)
  279. self.dst_pos = np.float32(dst_pos)
  280. def __call__(self, image):
  281. """
  282. Transform the image.
  283. Args:
  284. image (numpy.ndarray): Original image to be transformed.
  285. Returns:
  286. numpy.ndarray, transformed image.
  287. """
  288. image = check_numpy_param('image', image)
  289. h, w = image.shape[:2]
  290. matrix = cv2.getPerspectiveTransform(self.ori_pos, self.dst_pos)
  291. new_img = cv2.warpPerspective(image, matrix, (w, h))
  292. return new_img
  293. class MotionBlur:
  294. """
  295. Motion blur for a given image.
  296. Args:
  297. degree (int): Degree of blur. This value must be positive. Suggested value range in [1, 15].
  298. angle: (union[float, int]): Direction of motion blur. Angle=0 means up and down motion blur. Angle is
  299. counterclockwise.
  300. Example:
  301. >>> img = cv2.imread('1.png')
  302. >>> img = np.array(img)
  303. >>> angle = 0
  304. >>> degree = 5
  305. >>> trans = MotionBlur(degree=degree, angle=angle)
  306. >>> new_img = trans(img)
  307. """
  308. def __init__(self, degree=5, angle=45):
  309. super(MotionBlur, self).__init__()
  310. self.degree = check_int_positive('degree', degree)
  311. self.degree = check_param_multi_types('degree', degree, [float, int])
  312. self.angle = angle - 45
  313. def __call__(self, image):
  314. """
  315. Motion blur for a given image.
  316. Args:
  317. image (numpy.ndarray): Original image.
  318. Returns:
  319. numpy.ndarray, image after motion blur.
  320. """
  321. image = check_numpy_param('image', image)
  322. matrix = cv2.getRotationMatrix2D((self.degree / 2, self.degree / 2), self.angle, 1)
  323. motion_blur_kernel = np.diag(np.ones(self.degree))
  324. motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, matrix, (self.degree, self.degree))
  325. motion_blur_kernel = motion_blur_kernel / self.degree
  326. blurred = cv2.filter2D(image, -1, motion_blur_kernel)
  327. # convert to uint8
  328. cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
  329. blurred = np.array(blurred, dtype=np.uint8)
  330. return blurred
  331. class GradientBlur:
  332. """
  333. Gradient blur.
  334. Args:
  335. point (union[tuple, list]): 2D coordinate of the Blur center point.
  336. kernel_num (int): Number of blur kernels. Suggested value range in [1, 8].
  337. center (bool): Blurred or clear at the center of a specified point.
  338. Example:
  339. >>> img = cv2.imread('xx.png')
  340. >>> img = np.array(img)
  341. >>> number = 5
  342. >>> h, w = img.shape[:2]
  343. >>> point = (int(h / 5), int(w / 5))
  344. >>> center = True
  345. >>> trans = GradientBlur(point, number, center)
  346. >>> new_img = trans(img)
  347. """
  348. def __init__(self, point, kernel_num=3, center=True):
  349. super(GradientBlur).__init__()
  350. point = check_param_multi_types('point', point, [list, tuple])
  351. self.point = tuple(point)
  352. self.kernel_num = check_int_positive('kernel_num', kernel_num)
  353. self.center = check_param_type('center', center, bool)
  354. def __call__(self, image):
  355. """
  356. Args:
  357. image (numpy.ndarray): Original image.
  358. Returns:
  359. numpy.ndarray, gradient blurred image.
  360. """
  361. image = check_numpy_param('image', image)
  362. w, h = image.shape[:2]
  363. mask = np.zeros(image.shape, dtype=np.uint8)
  364. masks = []
  365. radius = max(w - self.point[0], self.point[0], h - self.point[1], self.point[1])
  366. radius = int(radius / self.kernel_num)
  367. for i in range(self.kernel_num):
  368. circle = cv2.circle(mask.copy(), self.point, radius * (1 + i), (1, 1, 1), -1)
  369. masks.append(circle)
  370. blurs = []
  371. for i in range(3, 3 + 2 * self.kernel_num, 2):
  372. ksize = (i, i)
  373. blur = cv2.GaussianBlur(image, ksize, 0)
  374. blurs.append(blur)
  375. dst = image.copy()
  376. if self.center:
  377. for i in range(self.kernel_num):
  378. dst = masks[i] * dst + (1 - masks[i]) * blurs[i]
  379. else:
  380. for i in range(self.kernel_num - 1, -1, -1):
  381. dst = masks[i] * blurs[self.kernel_num - 1 - i] + (1 - masks[i]) * dst
  382. return dst
  383. def _circle_gradient_mask(img_src, color_start, color_end, scope=0.5, point=None):
  384. """
  385. Generate circle gradient mask.
  386. Args:
  387. img_src (numpy.ndarray): Source image.
  388. color_start (union([tuple, list])): Color of circle gradient center.
  389. color_end (union([tuple, list])): Color of circle gradient edge.
  390. scope (float): Range of the gradient. A larger value indicates a larger gradient range.
  391. point (union([tuple, list]): Gradient center point.
  392. Returns:
  393. numpy.ndarray, gradients mask.
  394. """
  395. if not isinstance(img_src, np.ndarray):
  396. raise TypeError('`src` must be numpy.ndarray type, but got {0}.'.format(type(img_src)))
  397. height, width = img_src.shape[:2]
  398. if point is None:
  399. point = (height // 2, width // 2)
  400. x, y = point
  401. # upper left
  402. bound_upper_left = math.ceil(math.sqrt(x ** 2 + y ** 2))
  403. # upper right
  404. bound_upper_right = math.ceil(math.sqrt(height ** 2 + (width - y) ** 2))
  405. # lower left
  406. bound_lower_left = math.ceil(math.sqrt((height - x) ** 2 + y ** 2))
  407. # lower right
  408. bound_lower_right = math.ceil(math.sqrt((height - x) ** 2 + (width - y) ** 2))
  409. radius = max(bound_lower_left, bound_lower_right, bound_upper_left, bound_upper_right) * scope
  410. img_grad = np.ones_like(img_src, dtype=np.uint8) * max(color_end)
  411. # opencv use BGR format
  412. grad_b = float(color_end[0] - color_start[0]) / radius
  413. grad_g = float(color_end[1] - color_start[1]) / radius
  414. grad_r = float(color_end[2] - color_start[2]) / radius
  415. for i in range(height):
  416. for j in range(width):
  417. distance = math.ceil(math.sqrt((x - i) ** 2 + (y - j) ** 2))
  418. if distance >= radius:
  419. continue
  420. img_grad[i, j, 0] = color_start[0] + distance * grad_b
  421. img_grad[i, j, 1] = color_start[1] + distance * grad_g
  422. img_grad[i, j, 2] = color_start[2] + distance * grad_r
  423. return img_grad
  424. def _line_gradient_mask(image, start_pos=None, start_color=(0, 0, 0), end_color=(255, 255, 255), mode='horizontal'):
  425. """
  426. Generate liner gradient mask.
  427. Args:
  428. image (numpy.ndarray): Original image.
  429. start_pos (union[tuple, list]): 2D coordinate of gradient center.
  430. start_color (union([tuple, list])): Color of circle gradient center.
  431. end_color (union([tuple, list])): Color of circle gradient edge.
  432. mode (str): Direction of gradient. Optional value is 'vertical' or 'horizontal'.
  433. Returns:
  434. numpy.ndarray, gradients mask.
  435. """
  436. h, w = image.shape[:2]
  437. if start_pos is None:
  438. start_pos = 0.5
  439. else:
  440. if mode == 'horizontal':
  441. start_pos = start_pos[0] / h
  442. else:
  443. start_pos = start_pos[1] / w
  444. start_color = np.array(start_color)
  445. end_color = np.array(end_color)
  446. if mode == 'horizontal':
  447. w_l = int(w * start_pos)
  448. w_r = w - w_l
  449. if w_l > w_r:
  450. r_end_color = (end_color - start_color) / start_pos * (1 - start_pos) + start_color
  451. left = np.linspace(end_color, start_color, w_l)
  452. right = np.linspace(start_color, r_end_color, w_r)
  453. else:
  454. l_end_color = (end_color - start_color) / (1 - start_pos) * start_pos + start_color
  455. left = np.linspace(l_end_color, start_color, w_l)
  456. right = np.linspace(start_color, end_color, w_r)
  457. line = np.concatenate((left, right), axis=0)
  458. mask = np.reshape(np.tile(line, (h, 1)), (h, w, 3))
  459. mask = np.array(mask, dtype=np.uint8)
  460. else:
  461. # 'vertical'
  462. h_t = int(h * start_pos)
  463. h_b = h - h_t
  464. if h_t > h_b:
  465. b_end_color = (end_color - start_color) / start_pos * (1 - start_pos) + start_color
  466. top = np.linspace(end_color, start_color, h_t)
  467. bottom = np.linspace(start_color, b_end_color, h_b)
  468. else:
  469. t_end_color = (end_color - start_color) / (1 - start_pos) * start_pos + start_color
  470. top = np.linspace(t_end_color, start_color, h_t)
  471. bottom = np.linspace(start_color, end_color, h_b)
  472. line = np.concatenate((top, bottom), axis=0)
  473. mask = np.reshape(np.tile(line, (w, 1)), (w, h, 3))
  474. mask = np.transpose(mask, [1, 0, 2])
  475. mask = np.array(mask, dtype=np.uint8)
  476. return mask
  477. class GradientLuminance:
  478. """
  479. Gradient adjusts the luminance of picture.
  480. Args:
  481. color_start (union[tuple, list]): Color of gradient center. Default:(0, 0, 0).
  482. color_end (union[tuple, list]): Color of gradient edge. Default:(255, 255, 255).
  483. start_point (union[tuple, list]): 2D coordinate of gradient center.
  484. scope (float): Range of the gradient. A larger value indicates a larger gradient range. Default: 0.3.
  485. pattern (str): Dark or light, this value must be in ['light', 'dark'].
  486. bright_rate (float): Control brightness of . A larger value indicates a larger gradient range. If parameter
  487. 'pattern' is 'light', Suggested value range in [0.1, 0.7], if parameter 'pattern' is 'dark', Suggested value
  488. range in [0.1, 0.9].
  489. mode (str): Gradient mode, value must be in ['circle', 'horizontal', 'vertical'].
  490. Examples:
  491. >>> img = cv2.imread('x.png')
  492. >>> height, width = img.shape[:2]
  493. >>> point = (height // 4, width // 2)
  494. >>> start = (255, 255, 255)
  495. >>> end = (0, 0, 0)
  496. >>> scope = 0.3
  497. >>> pattern='light'
  498. >>> bright_rate = 0.3
  499. >>> trans = GradientLuminance(start, end, point, scope, pattern, bright_rate, mode='circle')
  500. >>> img_new = trans(img)
  501. """
  502. def __init__(self, color_start, color_end, start_point, scope=0.5, pattern='light', bright_rate=0.3, mode='circle'):
  503. self.color_start = check_param_multi_types('color_start', color_start, [list, tuple])
  504. self.color_end = check_param_multi_types('color_end', color_end, [list, tuple])
  505. self.start_point = check_param_multi_types('start_point', start_point, [list, tuple])
  506. self.scope = check_value_non_negative('scope', scope)
  507. self.bright_rate = check_param_type('bright_rate', bright_rate, float)
  508. self.bright_rate = check_param_in_range('bright_rate', bright_rate, 0, 1)
  509. if pattern in ['light', 'dark']:
  510. self.pattern = pattern
  511. else:
  512. msg = "Value of param pattern must be in ['light', 'dark']"
  513. LOGGER.error(TAG, msg)
  514. raise ValueError(msg)
  515. if mode in ['circle', 'horizontal', 'vertical']:
  516. self.mode = mode
  517. else:
  518. msg = "Value of param mode must be in ['circle', 'horizontal', 'vertical']"
  519. LOGGER.error(TAG, msg)
  520. raise ValueError(msg)
  521. def __call__(self, image):
  522. """
  523. Gradient adjusts the luminance of picture.
  524. Args:
  525. image (numpy.ndarray): Original image.
  526. Returns:
  527. numpy.ndarray, image with perlin noise.
  528. """
  529. image = check_numpy_param('image', image)
  530. if self.mode == 'circle':
  531. mask = _circle_gradient_mask(image, self.color_start, self.color_end, self.scope, self.start_point)
  532. else:
  533. mask = _line_gradient_mask(image, self.start_point, self.color_start, self.color_end, mode=self.mode)
  534. if self.pattern == 'light':
  535. img_new = cv2.addWeighted(image, 1, mask, self.bright_rate, 0.0)
  536. else:
  537. img_new = cv2.addWeighted(image, self.bright_rate, mask, 1 - self.bright_rate, 0.0)
  538. return img_new
  539. class Perlin:
  540. """
  541. Add perlin noise to given image.
  542. Args:
  543. ratio (float): Noise density. Suggested value range in [0.05, 0.9].
  544. shade (float): The degree of background shade color. Suggested value range in [0.1, 0.5].
  545. Examples:
  546. >>> img = cv2.imread('xx.png')
  547. >>> img = np.array(img)
  548. >>> ratio = 0.2
  549. >>> shade = 0.1
  550. >>> trans = Perlin(ratio, shade)
  551. >>> new_img = trans(img)
  552. """
  553. def __init__(self, ratio, shade=0.1):
  554. super(Perlin).__init__()
  555. ratio = check_param_type('ratio', ratio, float)
  556. ratio = check_param_in_range('ratio', ratio, 0, 1)
  557. if ratio > 0.7:
  558. self.ratio = 7
  559. else:
  560. self.ratio = int(ratio * 10)
  561. shade = check_param_type('shade', shade, float)
  562. self.shade = check_param_in_range('shade', shade, 0, 1)
  563. def __call__(self, image):
  564. """
  565. Add perlin noise to given image.
  566. Args:
  567. image (numpy.ndarray): Original image.
  568. Returns:
  569. numpy.ndarray, image with perlin noise.
  570. """
  571. image = check_numpy_param('image', image)
  572. noise = generate_fractal_noise_2d((1024, 1024), (2 ** self.ratio, 2 ** self.ratio), 4)
  573. noise[noise < 0] = 0
  574. noise[noise > 1] = 1
  575. back = np.array((1 - noise) * 255, dtype=np.uint8)
  576. back = cv2.resize(back, (image.shape[1], image.shape[0]))
  577. back = np.resize(np.repeat(back, 3), image.shape)
  578. dst = cv2.addWeighted(image, 1 - self.shade, back, self.shade, 0)
  579. return dst
  580. class BackShadow:
  581. """
  582. Add background picture to given image.
  583. Args:
  584. template_path (str): Path of template pictures file.
  585. shade (float): The weight of background. Suggested value range in [0.1, 0.7].
  586. Examples:
  587. >>> img = cv2.imread('xx.png')
  588. >>> img = np.array(img)
  589. >>> template_path = 'template/leaf'
  590. >>> shade = 0.2
  591. >>> trans = BackShadow(template_path, shade=shade)
  592. >>> new_img = trans(img)
  593. """
  594. def __init__(self, template_path, shade=0.1):
  595. super(BackShadow).__init__()
  596. if os.path.exists(template_path):
  597. self.template_path = template_path
  598. else:
  599. msg = "Template_path is not exist"
  600. LOGGER.error(TAG, msg)
  601. raise ValueError(msg)
  602. shade = check_param_type('shade', shade, float)
  603. self.shade = check_param_in_range('shade', shade, 0, 1)
  604. def __call__(self, image):
  605. """
  606. Add background picture to given image.
  607. Args:
  608. image (numpy.ndarray): Original image.
  609. Returns:
  610. numpy.ndarray, image with background shadow.
  611. """
  612. image = check_numpy_param('image', image)
  613. file = os.listdir(self.template_path)
  614. file_path = os.path.join(self.template_path, np.random.choice(file))
  615. shadow = cv2.imread(file_path)
  616. shadow = cv2.resize(shadow, (image.shape[1], image.shape[0]))
  617. dst = cv2.addWeighted(image, 1 - self.shade, shadow, self.shade, 0)
  618. return dst
  619. class NaturalNoise:
  620. """
  621. Add natural noise to an image.
  622. Args:
  623. ratio (float): Noise density, the proportion of noise blocks per unit pixel area. Suggested value range in
  624. [0.00001, 0.001].
  625. k_x_range (union[list, tuple]): Value range of the noise block length.
  626. k_y_range (union[list, tuple]): Value range of the noise block width.
  627. Examples:
  628. >>> img = cv2.imread('xx.png')
  629. >>> img = np.array(img)
  630. >>> ratio = 0.0002
  631. >>> k_x_range = (1, 5)
  632. >>> k_y_range = (3, 25)
  633. >>> trans = NaturalNoise(ratio, k_x_range, k_y_range)
  634. >>> new_img = trans(img)
  635. """
  636. def __init__(self, ratio=0.0002, k_x_range=(1, 5), k_y_range=(3, 25)):
  637. super(NaturalNoise).__init__()
  638. self.ratio = check_param_type('ratio', ratio, float)
  639. k_x_range = check_param_multi_types('k_x_range', k_x_range, [list, tuple])
  640. k_y_range = check_param_multi_types('k_y_range', k_y_range, [list, tuple])
  641. self.k_x_range = tuple(k_x_range)
  642. self.k_y_range = tuple(k_y_range)
  643. def __call__(self, image):
  644. """
  645. Add natural noise to given image.
  646. Args:
  647. image (numpy.ndarray): Original image.
  648. Returns:
  649. numpy.ndarray, image with natural noise.
  650. """
  651. image = check_numpy_param('image', image)
  652. randon_range = 100
  653. w, h = image.shape[:2]
  654. dst = np.ones((w, h, 3), dtype=np.uint8) * 255
  655. for _ in range(5):
  656. noise = np.ones((w, h, 3), dtype=np.uint8) * 255
  657. rate = self.ratio / 5
  658. mask = np.random.uniform(size=(w, h)) < rate
  659. noise[mask] = np.random.randint(0, randon_range)
  660. k_x, k_y = np.random.randint(*self.k_x_range), np.random.randint(*self.k_y_range)
  661. kernel = np.ones((k_x, k_y), np.uint8)
  662. erode = cv2.erode(noise, kernel, iterations=1)
  663. dst = erode * (erode < randon_range) + dst * (1 - erode < randon_range)
  664. # Add black point
  665. for _ in range(np.random.randint(k_x * k_y / 2)):
  666. x = np.random.randint(-k_x, k_x)
  667. y = np.random.randint(-k_y, k_y)
  668. matrix = np.array([[1, 0, y], [0, 1, x]], dtype=np.float)
  669. affine = cv2.warpAffine(noise, matrix, (h, w))
  670. dst = affine * (affine < randon_range) + dst * (1 - affine < randon_range)
  671. # Add white point
  672. for _ in range(int(k_x * k_y / 2)):
  673. x = np.random.randint(-k_x / 2 - 1, k_x / 2 + 1)
  674. y = np.random.randint(-k_y / 2 - 1, k_y / 2 + 1)
  675. matrix = np.array([[1, 0, y], [0, 1, x]], dtype=np.float)
  676. affine = cv2.warpAffine(noise, matrix, (h, w))
  677. white = affine < randon_range
  678. dst[white] = 255
  679. mask = dst < randon_range
  680. dst = image * (1 - mask) + dst * mask
  681. dst = np.array(dst, dtype=np.uint8)
  682. return dst
  683. class Curve:
  684. """
  685. Curve picture using sin method.
  686. Args:
  687. curves (union[float, int]): Divide width to curves of `2*math.pi`, which means how many curve cycles. Suggested
  688. value range in [0.1. 5].
  689. depth (union[float, int]): Amplitude of sin method. Suggested value not exceed 1/10 of the length of the picture.
  690. mode (str): Direction of deformation. Optional value is 'vertical' or 'horizontal'.
  691. Examples:
  692. >>> img = cv2.imread('x.png')
  693. >>> curves =1
  694. >>> depth = 10
  695. >>> trans = Curve(curves, depth, mode='vertical')
  696. >>> img_new = trans(img)
  697. """
  698. def __init__(self, curves=10, depth=10, mode='vertical'):
  699. super(Curve).__init__()
  700. self.curves = check_value_non_negative('curves', curves)
  701. self.depth = check_value_non_negative('depth', depth)
  702. if mode in ['vertical', 'horizontal']:
  703. self.mode = mode
  704. else:
  705. msg = "Value of param mode must be in ['vertical', 'horizontal']"
  706. LOGGER.error(TAG, msg)
  707. raise ValueError(msg)
  708. def __call__(self, image):
  709. """
  710. Curve picture using sin method.
  711. Args:
  712. image (numpy.ndarray): Original image.
  713. Returns:
  714. numpy.ndarray, curved image.
  715. """
  716. image = check_numpy_param('image', image)
  717. if self.mode == 'vertical':
  718. image = np.transpose(image, [1, 0, 2])
  719. heights, widths = image.shape[:2]
  720. src_x = np.zeros((heights, widths), np.float32)
  721. src_y = np.zeros((heights, widths), np.float32)
  722. for y in range(heights):
  723. for x in range(widths):
  724. src_x[y, x] = x
  725. src_y[y, x] = y + self.depth * math.sin(x / (widths / self.curves / 2 / math.pi))
  726. img_new = cv2.remap(image, src_x, src_y, cv2.INTER_LINEAR)
  727. if self.mode == 'vertical':
  728. img_new = np.transpose(img_new, [1, 0, 2])
  729. return img_new
  730. class BackgroundWord:
  731. """
  732. Overlay the background image on the original image.
  733. Args:
  734. shade (float): The weight of background. Suggested value range in [0.05, 0.3].
  735. back (numpy.ndarray): Background Image. If none, mean background image is the as original image.
  736. Examples:
  737. >>> img = cv2.imread('x.png')
  738. >>> back = cv2.imread('x.png')
  739. >>> shade=0.2
  740. >>> trans = BackgroundWord(shade, back)
  741. >>> img_new = trans(img)
  742. """
  743. def __init__(self, shade=0.1, back=None):
  744. super(BackgroundWord).__init__()
  745. self.shade = shade
  746. self.back = back
  747. def __call__(self, image):
  748. """
  749. Overlay the background image on the original image.
  750. Args:
  751. image (numpy.ndarray): Original image.
  752. Returns:
  753. numpy.ndarray, curved image.
  754. """
  755. image = check_numpy_param('image', image)
  756. width, height = image.shape[:2]
  757. x = np.random.randint(0, int(width / 5))
  758. y = np.random.randint(0, int(height / 5))
  759. matrix = np.array([[1, 0, y], [0, 1, x]], dtype=np.float)
  760. affine = cv2.warpAffine(image.copy(), matrix, (height, width))
  761. back = image.copy()
  762. back[x:, y:] = affine[x:, y:]
  763. dst = cv2.addWeighted(image, 1 - self.shade, back, self.shade, 0)
  764. return dst

MindArmour关注AI的安全和隐私问题。致力于增强模型的安全可信、保护用户的数据隐私。主要包含3个模块:对抗样本鲁棒性模块、Fuzz Testing模块、隐私保护与评估模块。 对抗样本鲁棒性模块 对抗样本鲁棒性模块用于评估模型对于对抗样本的鲁棒性,并提供模型增强方法用于增强模型抗对抗样本攻击的能力,提升模型鲁棒性。对抗样本鲁棒性模块包含了4个子模块:对抗样本的生成、对抗样本的检测、模型防御、攻防评估。