core.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import logging
  2. import glob
  3. from tqdm import tqdm
  4. import numpy as np
  5. import torch
  6. import cv2
  7. class FaceDetector(object):
  8. """An abstract class representing a face detector.
  9. Any other face detection implementation must subclass it. All subclasses
  10. must implement ``detect_from_image``, that return a list of detected
  11. bounding boxes. Optionally, for speed considerations detect from path is
  12. recommended.
  13. """
  14. def __init__(self, device, verbose):
  15. self.device = device
  16. self.verbose = verbose
  17. if verbose:
  18. if 'cpu' in device:
  19. logger = logging.getLogger(__name__)
  20. logger.warning("Detection running on CPU, this may be potentially slow.")
  21. if 'cpu' not in device and 'cuda' not in device:
  22. if verbose:
  23. logger.error("Expected values for device are: {cpu, cuda} but got: %s", device)
  24. raise ValueError
  25. def detect_from_image(self, tensor_or_path):
  26. """Detects faces in a given image.
  27. This function detects the faces present in a provided BGR(usually)
  28. image. The input can be either the image itself or the path to it.
  29. Arguments:
  30. tensor_or_path {numpy.ndarray, torch.tensor or string} -- the path
  31. to an image or the image itself.
  32. Example::
  33. >>> path_to_image = 'data/image_01.jpg'
  34. ... detected_faces = detect_from_image(path_to_image)
  35. [A list of bounding boxes (x1, y1, x2, y2)]
  36. >>> image = cv2.imread(path_to_image)
  37. ... detected_faces = detect_from_image(image)
  38. [A list of bounding boxes (x1, y1, x2, y2)]
  39. """
  40. raise NotImplementedError
  41. def detect_from_directory(self, path, extensions=['.jpg', '.png'], recursive=False, show_progress_bar=True):
  42. """Detects faces from all the images present in a given directory.
  43. Arguments:
  44. path {string} -- a string containing a path that points to the folder containing the images
  45. Keyword Arguments:
  46. extensions {list} -- list of string containing the extensions to be
  47. consider in the following format: ``.extension_name`` (default:
  48. {['.jpg', '.png']}) recursive {bool} -- option wherever to scan the
  49. folder recursively (default: {False}) show_progress_bar {bool} --
  50. display a progressbar (default: {True})
  51. Example:
  52. >>> directory = 'data'
  53. ... detected_faces = detect_from_directory(directory)
  54. {A dictionary of [lists containing bounding boxes(x1, y1, x2, y2)]}
  55. """
  56. if self.verbose:
  57. logger = logging.getLogger(__name__)
  58. if len(extensions) == 0:
  59. if self.verbose:
  60. logger.error("Expected at list one extension, but none was received.")
  61. raise ValueError
  62. if self.verbose:
  63. logger.info("Constructing the list of images.")
  64. additional_pattern = '/**/*' if recursive else '/*'
  65. files = []
  66. for extension in extensions:
  67. files.extend(glob.glob(path + additional_pattern + extension, recursive=recursive))
  68. if self.verbose:
  69. logger.info("Finished searching for images. %s images found", len(files))
  70. logger.info("Preparing to run the detection.")
  71. predictions = {}
  72. for image_path in tqdm(files, disable=not show_progress_bar):
  73. if self.verbose:
  74. logger.info("Running the face detector on image: %s", image_path)
  75. predictions[image_path] = self.detect_from_image(image_path)
  76. if self.verbose:
  77. logger.info("The detector was successfully run on all %s images", len(files))
  78. return predictions
  79. @property
  80. def reference_scale(self):
  81. raise NotImplementedError
  82. @property
  83. def reference_x_shift(self):
  84. raise NotImplementedError
  85. @property
  86. def reference_y_shift(self):
  87. raise NotImplementedError
  88. @staticmethod
  89. def tensor_or_path_to_ndarray(tensor_or_path, rgb=True):
  90. """Convert path (represented as a string) or torch.tensor to a numpy.ndarray
  91. Arguments:
  92. tensor_or_path {numpy.ndarray, torch.tensor or string} -- path to the image, or the image itself
  93. """
  94. if isinstance(tensor_or_path, str):
  95. return cv2.imread(tensor_or_path) if not rgb else cv2.imread(tensor_or_path)[..., ::-1]
  96. elif torch.is_tensor(tensor_or_path):
  97. # Call cpu in case its coming from cuda
  98. return tensor_or_path.cpu().numpy()[..., ::-1].copy() if not rgb else tensor_or_path.cpu().numpy()
  99. elif isinstance(tensor_or_path, np.ndarray):
  100. return tensor_or_path[..., ::-1].copy() if not rgb else tensor_or_path
  101. else:
  102. raise TypeError