crop_or_pad.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import numbers
  2. import warnings
  3. import khandy
  4. import numpy as np
  5. def crop(image, x_min, y_min, x_max, y_max, border_value=0):
  6. """
  7. See Also:
  8. translate_image
  9. References:
  10. PIL.Image.crop
  11. tf.image.resize_image_with_crop_or_pad
  12. """
  13. assert khandy.is_numpy_image(image)
  14. assert isinstance(x_min, numbers.Integral) and isinstance(y_min, numbers.Integral)
  15. assert isinstance(x_max, numbers.Integral) and isinstance(y_max, numbers.Integral)
  16. assert (x_min <= x_max) and (y_min <= y_max)
  17. src_height, src_width = image.shape[:2]
  18. dst_height, dst_width = y_max - y_min + 1, x_max - x_min + 1
  19. channels = 1 if image.ndim == 2 else image.shape[2]
  20. if image.ndim == 2:
  21. dst_image_shape = (dst_height, dst_width)
  22. else:
  23. dst_image_shape = (dst_height, dst_width, channels)
  24. if isinstance(border_value, numbers.Real):
  25. dst_image = np.full(dst_image_shape, border_value, dtype=image.dtype)
  26. elif isinstance(border_value, tuple):
  27. assert len(border_value) == channels, \
  28. 'Expected the num of elements in tuple equals the channels ' \
  29. 'of input image. Found {} vs {}'.format(
  30. len(border_value), channels)
  31. if channels == 1:
  32. dst_image = np.full(dst_image_shape, border_value[0], dtype=image.dtype)
  33. else:
  34. border_value = np.asarray(border_value, dtype=image.dtype)
  35. dst_image = np.empty(dst_image_shape, dtype=image.dtype)
  36. dst_image[:] = border_value
  37. else:
  38. raise ValueError(
  39. 'Invalid type {} for `border_value`.'.format(type(border_value)))
  40. src_x_begin = max(x_min, 0)
  41. src_x_end = min(x_max + 1, src_width)
  42. dst_x_begin = src_x_begin - x_min
  43. dst_x_end = src_x_end - x_min
  44. src_y_begin = max(y_min, 0)
  45. src_y_end = min(y_max + 1, src_height)
  46. dst_y_begin = src_y_begin - y_min
  47. dst_y_end = src_y_end - y_min
  48. if (src_x_begin >= src_x_end) or (src_y_begin >= src_y_end):
  49. return dst_image
  50. dst_image[dst_y_begin: dst_y_end, dst_x_begin: dst_x_end, ...] = \
  51. image[src_y_begin: src_y_end, src_x_begin: src_x_end, ...]
  52. return dst_image
  53. def crop_or_pad(image, x_min, y_min, x_max, y_max, border_value=0):
  54. warnings.warn('crop_or_pad will be deprecated, use crop instead!')
  55. return crop(image, x_min, y_min, x_max, y_max, border_value)
  56. def crop_coords(boxes, image_width, image_height):
  57. """
  58. References:
  59. `mmcv.impad`
  60. `pad` in https://github.com/kpzhang93/MTCNN_face_detection_alignment
  61. `MtcnnDetector.pad` in https://github.com/AITTSMD/MTCNN-Tensorflow
  62. """
  63. x_mins = boxes[:, 0]
  64. y_mins = boxes[:, 1]
  65. x_maxs = boxes[:, 2]
  66. y_maxs = boxes[:, 3]
  67. dst_widths = x_maxs - x_mins + 1
  68. dst_heights = y_maxs - y_mins + 1
  69. src_x_begin = np.maximum(x_mins, 0)
  70. src_x_end = np.minimum(x_maxs + 1, image_width)
  71. dst_x_begin = src_x_begin - x_mins
  72. dst_x_end = src_x_end - x_mins
  73. src_y_begin = np.maximum(y_mins, 0)
  74. src_y_end = np.minimum(y_maxs + 1, image_height)
  75. dst_y_begin = src_y_begin - y_mins
  76. dst_y_end = src_y_end - y_mins
  77. coords = np.stack([dst_y_begin, dst_y_end, dst_x_begin, dst_x_end,
  78. src_y_begin, src_y_end, src_x_begin, src_x_end,
  79. dst_heights, dst_widths], axis=0)
  80. return coords
  81. def crop_or_pad_coords(boxes, image_width, image_height):
  82. warnings.warn('crop_or_pad_coords will be deprecated, use crop_coords instead!')
  83. return crop_coords(boxes, image_width, image_height)
  84. def center_crop(image, dst_width, dst_height, strict=True):
  85. """
  86. strict:
  87. when True, raise error if src size is less than dst size.
  88. when False, remain unchanged if src size is less than dst size, otherwise center crop.
  89. """
  90. assert khandy.is_numpy_image(image)
  91. assert isinstance(dst_width, numbers.Integral) and isinstance(dst_height, numbers.Integral)
  92. src_height, src_width = image.shape[:2]
  93. if strict:
  94. assert (src_height >= dst_height) and (src_width >= dst_width)
  95. crop_top = max((src_height - dst_height) // 2, 0)
  96. crop_left = max((src_width - dst_width) // 2, 0)
  97. cropped = image[crop_top: dst_height + crop_top,
  98. crop_left: dst_width + crop_left, ...]
  99. return cropped
  100. def center_pad(image, dst_width, dst_height, strict=True):
  101. """
  102. strict:
  103. when True, raise error if src size is greater than dst size.
  104. when False, remain unchanged if src size is greater than dst size, otherwise center pad.
  105. """
  106. assert khandy.is_numpy_image(image)
  107. assert isinstance(dst_width, numbers.Integral) and isinstance(dst_height, numbers.Integral)
  108. src_height, src_width = image.shape[:2]
  109. if strict:
  110. assert (src_height <= dst_height) and (src_width <= dst_width)
  111. padding_x = max(dst_width - src_width, 0)
  112. padding_y = max(dst_height - src_height, 0)
  113. padding_top = padding_y // 2
  114. padding_left = padding_x // 2
  115. if image.ndim == 2:
  116. padding = ((padding_top, padding_y - padding_top),
  117. (padding_left, padding_x - padding_left))
  118. else:
  119. padding = ((padding_top, padding_y - padding_top),
  120. (padding_left, padding_x - padding_left), (0, 0))
  121. return np.pad(image, padding, 'constant')