|
1 |
| -import cv2 |
2 |
| -import os |
3 |
| -import matplotlib.pyplot as plt |
4 |
| -import numpy as np |
5 |
| -import tensorflow as tf |
6 |
| -import time |
7 |
| -import h5py |
8 |
| -import sys |
9 |
| - |
10 |
| -IM_SHAPE = (64, 64, 3) |
11 |
| - |
12 |
| - |
13 |
| -class TrainingDatasetLoader(object): |
14 |
| - def __init__(self, data_path): |
15 |
| - |
16 |
| - print ("Opening {}".format(data_path)) |
17 |
| - sys.stdout.flush() |
18 |
| - |
19 |
| - self.cache = h5py.File(data_path, 'r') |
20 |
| - |
21 |
| - print ("Loading data into memory...") |
22 |
| - sys.stdout.flush() |
23 |
| - self.images = self.cache['images'][:] |
24 |
| - self.labels = self.cache['labels'][:] |
25 |
| - self.image_dims = self.images.shape |
26 |
| - n_train_samples = self.image_dims[0] |
27 |
| - |
28 |
| - self.train_inds = np.random.permutation(np.arange(n_train_samples)) |
29 |
| - |
30 |
| - self.pos_train_inds = self.train_inds[ self.labels[self.train_inds, 0] == 1.0 ] |
31 |
| - self.neg_train_inds = self.train_inds[ self.labels[self.train_inds, 0] != 1.0 ] |
32 |
| - |
33 |
| - def get_train_size(self): |
34 |
| - return self.train_inds.shape[0] |
35 |
| - |
36 |
| - def get_train_steps_per_epoch(self, batch_size, factor=10): |
37 |
| - return self.get_train_size()//factor//batch_size |
38 |
| - |
39 |
| - def get_batch(self, n, only_faces=False, p_pos=None, p_neg=None, return_inds=False): |
40 |
| - if only_faces: |
41 |
| - selected_inds = np.random.choice(self.pos_train_inds, size=n, replace=False, p=p_pos) |
42 |
| - else: |
43 |
| - selected_pos_inds = np.random.choice(self.pos_train_inds, size=n//2, replace=False, p=p_pos) |
44 |
| - selected_neg_inds = np.random.choice(self.neg_train_inds, size=n//2, replace=False, p=p_neg) |
45 |
| - selected_inds = np.concatenate((selected_pos_inds, selected_neg_inds)) |
46 |
| - |
47 |
| - sorted_inds = np.sort(selected_inds) |
48 |
| - train_img = self.images[sorted_inds,:,:,::-1]/255. |
49 |
| - train_label = self.labels[sorted_inds,...] |
50 |
| - return (train_img, train_label, sorted_inds) if return_inds else (train_img, train_label) |
51 |
| - |
52 |
| - def get_n_most_prob_faces(self, prob, n): |
53 |
| - idx = np.argsort(prob)[::-1] |
54 |
| - most_prob_inds = self.pos_train_inds[idx[:10*n:10]] |
55 |
| - return self.images[most_prob_inds,...]/255. |
56 |
| - |
57 |
| - def get_all_train_faces(self): |
58 |
| - return self.images[ self.pos_train_inds ] |
59 |
| - |
60 |
| - |
61 |
| - |
62 |
| -class PPBFaceEvaluator: |
63 |
| - ''' Evaluate on the PPB dataset''' |
64 |
| - def __init__(self, skip=4): |
65 |
| - |
66 |
| - path_to_faces = tf.keras.utils.get_file('ppb', 'https://www.dropbox.com/s/l0lp6qxeplumouf/PPB.tar?dl=1', extract=True) |
67 |
| - self.ppb_root = os.path.join(os.path.split(path_to_faces)[0], 'PPB-2017') |
68 |
| - |
69 |
| - ppb_anno = os.path.join(self.ppb_root,'PPB-2017-metadata.csv') |
70 |
| - |
71 |
| - self.anno_dict = {} |
72 |
| - with open(ppb_anno) as f: |
73 |
| - for line in f.read().split('\r'): |
74 |
| - ind, name, gender, numeric, skin, country = line.split(',') |
75 |
| - self.anno_dict[name] = (gender.lower(),skin.lower()) |
76 |
| - |
77 |
| - image_dir = os.path.join(self.ppb_root, "imgs") |
78 |
| - image_files = sorted(os.listdir(image_dir))[::skip] #sample every 4 images for computation time in the lab |
79 |
| - |
80 |
| - self.raw_images = { |
81 |
| - 'male_darker':[], |
82 |
| - 'male_lighter':[], |
83 |
| - 'female_darker':[], |
84 |
| - 'female_lighter':[], |
85 |
| - } |
86 |
| - |
87 |
| - for filename in image_files: |
88 |
| - if not filename.endswith(".jpg"): |
89 |
| - continue |
90 |
| - image = cv2.imread(os.path.join(image_dir,filename))[:,:,::-1] |
91 |
| - gender, skin = self.anno_dict[filename] |
92 |
| - self.raw_images[gender+'_'+skin].append(image) |
93 |
| - |
94 |
| - |
95 |
| - def get_sample_faces_from_demographic(self, gender, skin_color): |
96 |
| - key = self.__get_key(gender, skin_color) |
97 |
| - data = self.raw_images[key][50]/255. |
98 |
| - return data |
99 |
| - |
100 |
| - |
101 |
| - def evaluate(self, models_to_test, gender, skin_color, output_idx=None, from_logit=False, patch_stride=0.2, patch_depth=5): |
102 |
| - correct_predictions = [0.0]*len(models_to_test) |
103 |
| - |
104 |
| - key = self.__get_key(gender, skin_color) |
105 |
| - num_faces = len(self.raw_images[key]) |
106 |
| - |
107 |
| - import progressbar |
108 |
| - bar = progressbar.ProgressBar() |
109 |
| - for face_idx in bar(range(num_faces)): |
110 |
| - |
111 |
| - image = self.raw_images[key][face_idx] |
112 |
| - height, width, _ = image.shape |
113 |
| - |
114 |
| - patches, bboxes = slide_square(image, patch_stride, width/2, width, patch_depth) |
115 |
| - patches = tf.cast(tf.constant(patches, dtype=tf.uint8), tf.float32)/255. |
116 |
| - |
117 |
| - for model_idx, model in enumerate(models_to_test): |
118 |
| - out = model(patches) |
119 |
| - y = out if output_idx is None else out[output_idx] |
120 |
| - y = y.numpy() |
121 |
| - y_inds = np.argsort(y.flatten()) |
122 |
| - most_likely_prob = y[y_inds[-1]] |
123 |
| - if (from_logit and most_likely_prob >= 0.0) or \ |
124 |
| - (not from_logit and most_likely_prob >= 0.5): |
125 |
| - correct_predictions[model_idx] += 1 |
126 |
| - |
127 |
| - accuracy = [correct_predictions[i]/num_faces for i,_ in enumerate(models_to_test)] |
128 |
| - return accuracy |
129 |
| - |
130 |
| - |
131 |
| - def __get_key(self, gender, skin_color): |
132 |
| - gender = gender.lower() |
133 |
| - skin_color = skin_color.lower() |
134 |
| - assert gender in ['male', 'female'] |
135 |
| - assert skin_color in ['lighter', 'darker'] |
136 |
| - return '{}_{}'.format(gender, skin_color) |
137 |
| - |
138 |
| - |
139 |
| - |
140 |
| -''' function to slide a square across image and extract square regions |
141 |
| -img = the image |
142 |
| -stride = (0,1], provides the fraction of the dimension for which to slide to generate a crop |
143 |
| -max_size = maximum square size |
144 |
| -min_size = minimum square size |
145 |
| -n = number of different sizes including min_size, max_size ''' |
146 |
| -def slide_square(img, stride, min_size, max_size, n): |
147 |
| - img_h, img_w = img.shape[:2] |
148 |
| - |
149 |
| - square_sizes = np.linspace(min_size, max_size, n, dtype=np.int32) |
150 |
| - square_images = [] |
151 |
| - square_bbox = [] # list of list of tuples: [(i1,j1), (i2,j2)] where i1,j1 is the top left corner; i2,j2 is bottom right corner |
152 |
| - # for each of the square_sizes |
153 |
| - for level, sq_dim in enumerate(square_sizes): |
154 |
| - |
155 |
| - stride_length = int(stride*sq_dim) |
156 |
| - stride_start_i = xrange(0,int(img_h-sq_dim+1),stride_length) |
157 |
| - stride_start_j = xrange(0,int(img_w-sq_dim+1),stride_length) |
158 |
| - for i in stride_start_i: |
159 |
| - for j in stride_start_j: |
160 |
| - square_top_left = (i,j) |
161 |
| - |
162 |
| - square_bottom_right = (i+sq_dim,j+sq_dim) |
163 |
| - |
164 |
| - square_corners = (square_top_left,square_bottom_right) |
165 |
| - square_corners_show = ((j,i),(j+sq_dim,i+sq_dim)) |
166 |
| - square_image = img[i:i+sq_dim,j:j+sq_dim] |
167 |
| - |
168 |
| - square_resize = cv2.resize(square_image, IM_SHAPE[:2], interpolation=cv2.INTER_NEAREST) |
169 |
| - # append to list of images and bounding boxes |
170 |
| - |
171 |
| - square_images.append(square_resize) |
172 |
| - square_bbox.append(square_corners) |
173 |
| - |
174 |
| - return square_images, square_bbox |
0 commit comments