/docs/examples/features_extractor.py

https://github.com/microsoft/SPTAG
Python | 169 lines | 134 code | 6 blank | 29 comment | 3 complexity | f589674ba5d75f140f8f70423df51e0f MD5 | raw file
  1. import glob
  2. import numpy as np
  3. from PIL import Image
  4. from keras import Model
  5. from keras.layers import GlobalAveragePooling2D, GlobalMaxPooling2D
  6. from keras.applications.vgg19 import VGG19
  7. from keras.applications.resnet50 import ResNet50
  8. from keras.applications.inception_v3 import InceptionV3
  9. def get_filenames(glob_pattern, recursive=True):
  10. """Extracts list of filenames (full paths) based on specific glob path pattern.
  11. Parameters
  12. ----------
  13. glob_pattern : str
  14. Glob pattern for glob to extract filenames, eg. "directory/**/*.jpg"
  15. recursive : bool, optional
  16. Recursively search through subdirectories, by default True
  17. Returns
  18. -------
  19. list
  20. List of file paths
  21. """
  22. all_files = glob.glob(glob_pattern, recursive=recursive)
  23. print('Found %s files using pattern: %s' % (len(all_files), glob_pattern))
  24. return all_files
  25. def expand2square(pil_img, background_color):
  26. """Function to pad an image to square using specific bg clr.
  27. Parameters
  28. ----------
  29. pil_img : PIL.Image
  30. Pillow Image object that should be processed
  31. background_color : int
  32. Integer value representing bg color
  33. Returns
  34. -------
  35. PIL.Image
  36. Square-padded image object
  37. """
  38. width, height = pil_img.size
  39. if width == height:
  40. return pil_img
  41. elif width > height:
  42. result = Image.new(pil_img.mode, (width, width), background_color)
  43. result.paste(pil_img, (0, (width - height) // 2))
  44. return result
  45. else:
  46. result = Image.new(pil_img.mode, (height, height), background_color)
  47. result.paste(pil_img, ((height - width) // 2, 0))
  48. return result
  49. def get_images(filenames, target_size=(200,200), color='RGB', bg_clr=0):
  50. """Reads image files from provided file paths list, applies square-padding,
  51. resizes all images into target size and returns them as a single numpy array
  52. Parameters
  53. ----------
  54. filenames : list
  55. List of image file paths
  56. target_size : tuple, optional
  57. Target size for all the images to be resized to, by default (200,200)
  58. color : str, optional
  59. Color mode strategy for PIL when loading images, by default 'RGB'
  60. bg_clr : int, optional
  61. Integer representing background color used for square-padding, by default 0
  62. Returns
  63. -------
  64. numpy.array
  65. Numpy array with resized images
  66. """
  67. imgs_list = []
  68. for filename in filenames:
  69. img = Image.open(filename).convert(color)
  70. im_square = expand2square(img, bg_clr)
  71. im_res = im_square.resize(target_size)
  72. imgs_list.append(np.array(im_res))
  73. return np.asarray(imgs_list)
  74. def create_feat_extractor(base_model, pooling_method='avg'):
  75. """Creates a features extractor based on the provided base network.
  76. Parameters
  77. ----------
  78. base_model : keras.Model
  79. Base network for feature extraction
  80. pooling_method : str, optional
  81. Pooling method that will be used as the last layer, by default 'avg'
  82. Returns
  83. -------
  84. keras.Model
  85. Ready to use feature extractor
  86. """
  87. assert pooling_method in ['avg', 'max']
  88. x = base_model.output
  89. if pooling_method=='avg':
  90. x = GlobalAveragePooling2D()(x)
  91. elif pooling_method=='max':
  92. x = GlobalMaxPooling2D()(x)
  93. model = Model(input=base_model.input, output=[x])
  94. return model
  95. def extract_features(imgs_np, pretrained_model="resnet50", pooling_method='avg'):
  96. """Takes in an array of fixed size images and returns features/embeddings
  97. returned by one of the selected pretrained networks.
  98. Parameters
  99. ----------
  100. imgs_np : numpy.array
  101. Numpy array of images
  102. pretrained_model : str, optional
  103. Name of the pretrained model to be used, by default "resnet50"
  104. ['resnet50', 'inception_v3', 'vgg19']
  105. pooling_method : str, optional
  106. Defines the last pooling layer that should be applied, by default 'avg'
  107. ['avg', 'max']
  108. Returns
  109. -------
  110. numpy.array
  111. Array of embeddings vectors. Each row represents embeddings for single input image
  112. """
  113. print('Input images shape: ', imgs_np.shape)
  114. pretrained_model = pretrained_model.lower()
  115. assert pretrained_model in ['resnet50', 'inception_v3', 'vgg19']
  116. assert pooling_method in ['avg', 'max']
  117. model_args={
  118. 'weights': 'imagenet',
  119. 'include_top': False,
  120. 'input_shape': imgs_np[0].shape
  121. }
  122. if pretrained_model=="resnet50":
  123. base = ResNet50(**model_args)
  124. from keras.applications.resnet50 import preprocess_input
  125. elif pretrained_model=="inception_v3":
  126. base = InceptionV3(**model_args)
  127. from keras.applications.inception_v3 import preprocess_input
  128. elif pretrained_model=="vgg19":
  129. base = VGG19(**model_args)
  130. from keras.applications.vgg19 import preprocess_input
  131. feat_extractor = create_feat_extractor(base, pooling_method=pooling_method)
  132. imgs_np = preprocess_input(imgs_np)
  133. embeddings_np = feat_extractor.predict(imgs_np)
  134. print('Features shape: ', embeddings_np.shape)
  135. return embeddings_np
  136. # if __name__ == "__main__":
  137. # filenames = get_filenames("101_ObjectCategories//**//*.*")
  138. # imgs_np = get_images(filenames, target_size=(200,200), color='RGB', bg_clr=0)
  139. # embeddings = extract_features(imgs_np, pretrained_model="resnet50")