clustering.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. """ Face Cluster """
  2. import tensorflow as tf
  3. import numpy as np
  4. import importlib
  5. import argparse
  6. import facenet
  7. import os
  8. import math
  9. def face_distance(face_encodings, face_to_compare):
  10. """
  11. Given a list of face encodings, compare them to a known face encoding and get a euclidean distance
  12. for each comparison face. The distance tells you how similar the faces are.
  13. :param faces: List of face encodings to compare
  14. :param face_to_compare: A face encoding to compare against
  15. :return: A numpy ndarray with the distance for each face in the same order as the 'faces' array
  16. """
  17. import numpy as np
  18. if len(face_encodings) == 0:
  19. return np.empty((0))
  20. #return 1/np.linalg.norm(face_encodings - face_to_compare, axis=1)
  21. return np.sum(face_encodings*face_to_compare,axis=1)
  22. def load_model(model_dir, meta_file, ckpt_file):
  23. model_dir_exp = os.path.expanduser(model_dir)
  24. saver = tf.train.import_meta_graph(os.path.join(model_dir_exp, meta_file))
  25. saver.restore(tf.get_default_session(), os.path.join(model_dir_exp, ckpt_file))
  26. def _chinese_whispers(encoding_list, threshold=0.55, iterations=20):
  27. """ Chinese Whispers Algorithm
  28. Modified from Alex Loveless' implementation,
  29. http://alexloveless.co.uk/data/chinese-whispers-graph-clustering-in-python/
  30. Inputs:
  31. encoding_list: a list of facial encodings from face_recognition
  32. threshold: facial match threshold,default 0.6
  33. iterations: since chinese whispers is an iterative algorithm, number of times to iterate
  34. Outputs:
  35. sorted_clusters: a list of clusters, a cluster being a list of imagepaths,
  36. sorted by largest cluster to smallest
  37. """
  38. #from face_recognition.api import _face_distance
  39. from random import shuffle
  40. import networkx as nx
  41. # Create graph
  42. nodes = []
  43. edges = []
  44. image_paths, encodings = zip(*encoding_list)
  45. if len(encodings) <= 1:
  46. print ("No enough encodings to cluster!")
  47. return []
  48. for idx, face_encoding_to_check in enumerate(encodings):
  49. # Adding node of facial encoding
  50. node_id = idx+1
  51. # Initialize 'cluster' to unique value (cluster of itself)
  52. node = (node_id, {'cluster': image_paths[idx], 'path': image_paths[idx]})
  53. nodes.append(node)
  54. # Facial encodings to compare
  55. if (idx+1) >= len(encodings):
  56. # Node is last element, don't create edge
  57. break
  58. compare_encodings = encodings[idx+1:]
  59. distances = face_distance(compare_encodings, face_encoding_to_check)
  60. encoding_edges = []
  61. for i, distance in enumerate(distances):
  62. if distance > threshold:
  63. # Add edge if facial match
  64. edge_id = idx+i+2
  65. encoding_edges.append((node_id, edge_id, {'weight': distance}))
  66. edges = edges + encoding_edges
  67. G = nx.Graph()
  68. G.add_nodes_from(nodes)
  69. G.add_edges_from(edges)
  70. # Iterate
  71. for _ in range(0, iterations):
  72. cluster_nodes = G.nodes()
  73. shuffle(cluster_nodes)
  74. for node in cluster_nodes:
  75. neighbors = G[node]
  76. clusters = {}
  77. for ne in neighbors:
  78. if isinstance(ne, int):
  79. if G.node[ne]['cluster'] in clusters:
  80. clusters[G.node[ne]['cluster']] += G[node][ne]['weight']
  81. else:
  82. clusters[G.node[ne]['cluster']] = G[node][ne]['weight']
  83. # find the class with the highest edge weight sum
  84. edge_weight_sum = 0
  85. max_cluster = 0
  86. #use the max sum of neighbor weights class as current node's class
  87. for cluster in clusters:
  88. if clusters[cluster] > edge_weight_sum:
  89. edge_weight_sum = clusters[cluster]
  90. max_cluster = cluster
  91. # set the class of target node to the winning local class
  92. G.node[node]['cluster'] = max_cluster
  93. clusters = {}
  94. # Prepare cluster output
  95. for (_, data) in G.node.items():
  96. cluster = data['cluster']
  97. path = data['path']
  98. if cluster:
  99. if cluster not in clusters:
  100. clusters[cluster] = []
  101. clusters[cluster].append(path)
  102. # Sort cluster output
  103. sorted_clusters = sorted(clusters.values(), key=len, reverse=True)
  104. return sorted_clusters
  105. def cluster_facial_encodings(facial_encodings):
  106. """ Cluster facial encodings
  107. Intended to be an optional switch for different clustering algorithms, as of right now
  108. only chinese whispers is available.
  109. Input:
  110. facial_encodings: (image_path, facial_encoding) dictionary of facial encodings
  111. Output:
  112. sorted_clusters: a list of clusters, a cluster being a list of imagepaths,
  113. sorted by largest cluster to smallest
  114. """
  115. if len(facial_encodings) <= 1:
  116. print ("Number of facial encodings must be greater than one, can't cluster")
  117. return []
  118. # Only use the chinese whispers algorithm for now
  119. sorted_clusters = _chinese_whispers(facial_encodings.items())
  120. return sorted_clusters
  121. def compute_facial_encodings(sess,images_placeholder,embeddings,phase_train_placeholder,image_size,
  122. embedding_size,nrof_images,nrof_batches,emb_array,batch_size,paths):
  123. """ Compute Facial Encodings
  124. Given a set of images, compute the facial encodings of each face detected in the images and
  125. return them. If no faces, or more than one face found, return nothing for that image.
  126. Inputs:
  127. image_paths: a list of image paths
  128. Outputs:
  129. facial_encodings: (image_path, facial_encoding) dictionary of facial encodings
  130. """
  131. for i in range(nrof_batches):
  132. start_index = i*batch_size
  133. end_index = min((i+1)*batch_size, nrof_images)
  134. paths_batch = paths[start_index:end_index]
  135. images = facenet.load_data(paths_batch, False, False, image_size)
  136. feed_dict = { images_placeholder:images, phase_train_placeholder:False }
  137. emb_array[start_index:end_index,:] = sess.run(embeddings, feed_dict=feed_dict)
  138. facial_encodings = {}
  139. for x in range(nrof_images):
  140. facial_encodings[paths[x]] = emb_array[x,:]
  141. return facial_encodings
  142. def get_onedir(paths):
  143. dataset = []
  144. path_exp = os.path.expanduser(paths)
  145. if os.path.isdir(path_exp):
  146. images = os.listdir(path_exp)
  147. image_paths = [os.path.join(path_exp,img) for img in images]
  148. for x in image_paths:
  149. if os.path.getsize(x)>0:
  150. dataset.append(x)
  151. return dataset
  152. def main(args):
  153. """ Main
  154. Given a list of images, save out facial encoding data files and copy
  155. images into folders of face clusters.
  156. """
  157. from os.path import join, basename, exists
  158. from os import makedirs
  159. import numpy as np
  160. import shutil
  161. import sys
  162. if not exists(args.output):
  163. makedirs(args.output)
  164. with tf.Graph().as_default():
  165. with tf.Session() as sess:
  166. image_paths = get_onedir(args.input)
  167. #image_list, label_list = facenet.get_image_paths_and_labels(train_set)
  168. meta_file, ckpt_file = facenet.get_model_filenames(os.path.expanduser(args.model_dir))
  169. print('Metagraph file: %s' % meta_file)
  170. print('Checkpoint file: %s' % ckpt_file)
  171. load_model(args.model_dir, meta_file, ckpt_file)
  172. # Get input and output tensors
  173. images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
  174. embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
  175. phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
  176. image_size = images_placeholder.get_shape()[1]
  177. print("image_size:",image_size)
  178. embedding_size = embeddings.get_shape()[1]
  179. # Run forward pass to calculate embeddings
  180. print('Runnning forward pass on images')
  181. nrof_images = len(image_paths)
  182. nrof_batches = int(math.ceil(1.0*nrof_images / args.batch_size))
  183. emb_array = np.zeros((nrof_images, embedding_size))
  184. facial_encodings = compute_facial_encodings(sess,images_placeholder,embeddings,phase_train_placeholder,image_size,
  185. embedding_size,nrof_images,nrof_batches,emb_array,args.batch_size,image_paths)
  186. sorted_clusters = cluster_facial_encodings(facial_encodings)
  187. num_cluster = len(sorted_clusters)
  188. # Copy image files to cluster folders
  189. for idx, cluster in enumerate(sorted_clusters):
  190. #save all the cluster
  191. cluster_dir = join(args.output, str(idx))
  192. if not exists(cluster_dir):
  193. makedirs(cluster_dir)
  194. for path in cluster:
  195. shutil.copy(path, join(cluster_dir, basename(path)))
  196. def parse_args():
  197. """Parse input arguments."""
  198. import argparse
  199. parser = argparse.ArgumentParser(description='Get a shape mesh (t-pose)')
  200. parser.add_argument('--model_dir', type=str, help='model dir', required=True)
  201. parser.add_argument('--batch_size', type=int, help='batch size', required=30)
  202. parser.add_argument('--input', type=str, help='Input dir of images', required=True)
  203. parser.add_argument('--output', type=str, help='Output dir of clusters', required=True)
  204. args = parser.parse_args()
  205. return args
  206. if __name__ == '__main__':
  207. """ Entry point """
  208. main(parse_args())