export_embeddings.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. """
  2. Exports the embeddings and labels of a directory of images as numpy arrays.
  3. Typicall usage expect the image directory to be of the openface/facenet form and
  4. the images to be aligned. Simply point to your model and your image directory:
  5. python facenet/contributed/export_embeddings.py ~/models/facenet/20170216-091149/ ~/datasets/lfw/mylfw
  6. Output:
  7. embeddings.npy -- Embeddings as np array, Use --embeddings_name to change name
  8. labels.npy -- Integer labels as np array, Use --labels_name to change name
  9. label_strings.npy -- Strings from folders names, --labels_strings_name to change name
  10. Use --image_batch to dictacte how many images to load in memory at a time.
  11. If your images aren't already pre-aligned, use --is_aligned False
  12. I started with compare.py from David Sandberg, and modified it to export
  13. the embeddings. The image loading is done use the facenet library if the image
  14. is pre-aligned. If the image isn't pre-aligned, I use the compare.py function.
  15. I've found working with the embeddings useful for classifications models.
  16. Charles Jekel 2017
  17. """
  18. # MIT License
  19. #
  20. # Copyright (c) 2016 David Sandberg
  21. #
  22. # Permission is hereby granted, free of charge, to any person obtaining a copy
  23. # of this software and associated documentation files (the "Software"), to deal
  24. # in the Software without restriction, including without limitation the rights
  25. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  26. # copies of the Software, and to permit persons to whom the Software is
  27. # furnished to do so, subject to the following conditions:
  28. #
  29. # The above copyright notice and this permission notice shall be included in all
  30. # copies or substantial portions of the Software.
  31. #
  32. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  33. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  34. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  35. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  36. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  37. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  38. # SOFTWARE.
  39. from __future__ import absolute_import
  40. from __future__ import division
  41. from __future__ import print_function
  42. import time
  43. from scipy import misc
  44. import tensorflow as tf
  45. import numpy as np
  46. import sys
  47. import os
  48. import argparse
  49. import facenet
  50. import align.detect_face
  51. import glob
  52. from six.moves import xrange
  53. def main(args):
  54. train_set = facenet.get_dataset(args.data_dir)
  55. image_list, label_list = facenet.get_image_paths_and_labels(train_set)
  56. # fetch the classes (labels as strings) exactly as it's done in get_dataset
  57. path_exp = os.path.expanduser(args.data_dir)
  58. classes = [path for path in os.listdir(path_exp) \
  59. if os.path.isdir(os.path.join(path_exp, path))]
  60. classes.sort()
  61. # get the label strings
  62. label_strings = [name for name in classes if \
  63. os.path.isdir(os.path.join(path_exp, name))]
  64. with tf.Graph().as_default():
  65. with tf.Session() as sess:
  66. # Load the model
  67. facenet.load_model(args.model_dir)
  68. # Get input and output tensors
  69. images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
  70. embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
  71. phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
  72. # Run forward pass to calculate embeddings
  73. nrof_images = len(image_list)
  74. print('Number of images: ', nrof_images)
  75. batch_size = args.image_batch
  76. if nrof_images % batch_size == 0:
  77. nrof_batches = nrof_images // batch_size
  78. else:
  79. nrof_batches = (nrof_images // batch_size) + 1
  80. print('Number of batches: ', nrof_batches)
  81. embedding_size = embeddings.get_shape()[1]
  82. emb_array = np.zeros((nrof_images, embedding_size))
  83. start_time = time.time()
  84. for i in range(nrof_batches):
  85. if i == nrof_batches -1:
  86. n = nrof_images
  87. else:
  88. n = i*batch_size + batch_size
  89. # Get images for the batch
  90. if args.is_aligned is True:
  91. images = facenet.load_data(image_list[i*batch_size:n], False, False, args.image_size)
  92. else:
  93. images = load_and_align_data(image_list[i*batch_size:n], args.image_size, args.margin, args.gpu_memory_fraction)
  94. feed_dict = { images_placeholder: images, phase_train_placeholder:False }
  95. # Use the facenet model to calcualte embeddings
  96. embed = sess.run(embeddings, feed_dict=feed_dict)
  97. emb_array[i*batch_size:n, :] = embed
  98. print('Completed batch', i+1, 'of', nrof_batches)
  99. run_time = time.time() - start_time
  100. print('Run time: ', run_time)
  101. # export emedings and labels
  102. label_list = np.array(label_list)
  103. np.save(args.embeddings_name, emb_array)
  104. np.save(args.labels_name, label_list)
  105. label_strings = np.array(label_strings)
  106. np.save(args.labels_strings_name, label_strings[label_list])
  107. def load_and_align_data(image_paths, image_size, margin, gpu_memory_fraction):
  108. minsize = 20 # minimum size of face
  109. threshold = [ 0.6, 0.7, 0.7 ] # three steps's threshold
  110. factor = 0.709 # scale factor
  111. print('Creating networks and loading parameters')
  112. with tf.Graph().as_default():
  113. gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_memory_fraction)
  114. sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
  115. with sess.as_default():
  116. pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None)
  117. nrof_samples = len(image_paths)
  118. img_list = [None] * nrof_samples
  119. for i in xrange(nrof_samples):
  120. print(image_paths[i])
  121. img = misc.imread(os.path.expanduser(image_paths[i]))
  122. img_size = np.asarray(img.shape)[0:2]
  123. bounding_boxes, _ = align.detect_face.detect_face(img, minsize, pnet, rnet, onet, threshold, factor)
  124. det = np.squeeze(bounding_boxes[0,0:4])
  125. bb = np.zeros(4, dtype=np.int32)
  126. bb[0] = np.maximum(det[0]-margin/2, 0)
  127. bb[1] = np.maximum(det[1]-margin/2, 0)
  128. bb[2] = np.minimum(det[2]+margin/2, img_size[1])
  129. bb[3] = np.minimum(det[3]+margin/2, img_size[0])
  130. cropped = img[bb[1]:bb[3],bb[0]:bb[2],:]
  131. aligned = misc.imresize(cropped, (image_size, image_size), interp='bilinear')
  132. prewhitened = facenet.prewhiten(aligned)
  133. img_list[i] = prewhitened
  134. images = np.stack(img_list)
  135. return images
  136. def parse_arguments(argv):
  137. parser = argparse.ArgumentParser()
  138. parser.add_argument('model_dir', type=str,
  139. help='Directory containing the meta_file and ckpt_file')
  140. parser.add_argument('data_dir', type=str,
  141. help='Directory containing images. If images are not already aligned and cropped include --is_aligned False.')
  142. parser.add_argument('--is_aligned', type=str,
  143. help='Is the data directory already aligned and cropped?', default=True)
  144. parser.add_argument('--image_size', type=int,
  145. help='Image size (height, width) in pixels.', default=160)
  146. parser.add_argument('--margin', type=int,
  147. help='Margin for the crop around the bounding box (height, width) in pixels.',
  148. default=44)
  149. parser.add_argument('--gpu_memory_fraction', type=float,
  150. help='Upper bound on the amount of GPU memory that will be used by the process.',
  151. default=1.0)
  152. parser.add_argument('--image_batch', type=int,
  153. help='Number of images stored in memory at a time. Default 500.',
  154. default=500)
  155. # numpy file Names
  156. parser.add_argument('--embeddings_name', type=str,
  157. help='Enter string of which the embeddings numpy array is saved as.',
  158. default='embeddings.npy')
  159. parser.add_argument('--labels_name', type=str,
  160. help='Enter string of which the labels numpy array is saved as.',
  161. default='labels.npy')
  162. parser.add_argument('--labels_strings_name', type=str,
  163. help='Enter string of which the labels as strings numpy array is saved as.',
  164. default='label_strings.npy')
  165. return parser.parse_args(argv)
  166. if __name__ == '__main__':
  167. main(parse_arguments(sys.argv[1:]))