hjianganthony commited on
Commit
4c138bb
·
1 Parent(s): f20adff

First Trial

Browse files
models.py ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # keras vggface model
2
+ import tensorflow as tf
3
+ from keras.layers import Flatten, Dense, Input, Dropout, Activation, BatchNormalization
4
+
5
+ from keras_vggface.vggface import VGGFace
6
+ from keras.models import Model
7
+ # example of loading an image with the Keras API
8
+ # since 2021 tensorflow updated the package and moved model directory
9
+ from tensorflow.keras.preprocessing import image
10
+ import keras_vggface.utils as utils
11
+
12
+ # image manipulation
13
+ from matplotlib import pyplot as plt
14
+ import matplotlib.patches as patches
15
+ from PIL import Image, ImageDraw
16
+ import cv2
17
+
18
+ # face alignment
19
+ from mtcnn.mtcnn import MTCNN
20
+
21
+ # model metrics
22
+ from sklearn.metrics import roc_auc_score
23
+ from sklearn.metrics import mean_squared_error, mean_absolute_error
24
+ from scipy.stats import pearsonr
25
+
26
+ # common packages
27
+ import os
28
+ import numpy as np
29
+ import pandas as pd
30
+ import pickle
31
+
32
+ import shutil
33
+ from tqdm import tqdm
34
+ import tempfile
35
+ import hashlib
36
+
37
+ # Operations regarding to folder/file
38
+ def copy_images(file_paths, source_folder, destination_folder):
39
+ for file_path in file_paths:
40
+ file_name = os.path.basename(file_path)
41
+ source_file = os.path.join(source_folder, file_name)
42
+ destination_file = os.path.join(destination_folder, file_name)
43
+ shutil.copyfile(source_file, destination_file)
44
+
45
+ def get_file_names(folder_path):
46
+ file_names = []
47
+ for file_name in os.listdir(folder_path):
48
+ if os.path.isfile(os.path.join(folder_path, file_name)):
49
+ file_names.append(file_name)
50
+ return file_names
51
+
52
+ # Easy-to-use Performance metrics
53
+ def rmse(x,y):
54
+ return np.sqrt(mean_squared_error(x,y))
55
+
56
+ def mae(x,y):
57
+ return mean_absolute_error(x,y)
58
+
59
+ def auc(label, pred):
60
+ return roc_auc_score(label, pred)
61
+
62
+
63
+ # Previous codes for image2array processing; still adopted for single imgae prediction
64
+ def imgs_to_array(img_paths, version=1):
65
+ ''' extract features from all images and convert to multi-dimensional array
66
+ Takes:
67
+ img_path: str
68
+ version: int
69
+ Returns:
70
+ np.array
71
+ '''
72
+ imgs = []
73
+ for img_path in img_paths: # += is equivalent to extend @http://noahsnail.com/2020/06/17/2020-06-17-python%E4%B8%ADlist%E7%9A%84append,%20extend%E5%8C%BA%E5%88%AB/
74
+ imgs += [img_to_array(img_path, version)]
75
+ return np.concatenate(imgs)
76
+
77
+ def process_array(arr, version):
78
+ '''array processing (resize)
79
+ Takes: arr: np.array
80
+ Returns: np.array
81
+ '''
82
+ desired_size = (224, 224)
83
+ img = cv2.resize(arr, desired_size)
84
+ img = img * (1./255)
85
+ #img = np.expand_dims(img, axis=0)
86
+ img = utils.preprocess_input(img, version=version)
87
+ return img
88
+
89
+ def img_to_array(img_path, version):
90
+ '''conver a SINGLE image to array
91
+ Takes: img_path: str
92
+ Returns: np.array
93
+ '''
94
+ if not os.path.exists(img_path):
95
+ return None
96
+
97
+ img = image.load_img(img_path)
98
+ img = image.img_to_array(img)
99
+ img = process_array(img, version)
100
+ return img
101
+
102
+ def crop_img(img,x,y,w,h):
103
+ '''crop image
104
+ Takes: img: np.array
105
+ x,y,w,h: int
106
+ Returns: np.array
107
+ '''
108
+ return img[y:y+h,x:x+w,:]
109
+
110
+ def array_to_img(arr):
111
+ '''Converts a numpy array to an image.
112
+ Takes: arr: np.array
113
+ Returns: PIL.Image
114
+ '''
115
+ # Convert array to image
116
+ img = Image.fromarray(np.uint8(arr*255))
117
+ return img
118
+
119
+
120
+ # build a ImageDataGenerator
121
+ from tensorflow.keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
122
+
123
+ def single_test_generator(img_path, target_size=(224, 224), batch_size=1):
124
+ '''Generate a single test generator from an image file.
125
+ Takes:
126
+ - img_path: str, path to the image file
127
+ - target_size: tuple, target size for image resizing (default: (224, 224))
128
+ - batch_size: int, batch size for the generator (default: 32)
129
+ Returns:
130
+ - single_test_gen: ImageDataGenerator, generated image generator
131
+ '''
132
+ # Load the image
133
+ img = load_img(img_path, target_size=target_size)
134
+ # Convert the image to an array
135
+ img_array = img_to_array(img)
136
+ # Reshape the array to match the expected input shape of the model
137
+ img_array = img_array.reshape((1,) + img_array.shape)
138
+ # Create an instance of ImageDataGenerator
139
+ test_datagen = ImageDataGenerator(
140
+ rescale = 1./255,
141
+ shear_range = 0.2,
142
+ zoom_range = 0.2,
143
+ horizontal_flip = True)
144
+ # Generate the images
145
+ single_test_gen = test_datagen.flow(img_array, batch_size=batch_size)
146
+
147
+ return single_test_gen
148
+
149
+ # Create the ImageDataGenerator for the test_data
150
+ test_datagen = ImageDataGenerator(
151
+ #preprocessing_function=lambda x: (x - mean_pixel_value) / 255.0,
152
+ rescale = 1./255)
153
+
154
+ def img_data_generator(data, bs, img_dir, train_mode=True, version = 1): #replace function name later
155
+ """data input pipeline
156
+ Takes:
157
+ data: pd.DataFrame
158
+ bs: batch size
159
+ img_dir: str, directory to the images
160
+ train_mode: bool, if False, take samples from test set to aoivd overfitting
161
+ version: int, keras_vggface version
162
+ Returns:
163
+ features: tuple of (x,y): features and targets
164
+ """
165
+ loop = True
166
+
167
+ while loop:
168
+ if train_mode:
169
+ x = imgs_to_array(data['path'], version)
170
+ y = data['bmi'].values
171
+ features = (x,y)
172
+ else:
173
+ if len(data) >= bs:
174
+ sampled = data.iloc[:bs,:]
175
+ data = data.iloc[bs:,:]
176
+ features = imgs_to_array(sampled['index'], img_dir, version)
177
+ else:
178
+ loop = False
179
+ yield features
180
+
181
+
182
+
183
+ # Build a prediction class
184
+ class FacePrediction(object):
185
+
186
+ def __init__(self, img_dir=None, model_type='vgg16'):
187
+ self.model_type = model_type
188
+ self.img_dir = img_dir
189
+ self.detector = MTCNN()
190
+ if model_type in ['vgg16', 'vgg16_fc6']: # we might use other models, but in that case we need to just version input
191
+ self.version = 1
192
+ else:
193
+ self.version = 2
194
+
195
+ def define_model(self, hidden_dim = 64, drop_rate=0.0, freeze_backbone = True): # replace function name later
196
+ ''' initialize the vgg model
197
+ Reference:
198
+ @https://zhuanlan.zhihu.com/p/53116610
199
+ @https://zhuanlan.zhihu.com/p/26934085
200
+ '''
201
+ if self.model_type == 'vgg16_fc6':
202
+ vgg_model = VGGFace(model = 'vgg16', include_top=True, input_shape=(224, 224, 3))
203
+ last_layer = vgg_model.get_layer('fc6').output
204
+ flatten = Activation('relu')(last_layer)
205
+ else:
206
+ vgg_model = VGGFace(model = self.model_type, include_top=False, input_shape=(224, 224, 3))
207
+ last_layer = vgg_model.output
208
+ flatten = Flatten()(last_layer)
209
+
210
+ if freeze_backbone: # free the vgg layers to fine-tune
211
+ for layer in vgg_model.layers:
212
+ layer.trainable = False
213
+
214
+ def model_init(flatten, name):
215
+ # x = Dense(64, name=name + '_fc1')(flatten)
216
+ # x = BatchNormalization(name = name + '_bn1')(x)
217
+ # x = Activation('relu', name = name+'_act1')(x)
218
+ # x = Dropout(0.2)(x)
219
+ # x = Dense(64, name=name + '_fc2')(x)
220
+ # x = BatchNormalization(name = name + '_bn2')(x)
221
+ # x = Activation('relu', name = name+'_act2')(x)
222
+ # x = Dropout(drop_rate)(x)
223
+ x = flatten
224
+ return x
225
+
226
+ x = model_init(flatten, name = 'bmi')
227
+ bmi_pred = Dense(1, activation='linear', name='bmi')(x) #{'relu': , 'linear': terrible}
228
+
229
+ custom_vgg_model = Model(vgg_model.input, bmi_pred)
230
+ custom_vgg_model.compile('adam',
231
+ {'bmi':'mae'}, #{'bmi':'mae'},
232
+ loss_weights={'bmi': 1})
233
+
234
+ self.model = custom_vgg_model
235
+
236
+ def train(self, train_gen, val_gen, train_step, val_step, bs, epochs, callbacks):
237
+ ''' train the model
238
+ Takes:
239
+ train_data: dataframe
240
+ val_data: dataframe
241
+ bs: int, batch size
242
+ epochs: int, number of epochs
243
+ callbacks: list, callbacks
244
+ Recall the input for img_data_generator: data, bs, img_dir, train_mode=True, version = 1
245
+ '''
246
+ self.model.fit_generator(train_gen, train_step, epochs=epochs,
247
+ validation_data=val_gen, validation_steps=val_step,
248
+ callbacks=callbacks)
249
+
250
+
251
+ def evaluate_perf(self, val_data):
252
+ img_paths = val_data['path'].values
253
+ arr = imgs_to_array(img_paths, self.version)
254
+ bmi = self.model.predict(arr)
255
+ metrics = {'bmi_mae':mae(bmi[:,0], val_data.bmi.values)}
256
+ return metrics
257
+
258
+ def detect_faces(self, img_path, confidence):
259
+ img = image.load_img(img_path)
260
+ img = image.img_to_array(img)
261
+ box = self.detector.detect_faces(img)
262
+ box = [i for i in box if i['confidence'] > confidence]
263
+ res = [crop_img(img, *i['box']) for i in box]
264
+ res = [process_array(i, self.version) for i in res]
265
+ return box, res
266
+
267
+ def crop_image_around_face(self, img, box, crop_percentage):
268
+ x, y, width, height = box['box']
269
+ center_x = x + (width // 2)
270
+ center_y = y + (height // 2)
271
+ crop_width = int(width * crop_percentage)
272
+ crop_height = int(height * crop_percentage)
273
+ crop_left = max(0, center_x - (crop_width // 2))
274
+ crop_top = max(0, center_y - (crop_height // 2))
275
+ crop_right = min(img.width, crop_left + crop_width)
276
+ crop_bottom = min(img.height, crop_top + crop_height)
277
+ cropped_img = img.crop((crop_left, crop_top, crop_right, crop_bottom))
278
+ return cropped_img
279
+
280
+ def process_input_image(self, img_input_path):
281
+ img = Image.open(img_input_path)
282
+
283
+ # Check image size
284
+ if img.size == (244, 244):
285
+ return img_input_path
286
+ else:
287
+ # Detect faces and crop
288
+ confidence_threshold = 0.5
289
+ boxes, cropped_images = self.detect_faces(img_input_path, confidence_threshold)
290
+
291
+ if len(cropped_images) > 0:
292
+ # Save the cropped image in a temporary folder
293
+ tmp_folder = 'tmp'
294
+ os.makedirs(tmp_folder, exist_ok=True)
295
+
296
+ # Generate hash value from the image input path
297
+ hash_value = hashlib.sha1(img_input_path.encode()).hexdigest()
298
+
299
+ tmp_img_path = os.path.join(tmp_folder, hash_value + 'temp_image.bmp')
300
+
301
+ # Print confidence for each detected face
302
+ for i, box in enumerate(boxes):
303
+ confidence = box['confidence']
304
+ print(f"Face {i + 1}: Confidence - {confidence}")
305
+
306
+ # Crop the image around the detected face
307
+ cropped_img = self.crop_image_around_face(img, box, crop_percentage=1.25)
308
+
309
+ # Save the cropped image
310
+ cropped_img.save(tmp_img_path)
311
+
312
+ return tmp_img_path
313
+ else:
314
+ # No faces detected, return the original image
315
+ return img_input_path
316
+
317
+ def predict_external(self, img_input_dir, input_df=None, image_width=244, image_height=244, batch_size=32, show_img=False):
318
+ if os.path.isdir(img_input_dir) and input_df is not None:
319
+ # Predict using the data generator
320
+ test_df = input_df
321
+ processed_img_paths = [self.process_input_image(i) for i in test_df['path']]
322
+ processed_img_names = [i.split('/')[-1] for i in processed_img_paths]
323
+ processed_img_dir = '/'.join(processed_img_paths[0].split('/')[:-1])
324
+ test_df['processed_paths'], test_df['processed_names'] = processed_img_paths, processed_img_names
325
+
326
+
327
+ # Load the test data with target data
328
+ test_set_gen = test_datagen.flow_from_dataframe(
329
+ test_df,
330
+ directory = img_input_dir,
331
+ x_col='name',
332
+ y_col='bmi',
333
+ target_size=(image_width, image_height),
334
+ batch_size=batch_size,
335
+ color_mode='rgb',
336
+ class_mode='raw')
337
+
338
+ preds = self.model.predict_generator(test_set_gen)
339
+
340
+ if show_img and (test_df is not None):
341
+ bmi = preds
342
+ num_plots = len(test_df['path'])
343
+ ncols = 5
344
+ nrows = int((num_plots - 0.1) // ncols + 1)
345
+ fig, axs = plt.subplots(nrows, ncols)
346
+ fig.set_size_inches(3 * ncols, 3 * nrows)
347
+ for i, img_path in enumerate(test_df['path']):
348
+ col = i % ncols
349
+ row = i // ncols
350
+ img = plt.imread(img_path)
351
+ axs[row, col].imshow(img)
352
+ axs[row, col].axis('off')
353
+ axs[row, col].set_title('BMI: {:3.1f}'.format(bmi[i, 0], fontsize=10))
354
+ return preds
355
+
356
+ else:
357
+ # Single image input
358
+ single_test_path = self.process_input_image(img_input_dir)
359
+ single_test_gen = single_test_generator(single_test_path)
360
+
361
+ if show_img:
362
+ img_path = img_input_dir
363
+ img = plt.imread(single_test_path)
364
+ fig, ax = plt.subplots()
365
+ ax.imshow(img)
366
+ ax.axis('off')
367
+ preds = self.model.predict_generator(single_test_gen)
368
+ ax.set_title('BMI: {:3.1f}'.format(preds[0, 0], fontsize=10))
369
+ plt.show()
370
+
371
+ preds = self.model.predict_generator(single_test_gen)
372
+ return preds
373
+
374
+
375
+
376
+ def predict(self, img_input_dir, input_generator=None, input_df=None, show_img=False):
377
+ if os.path.isdir(img_input_dir) and input_generator is not None:
378
+ # Predict using the data generator
379
+ preds = self.model.predict_generator(input_generator)
380
+
381
+ if show_img and (input_df is not None):
382
+ bmi = preds
383
+ num_plots = len(input_df['path'])
384
+ ncols = 5
385
+ nrows = int((num_plots - 0.1) // ncols + 1)
386
+ fig, axs = plt.subplots(nrows, ncols)
387
+ fig.set_size_inches(3 * ncols, 3 * nrows)
388
+ for i, img_path in enumerate(input_df['path']):
389
+ col = i % ncols
390
+ row = i // ncols
391
+ img = plt.imread(img_path)
392
+ axs[row, col].imshow(img)
393
+ axs[row, col].axis('off')
394
+ axs[row, col].set_title('BMI: {:3.1f}'.format(bmi[i, 0], fontsize=10))
395
+ return preds
396
+
397
+ else:
398
+ single_test_gen = single_test_generator(img_input_dir)
399
+
400
+ if show_img:
401
+ img_path = img_input_dir
402
+ img = plt.imread(img_path)
403
+ fig, ax = plt.subplots()
404
+ ax.imshow(img)
405
+ ax.axis('off')
406
+ #preds = self.model.predict(img_to_array(img_path, self.version))
407
+ preds = self.model.predict_generator(single_test_gen)
408
+ ax.set_title('BMI: {:3.1f}'.format(preds[0, 0], fontsize=10))
409
+ plt.show()
410
+
411
+ preds = self.model.predict_generator(single_test_gen)
412
+ #preds = self.model.predict(img_to_array(img_path, self.version))
413
+ return preds
414
+
415
+
416
+ def predict_df(self, img_dir):
417
+ assert os.path.isdir(img_dir), 'input must be directory'
418
+ fnames = os.listdir(img_dir)
419
+ bmi = self.predict(img_dir)
420
+ results = pd.DataFrame({'img':fnames, 'bmi':bmi[:,0]})
421
+ return results
422
+
423
+ def save_weights(self, model_dir):
424
+ self.model.save_weights(model_dir)
425
+
426
+ def load_weights(self, model_dir):
427
+ self.model.load_weights(model_dir)
428
+
429
+ def load_model(self, model_dir):
430
+ self.model.load_model(model_dir)
431
+
432
+ def predict_faces(self, img_path, show_img = True, color = "white", fontsize = 12,
433
+ confidence = 0.95, fig_size = (16,12)):
434
+
435
+ assert os.path.isfile(img_path), 'only single image is supported'
436
+ img = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
437
+ boxes, faces = self.detect_faces(img_path, confidence)
438
+ preds = [self.model.predict(face) for face in faces]
439
+
440
+ if show_img:
441
+ # Create figure and axes
442
+ num_box = len(boxes)
443
+ fig,ax = plt.subplots()
444
+ fig.set_size_inches(fig_size)
445
+ # Display the image
446
+ ax.imshow(img)
447
+ ax.axis('off')
448
+ # Create a Rectangle patch
449
+ for idx, box in enumerate(boxes):
450
+ bmi = preds[idx]
451
+ box_x, box_y, box_w, box_h = box['box']
452
+ rect = patches.Rectangle((box_x, box_y), box_w, box_h, linewidth=1,edgecolor='yellow',facecolor='none')
453
+ ax.add_patch(rect)
454
+ ax.text(box_x, box_y,
455
+ 'BMI:{:3.1f}'.format(bmi[0,0]),
456
+ color = color, fontsize = fontsize)
457
+ plt.show()
458
+
459
+ return preds
models/0508_saved_model_small_sample_vgg16_base.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fe9b29ebadad26dc628dc191d81dbd91669094dcac4d196e4c91a8cdcc5c7132
3
+ size 59239768