daliprf commited on
Commit
3dbf98e
1 Parent(s): f7f2fc9
.gitattributes CHANGED
@@ -29,3 +29,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
29
  *.zip filter=lfs diff=lfs merge=lfs -text
30
  *.zstandard filter=lfs diff=lfs merge=lfs -text
31
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
29
  *.zip filter=lfs diff=lfs merge=lfs -text
30
  *.zstandard filter=lfs diff=lfs merge=lfs -text
31
  *tfevents* filter=lfs diff=lfs merge=lfs -text
32
+ *.pdf filter=lfs diff=lfs merge=lfs -text
33
+ *.png filter=lfs diff=lfs merge=lfs -text
34
+ *.jpg filter=lfs diff=lfs merge=lfs -text
Data_custom_generator.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from skimage.io import imread
2
+ from skimage.transform import resize
3
+ import numpy as np
4
+ import tensorflow as tf
5
+ import keras
6
+ from skimage.transform import resize
7
+ from tf_record_utility import TFRecordUtility
8
+ from configuration import DatasetName, DatasetType, \
9
+ AffectnetConf, D300wConf, W300Conf, InputDataSize, LearningConfig
10
+ from numpy import save, load, asarray
11
+
12
+
13
+ class CustomHeatmapGenerator(keras.utils.Sequence):
14
+
15
+ def __init__(self, is_single, image_filenames, label_filenames, batch_size, n_outputs, accuracy=100):
16
+ self.image_filenames = image_filenames
17
+ self.label_filenames = label_filenames
18
+ self.batch_size = batch_size
19
+ self.n_outputs = n_outputs
20
+ self.is_single = is_single
21
+ self.accuracy = accuracy
22
+
23
+ def __len__(self):
24
+ _len = np.ceil(len(self.image_filenames) // float(self.batch_size))
25
+ return int(_len)
26
+
27
+ def __getitem__(self, idx):
28
+ img_path = D300wConf.train_images_dir
29
+ tr_path_85 = D300wConf.train_hm_dir_85
30
+ tr_path_90 = D300wConf.train_hm_dir_90
31
+ tr_path_97 = D300wConf.train_hm_dir_97
32
+ tr_path = D300wConf.train_hm_dir
33
+
34
+ batch_x = self.image_filenames[idx * self.batch_size:(idx + 1) * self.batch_size]
35
+ batch_y = self.label_filenames[idx * self.batch_size:(idx + 1) * self.batch_size]
36
+
37
+ img_batch = np.array([imread(img_path + file_name) for file_name in batch_x])
38
+
39
+ if self.is_single:
40
+ if self.accuracy == 85:
41
+ lbl_batch = np.array([load(tr_path_85 + file_name) for file_name in batch_y])
42
+ elif self.accuracy == 90:
43
+ lbl_batch = np.array([load(tr_path_90 + file_name) for file_name in batch_y])
44
+ elif self.accuracy == 97:
45
+ lbl_batch = np.array([load(tr_path_97 + file_name) for file_name in batch_y])
46
+ else:
47
+ lbl_batch = np.array([load(tr_path + file_name) for file_name in batch_y])
48
+
49
+ lbl_out_array = lbl_batch
50
+ else:
51
+ lbl_batch_85 = np.array([load(tr_path_85 + file_name) for file_name in batch_y])
52
+ lbl_batch_90 = np.array([load(tr_path_90 + file_name) for file_name in batch_y])
53
+ lbl_batch_97 = np.array([load(tr_path_97 + file_name) for file_name in batch_y])
54
+ lbl_out_array = [lbl_batch_85, lbl_batch_90, lbl_batch_97]
55
+
56
+ return img_batch, lbl_out_array
README.md CHANGED
@@ -1,3 +1,150 @@
1
- ---
2
- license: mit
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/facial-landmark-points-detection-using/face-alignment-on-cofw)](https://paperswithcode.com/sota/face-alignment-on-cofw?p=facial-landmark-points-detection-using)
3
+
4
+ #
5
+ Facial Landmark Points Detection Using Knowledge Distillation-Based Neural Networks
6
+
7
+
8
+
9
+ #### Link to the paper:
10
+ Google Scholar:
11
+ https://scholar.google.com/citations?view_op=view_citation&hl=en&user=96lS6HIAAAAJ&citation_for_view=96lS6HIAAAAJ:zYLM7Y9cAGgC
12
+
13
+ Elsevier:
14
+ https://www.sciencedirect.com/science/article/pii/S1077314221001582
15
+
16
+ Arxiv:
17
+ https://arxiv.org/abs/2111.07047
18
+
19
+ #### Link to the paperswithcode.com:
20
+ https://paperswithcode.com/paper/facial-landmark-points-detection-using
21
+
22
+ ```diff
23
+ @@plaese STAR the repo if you like it.@@
24
+ ```
25
+
26
+ ```
27
+ Please cite this work as:
28
+
29
+ @article{fard2022facial,
30
+ title={Facial landmark points detection using knowledge distillation-based neural networks},
31
+ author={Fard, Ali Pourramezan and Mahoor, Mohammad H},
32
+ journal={Computer Vision and Image Understanding},
33
+ volume={215},
34
+ pages={103316},
35
+ year={2022},
36
+ publisher={Elsevier}
37
+ }
38
+
39
+ ```
40
+
41
+ ## Introduction
42
+ Facial landmark detection is a vital step for numerous facial image analysis applications. Although some deep learning-based methods have achieved good performances in this task, they are often not suitable for running on mobile devices. Such methods rely on networks with many parameters, which makes the training and inference time-consuming. Training lightweight neural networks such as MobileNets are often challenging, and the models might have low accuracy. Inspired by knowledge distillation (KD), this paper presents a novel loss function to train a lightweight Student network (e.g., MobileNetV2) for facial landmark detection. We use two Teacher networks, a Tolerant-Teacher and a Tough-Teacher in conjunction with the Student network. The Tolerant-Teacher is trained using Soft-landmarks created by active shape models, while the Tough-Teacher is trained using the ground truth (aka Hard-landmarks) landmark points. To utilize the facial landmark points predicted by the Teacher networks, we define an Assistive Loss (ALoss) for each Teacher network. Moreover, we define a loss function called KD-Loss that utilizes the facial landmark points predicted by the two pre-trained Teacher networks (EfficientNet-b3) to guide the lightweight Student network towards predicting the Hard-landmarks. Our experimental results on three challenging facial datasets show that the proposed architecture will result in a better-trained Student network that can extract facial landmark points with high accuracy.
43
+
44
+
45
+ ##Architecture
46
+
47
+ We train the Tough-Teacher, and the Tolerant-Teacher networks independently using the Hard-landmarks and the Soft-landmarks respectively utilizing the L2 loss:
48
+
49
+ ![teacher_arch](https://github.com/aliprf/KD-Loss/blob/master/samples/teacher_arch-1.jpg?raw=true)
50
+
51
+
52
+ Proposed KD-based architecture for training the Student network. KDLoss uses the knowledge of the previously trained Teacher networks by utilizing the assistive loss functions ALossT ou and ALossT ol, to improve the performance the face alignment task:
53
+
54
+ ![general_framework](https://github.com/aliprf/KD-Loss/blob/master/samples/general_framework-1.jpg?raw=true)
55
+
56
+
57
+ ## Evaluation
58
+
59
+ Following are some samples in order to show the visual performance of KD-Loss on 300W, COFW and WFLW datasets:
60
+
61
+ 300W:
62
+ ![KD_300W_samples](https://github.com/aliprf/KD-Loss/blob/master/samples/KD_300W_samples-1.jpg?raw=true)
63
+
64
+ COFW:
65
+ ![KD_cofw_samples](https://github.com/aliprf/KD-Loss/blob/master/samples/KD_cofw_samples-1.jpg?raw=true)
66
+
67
+ WFLW:
68
+ ![KD_WFLW_samples](https://github.com/aliprf/KD-Loss/blob/master/samples/KD_WFLW_samples-1.jpg?raw=true)
69
+
70
+ ----------------------------------------------------------------------------------------------------------------------------------
71
+ ## Installing the requirements
72
+ In order to run the code you need to install python >= 3.5.
73
+ The requirements and the libraries needed to run the code can be installed using the following command:
74
+
75
+ ```
76
+ pip install -r requirements.txt
77
+ ```
78
+
79
+
80
+ ## Using the pre-trained models
81
+ You can test and use the preetrained models using the following codes which are available in the test.py:
82
+ The pretrained student model are also located in "models/students".
83
+
84
+ ```
85
+ cnn = CNNModel()
86
+ model = cnn.get_model(arch=arch, input_tensor=None, output_len=self.output_len)
87
+
88
+ model.load_weights(weight_fname)
89
+
90
+ img = None # load a cropped image
91
+
92
+ image_utility = ImageUtility()
93
+ pose_predicted = []
94
+ image = np.expand_dims(img, axis=0)
95
+
96
+ pose_predicted = model.predict(image)[1][0]
97
+ ```
98
+
99
+
100
+ ## Training Network from scratch
101
+
102
+
103
+ ### Preparing Data
104
+ Data needs to be normalized and saved in npy format.
105
+
106
+ ### Training
107
+
108
+ ### Training Teacher Networks:
109
+
110
+ The training implementation is located in teacher_trainer.py class. You can use the following code to start the training for the teacher networks:
111
+
112
+ ```
113
+ '''train Teacher Networks'''
114
+ trainer = TeacherTrainer(dataset_name=DatasetName.w300)
115
+ trainer.train(arch='efficientNet',weight_path=None)
116
+ ```
117
+
118
+ ### Training Student Networks:
119
+ After Training the teacher networks, you can use the trained teachers to train the student network. The implemetation of training of the student network is provided in teacher_trainer.py . You can use the following code to start the training for the student networks:
120
+
121
+ ```
122
+ st_trainer = StudentTrainer(dataset_name=DatasetName.w300, use_augmneted=True)
123
+ st_trainer.train(arch_student='mobileNetV2', weight_path_student=None,
124
+ loss_weight_student=2.0,
125
+ arch_tough_teacher='efficientNet', weight_path_tough_teacher='./models/teachers/ds_300w_ef_tou.h5',
126
+ loss_weight_tough_teacher=1,
127
+ arch_tol_teacher='efficientNet', weight_path_tol_teacher='./models/teachers/ds_300w_ef_tol.h5',
128
+ loss_weight_tol_teacher=1)
129
+ ```
130
+
131
+
132
+
133
+ ```
134
+ Please cite this work as:
135
+
136
+ @article{fard2022facial,
137
+ title={Facial landmark points detection using knowledge distillation-based neural networks},
138
+ author={Fard, Ali Pourramezan and Mahoor, Mohammad H},
139
+ journal={Computer Vision and Image Understanding},
140
+ volume={215},
141
+ pages={103316},
142
+ year={2022},
143
+ publisher={Elsevier}
144
+ }
145
+
146
+ ```
147
+
148
+ ```diff
149
+ @@plaese STAR the repo if you like it.@@
150
+ ```
cnn_model.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from configuration import DatasetName, DatasetType, \
2
+ D300wConf, InputDataSize, LearningConfig
3
+
4
+ import tensorflow as tf
5
+ # tf.compat.v1.disable_eager_execution()
6
+
7
+ from tensorflow import keras
8
+ from skimage.transform import resize
9
+ from keras.regularizers import l2
10
+
11
+ # tf.logging.set_verbosity(tf.logging.ERROR)
12
+ from tensorflow.keras.models import Model
13
+ from tensorflow.keras.applications import mobilenet_v2, mobilenet, resnet50, densenet
14
+
15
+ from tensorflow.keras.layers import Dense, MaxPooling2D, Conv2D, Flatten, \
16
+ BatchNormalization, Activation, GlobalAveragePooling2D, DepthwiseConv2D, Dropout, ReLU, Concatenate, Input, Conv2DTranspose
17
+
18
+ from keras.callbacks import ModelCheckpoint
19
+ from keras import backend as K
20
+
21
+ from keras.optimizers import Adam
22
+ import numpy as np
23
+ import matplotlib.pyplot as plt
24
+ import math
25
+ from keras.callbacks import CSVLogger
26
+ from datetime import datetime
27
+
28
+ import cv2
29
+ import os.path
30
+ from keras.utils.vis_utils import plot_model
31
+ from scipy.spatial import distance
32
+ import scipy.io as sio
33
+
34
+ import efficientnet.tfkeras as efn
35
+
36
+
37
+ class CNNModel:
38
+ def get_model(self, arch, input_tensor, output_len,
39
+ inp_shape=[InputDataSize.image_input_size, InputDataSize.image_input_size, 3],
40
+ weight_path=None):
41
+ if arch == 'efficientNet':
42
+ model = self.create_efficientNet(inp_shape=inp_shape, input_tensor=input_tensor, output_len=output_len)
43
+ elif arch == 'mobileNetV2':
44
+ model = self.create_MobileNet(inp_shape=inp_shape, inp_tensor=input_tensor)
45
+ return model
46
+
47
+ def create_MobileNet(self, inp_shape, inp_tensor):
48
+
49
+ mobilenet_model = mobilenet_v2.MobileNetV2(input_shape=inp_shape,
50
+ alpha=1.0,
51
+ include_top=True,
52
+ weights=None,
53
+ input_tensor=inp_tensor,
54
+ pooling=None)
55
+
56
+ inp = mobilenet_model.input
57
+ out_landmarks = mobilenet_model.get_layer('O_L').output
58
+ revised_model = Model(inp, [out_landmarks])
59
+ model_json = revised_model.to_json()
60
+ with open("mobileNet_v2_stu.json", "w") as json_file:
61
+ json_file.write(model_json)
62
+ return revised_model
63
+
64
+ def create_efficientNet(self, inp_shape, input_tensor, output_len, is_teacher=True):
65
+ if is_teacher: # for teacher we use a heavier network
66
+ eff_net = efn.EfficientNetB3(include_top=True,
67
+ weights=None,
68
+ input_tensor=None,
69
+ input_shape=[InputDataSize.image_input_size, InputDataSize.image_input_size,
70
+ 3],
71
+ pooling=None,
72
+ classes=output_len)
73
+ else: # for student we use the small network
74
+ eff_net = efn.EfficientNetB0(include_top=True,
75
+ weights=None,
76
+ input_tensor=None,
77
+ input_shape=inp_shape,
78
+ pooling=None,
79
+ classes=output_len) # or weights='noisy-student'
80
+
81
+ eff_net.layers.pop()
82
+ inp = eff_net.input
83
+
84
+ x = eff_net.get_layer('top_activation').output
85
+ x = GlobalAveragePooling2D()(x)
86
+ x = keras.layers.Dropout(rate=0.5)(x)
87
+ output = Dense(output_len, activation='linear', name='out')(x)
88
+
89
+ eff_net = Model(inp, output)
90
+
91
+ eff_net.summary()
92
+
93
+ return eff_net
94
+
configuration.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class DatasetName:
2
+ w300 = 'w300'
3
+ cofw = 'cofw'
4
+ wflw = 'wflw'
5
+
6
+ class DatasetType:
7
+ data_type_train = 0
8
+ data_type_validation = 1
9
+ data_type_test = 2
10
+
11
+ class LearningConfig:
12
+ batch_size = 70
13
+ epochs = 150
14
+
15
+ class InputDataSize:
16
+ image_input_size = 224
17
+
18
+ class WflwConf:
19
+ Wflw_prefix_path = './data/wflw/' # --> local
20
+
21
+ ''' augmented version'''
22
+ augmented_train_pose = Wflw_prefix_path + 'training_set/augmented/pose/'
23
+ augmented_train_annotation = Wflw_prefix_path + 'training_set/augmented/annotations/'
24
+ augmented_train_atr = Wflw_prefix_path + 'training_set/augmented/atrs/'
25
+ augmented_train_image = Wflw_prefix_path + 'training_set/augmented/images/'
26
+ augmented_train_tf_path = Wflw_prefix_path + 'training_set/augmented/tf/'
27
+ ''' original version'''
28
+ no_aug_train_annotation = Wflw_prefix_path + 'training_set/no_aug/annotations/'
29
+ no_aug_train_atr = Wflw_prefix_path + 'training_set/no_aug/atrs/'
30
+ no_aug_train_pose = Wflw_prefix_path + 'training_set/no_aug/pose/'
31
+ no_aug_train_image = Wflw_prefix_path + 'training_set/no_aug/images/'
32
+ no_aug_train_tf_path = Wflw_prefix_path + 'training_set/no_aug/tf/'
33
+
34
+ orig_number_of_training = 7500
35
+ orig_number_of_test = 2500
36
+
37
+ augmentation_factor = 4 # create . image from 1
38
+ augmentation_factor_rotate = 15 # create . image from 1
39
+ num_of_landmarks = 98
40
+
41
+ class CofwConf:
42
+ Cofw_prefix_path = './data/cofw/' # --> local
43
+
44
+ augmented_train_pose = Cofw_prefix_path + 'training_set/augmented/pose/'
45
+ augmented_train_annotation = Cofw_prefix_path + 'training_set/augmented/annotations/'
46
+ augmented_train_image = Cofw_prefix_path + 'training_set/augmented/images/'
47
+ augmented_train_tf_path = Cofw_prefix_path + 'training_set/augmented/tf/'
48
+
49
+ orig_number_of_training = 1345
50
+ orig_number_of_test = 507
51
+
52
+ augmentation_factor = 10
53
+ num_of_landmarks = 29
54
+
55
+
56
+ class D300wConf:
57
+ w300w_prefix_path = './data/300W/' # --> local
58
+
59
+ orig_300W_train = w300w_prefix_path + 'orig_300W_train/'
60
+ augmented_train_pose = w300w_prefix_path + 'training_set/augmented/pose/'
61
+ augmented_train_annotation = w300w_prefix_path + 'training_set/augmented/annotations/'
62
+ augmented_train_image = w300w_prefix_path + 'training_set/augmented/images/'
63
+ augmented_train_tf_path = w300w_prefix_path + 'training_set/augmented/tf/'
64
+
65
+ no_aug_train_annotation = w300w_prefix_path + 'training_set/no_aug/annotations/'
66
+ no_aug_train_pose = w300w_prefix_path + 'training_set/no_aug/pose/'
67
+ no_aug_train_image = w300w_prefix_path + 'training_set/no_aug/images/'
68
+ no_aug_train_tf_path = w300w_prefix_path + 'training_set/no_aug/tf/'
69
+
70
+ orig_number_of_training = 3148
71
+ orig_number_of_test_full = 689
72
+ orig_number_of_test_common = 554
73
+ orig_number_of_test_challenging = 135
74
+
75
+ augmentation_factor = 4 # create . image from 1
76
+ num_of_landmarks = 68
77
+
custom_Losses.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+
3
+ import numpy as np
4
+ import tensorflow as tf
5
+
6
+ # tf.compat.v1.disable_eager_execution()
7
+ # tf.compat.v1.enable_eager_execution()
8
+
9
+ from PIL import Image
10
+ from tensorflow.keras import backend as K
11
+ from scipy.spatial import distance
12
+
13
+ from cnn_model import CNNModel
14
+ from configuration import DatasetName, D300wConf, LearningConfig
15
+ from image_utility import ImageUtility
16
+ from pca_utility import PCAUtility
17
+
18
+ print(tf.__version__)
19
+
20
+
21
+ class Custom_losses:
22
+
23
+ def MSE(self, x_pr, x_gt):
24
+ return tf.losses.mean_squared_error(x_pr, x_gt)
25
+
26
+
27
+ def kd_loss(self, x_pr, x_gt, x_tough, x_tol, alpha_tough, alpha_mi_tough, alpha_tol, alpha_mi_tol,
28
+ main_loss_weight, tough_loss_weight, tol_loss_weight):
29
+ """km"""
30
+ '''los KD'''
31
+ # we revise teachers for reflection:
32
+ x_tough = x_gt + tf.sign(x_pr - x_gt) * tf.abs(x_tough - x_gt)
33
+ b_tough = x_gt + tf.sign(x_pr - x_gt) * tf.abs(x_tough - x_gt) * 0.15
34
+ x_tol = x_gt + tf.sign(x_pr - x_gt) * tf.abs(x_tol - x_gt)
35
+ b_tol = x_gt + tf.sign(x_pr - x_gt) * tf.abs(x_tol - x_gt) * 0.15
36
+ # Region A: from T -> +inf
37
+ tou_pos_map = tf.where(tf.sign(x_pr - x_tough) * tf.sign(x_tough - x_gt) > 0, alpha_tough, 0.0)
38
+ tou_neg_map = tf.where(tf.sign(x_tough - x_pr) * tf.sign(x_pr - b_tough) >= 0, alpha_mi_tough, 0.0)
39
+ # tou_red_map = tf.where(tf.sign(tf.abs(b_tough) - tf.abs(x_pr))*tf.sign(tf.abs(x_pr) - tf.abs(x_gt)) > 0, 0.1, 0.0)
40
+ tou_map = tou_pos_map + tou_neg_map # + tou_red_map
41
+
42
+ tol_pos_map = tf.where(tf.sign(x_pr - x_tol) * tf.sign(x_tol - x_gt) > 0, alpha_tol, 0.0)
43
+ tol_neg_map = tf.where(tf.sign(x_tol - x_pr) * tf.sign(x_pr - b_tol) >= 0, alpha_mi_tol, 0.0)
44
+ # tol_red_map = tf.where(tf.sign(tf.abs(b_tol) - tf.abs(x_pr))*tf.sign(tf.abs(x_pr) - tf.abs(x_gt)) > 0, 0.1, 0.0)
45
+ tol_map = tol_pos_map + tol_neg_map # + tou_red_map
46
+
47
+ '''calculate dif map for linear and non-linear part'''
48
+ low_diff_main_map = tf.where(tf.abs(x_gt - x_pr) <= tf.abs(x_gt - x_tol), 1.0, 0.0)
49
+ high_diff_main_map = tf.where(tf.abs(x_gt - x_pr) > tf.abs(x_gt - x_tol), 1.0, 0.0)
50
+
51
+ '''calculate loss'''
52
+ loss_main_high_dif = tf.reduce_mean(
53
+ high_diff_main_map * (tf.square(x_gt - x_pr) + (3 * tf.abs(x_gt - x_tol)) - tf.square(x_gt - x_tol)))
54
+ loss_main_low_dif = tf.reduce_mean(low_diff_main_map * (3 * tf.abs(x_gt - x_pr)))
55
+ loss_main = main_loss_weight * (loss_main_high_dif + loss_main_low_dif)
56
+
57
+ loss_tough_assist = tough_loss_weight * tf.reduce_mean(tou_map * tf.abs(x_tough - x_pr))
58
+ loss_tol_assist = tol_loss_weight * tf.reduce_mean(tol_map * tf.abs(x_tol - x_pr))
59
+
60
+ '''dif loss:'''
61
+ loss_total = loss_main + loss_tough_assist + loss_tol_assist
62
+
63
+ return loss_total, loss_main, loss_tough_assist, loss_tol_assist
image_utility.py ADDED
@@ -0,0 +1,585 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import numpy as np
3
+
4
+ import matplotlib
5
+ matplotlib.use('agg')
6
+ import matplotlib.pyplot as plt
7
+
8
+ import math
9
+ from skimage.transform import warp, AffineTransform
10
+ import cv2
11
+ from scipy import misc
12
+ from skimage.transform import rotate
13
+ from PIL import Image
14
+ from PIL import ImageOps
15
+ from skimage.transform import resize
16
+ from skimage import transform
17
+ from skimage.transform import SimilarityTransform, AffineTransform
18
+ import random
19
+
20
+ class ImageUtility:
21
+
22
+ def random_rotate(self, _image, _label, file_name, fn):
23
+
24
+ xy_points, x_points, y_points = self.create_landmarks(landmarks=_label,
25
+ scale_factor_x=1, scale_factor_y=1)
26
+ _image, _label = self.cropImg_2time(_image, x_points, y_points)
27
+
28
+ scale = (np.random.uniform(0.7, 1.3), np.random.uniform(0.7, 1.3))
29
+ # scale = (1, 1)
30
+
31
+ rot = np.random.uniform(-1 * 0.55, 0.55)
32
+ translation = (0, 0)
33
+ shear = 0
34
+
35
+ tform = AffineTransform(
36
+ scale=scale, # ,
37
+ rotation=rot,
38
+ translation=translation,
39
+ shear=np.deg2rad(shear)
40
+ )
41
+
42
+ output_img = transform.warp(_image, tform.inverse, mode='symmetric')
43
+
44
+ sx, sy = scale
45
+ t_matrix = np.array([
46
+ [sx * math.cos(rot), -sy * math.sin(rot + shear), 0],
47
+ [sx * math.sin(rot), sy * math.cos(rot + shear), 0],
48
+ [0, 0, 1]
49
+ ])
50
+ landmark_arr_xy, landmark_arr_x, landmark_arr_y = self.create_landmarks(_label, 1, 1)
51
+ label = np.array(landmark_arr_x + landmark_arr_y).reshape([2, 68])
52
+ marging = np.ones([1, 68])
53
+ label = np.concatenate((label, marging), axis=0)
54
+
55
+ label_t = np.dot(t_matrix, label)
56
+ lbl_flat = np.delete(label_t, 2, axis=0).reshape([136])
57
+
58
+ t_label = self.__reorder(lbl_flat)
59
+
60
+ '''crop data: we add a small margin to the images'''
61
+ xy_points, x_points, y_points = self.create_landmarks(landmarks=t_label,
62
+ scale_factor_x=1, scale_factor_y=1)
63
+ img_arr, points_arr = self.cropImg(output_img, x_points, y_points, no_padding=False)
64
+ # img_arr = output_img
65
+ # points_arr = t_label
66
+ '''resize image to 224*224'''
67
+ resized_img = resize(img_arr,
68
+ (224, 224, 3),
69
+ anti_aliasing=True)
70
+ dims = img_arr.shape
71
+ height = dims[0]
72
+ width = dims[1]
73
+ scale_factor_y = 224 / height
74
+ scale_factor_x = 224 / width
75
+
76
+ '''rescale and retrieve landmarks'''
77
+ landmark_arr_xy, landmark_arr_x, landmark_arr_y = \
78
+ self.create_landmarks(landmarks=points_arr,
79
+ scale_factor_x=scale_factor_x,
80
+ scale_factor_y=scale_factor_y)
81
+
82
+ if not(min(landmark_arr_x) < 0.0 or min(landmark_arr_y) < 0.0 or max(landmark_arr_x) > 224 or max(landmark_arr_y) > 224):
83
+
84
+ # self.print_image_arr(fn, resized_img, landmark_arr_x, landmark_arr_y)
85
+
86
+ im = Image.fromarray((resized_img * 255).astype(np.uint8))
87
+ im.save(str(file_name) + '.jpg')
88
+
89
+ pnt_file = open(str(file_name) + ".pts", "w")
90
+ pre_txt = ["version: 1 \n", "n_points: 68 \n", "{ \n"]
91
+ pnt_file.writelines(pre_txt)
92
+ points_txt = ""
93
+ for i in range(0, len(landmark_arr_xy), 2):
94
+ points_txt += str(landmark_arr_xy[i]) + " " + str(landmark_arr_xy[i + 1]) + "\n"
95
+
96
+ pnt_file.writelines(points_txt)
97
+ pnt_file.write("} \n")
98
+ pnt_file.close()
99
+
100
+ return t_label, output_img
101
+
102
+
103
+
104
+ def random_rotate_m(self, _image, _label_img, file_name):
105
+
106
+ rot = random.uniform(-80.9, 80.9)
107
+
108
+ output_img = rotate(_image, rot, resize=True)
109
+ output_img_lbl = rotate(_label_img, rot, resize=True)
110
+
111
+ im = Image.fromarray((output_img * 255).astype(np.uint8))
112
+ im_lbl = Image.fromarray((output_img_lbl * 255).astype(np.uint8))
113
+
114
+ im_m = ImageOps.mirror(im)
115
+ im_lbl_m = ImageOps.mirror(im_lbl)
116
+
117
+ im.save(str(file_name)+'.jpg')
118
+ # im_lbl.save(str(file_name)+'_lbl.jpg')
119
+
120
+ im_m.save(str(file_name) + '_m.jpg')
121
+ # im_lbl_m.save(str(file_name) + '_m_lbl.jpg')
122
+
123
+ im_lbl_ar = np.array(im_lbl)
124
+ im_lbl_m_ar = np.array(im_lbl_m)
125
+
126
+ self.__save_label(im_lbl_ar, file_name, np.array(im))
127
+ self.__save_label(im_lbl_m_ar, file_name+"_m", np.array(im_m))
128
+
129
+
130
+ def __save_label(self, im_lbl_ar, file_name, img_arr):
131
+
132
+ im_lbl_point = []
133
+ for i in range(im_lbl_ar.shape[0]):
134
+ for j in range(im_lbl_ar.shape[1]):
135
+ if im_lbl_ar[i, j] != 0:
136
+ im_lbl_point.append(j)
137
+ im_lbl_point.append(i)
138
+
139
+ pnt_file = open(str(file_name)+".pts", "w")
140
+
141
+ pre_txt = ["version: 1 \n", "n_points: 68 \n", "{ \n"]
142
+ pnt_file.writelines(pre_txt)
143
+ points_txt = ""
144
+ for i in range(0, len(im_lbl_point), 2):
145
+ points_txt += str(im_lbl_point[i]) + " " + str(im_lbl_point[i+1]) + "\n"
146
+
147
+ pnt_file.writelines(points_txt)
148
+ pnt_file.write("} \n")
149
+ pnt_file.close()
150
+
151
+ '''crop data: we add a small margin to the images'''
152
+ xy_points, x_points, y_points = self.create_landmarks(landmarks=im_lbl_point,
153
+ scale_factor_x=1, scale_factor_y=1)
154
+ img_arr, points_arr = self.cropImg(img_arr, x_points, y_points)
155
+
156
+ '''resize image to 224*224'''
157
+ resized_img = resize(img_arr,
158
+ (224, 224, 3),
159
+ anti_aliasing=True)
160
+ dims = img_arr.shape
161
+ height = dims[0]
162
+ width = dims[1]
163
+ scale_factor_y = 224 / height
164
+ scale_factor_x = 224 / width
165
+
166
+ '''rescale and retrieve landmarks'''
167
+ landmark_arr_xy, landmark_arr_x, landmark_arr_y = \
168
+ self.create_landmarks(landmarks=points_arr,
169
+ scale_factor_x=scale_factor_x,
170
+ scale_factor_y=scale_factor_y)
171
+
172
+ im = Image.fromarray((resized_img * 255).astype(np.uint8))
173
+ im.save(str(im_lbl_point[0])+'.jpg')
174
+ # self.print_image_arr(im_lbl_point[0], resized_img, landmark_arr_x, landmark_arr_y)
175
+
176
+
177
+ def augment(self, _image, _label):
178
+
179
+ # face = misc.face(gray=True)
180
+ #
181
+ # rotate_face = ndimage.rotate(_image, 45)
182
+ # self.print_image_arr(_label[0], rotate_face, [],[])
183
+
184
+ # hue_img = tf.image.random_hue(_image, max_delta=0.1) # max_delta must be in the interval [0, 0.5].
185
+ # sat_img = tf.image.random_saturation(hue_img, lower=0.0, upper=3.0)
186
+ #
187
+ # sat_img = K.eval(sat_img)
188
+ #
189
+ _image = self.__noisy(_image)
190
+
191
+ shear = 0
192
+
193
+ # rot = 0.0
194
+ # scale = (1, 1)
195
+
196
+ rot = np.random.uniform(-1 * 0.009, 0.009)
197
+
198
+ scale = (random.uniform(0.9, 1.00), random.uniform(0.9, 1.00))
199
+
200
+ tform = AffineTransform(scale=scale, rotation=rot, shear=shear,
201
+ translation=(0, 0))
202
+
203
+ output_img = warp(_image, tform.inverse, output_shape=(_image.shape[0], _image.shape[1]))
204
+
205
+ sx, sy = scale
206
+ t_matrix = np.array([
207
+ [sx * math.cos(rot), -sy * math.sin(rot + shear), 0],
208
+ [sx * math.sin(rot), sy * math.cos(rot + shear), 0],
209
+ [0, 0, 1]
210
+ ])
211
+ landmark_arr_xy, landmark_arr_x, landmark_arr_y = self.create_landmarks(_label, 1, 1)
212
+ label = np.array(landmark_arr_x + landmark_arr_y).reshape([2, 68])
213
+ marging = np.ones([1, 68])
214
+ label = np.concatenate((label, marging), axis=0)
215
+
216
+ label_t = np.dot(t_matrix, label)
217
+ lbl_flat = np.delete(label_t, 2, axis=0).reshape([136])
218
+
219
+ t_label = self.__reorder(lbl_flat)
220
+ return t_label, output_img
221
+
222
+ def __noisy(self, image):
223
+ noise_typ = random.randint(0, 3)
224
+ if noise_typ == 0 :#"gauss":
225
+ row, col, ch = image.shape
226
+ mean = 0
227
+ var = 0.1
228
+ sigma = var ** 0.5
229
+ gauss = np.random.normal(mean, sigma, (row, col, ch))
230
+ gauss = gauss.reshape(row, col, ch)
231
+ noisy = image + gauss
232
+ return noisy
233
+ elif noise_typ == 1 :# "s&p":
234
+ row, col, ch = image.shape
235
+ s_vs_p = 0.5
236
+ amount = 0.004
237
+ out = np.copy(image)
238
+ # Salt mode
239
+ num_salt = np.ceil(amount * image.size * s_vs_p)
240
+ coords = [np.random.randint(0, i - 1, int(num_salt))
241
+ for i in image.shape]
242
+ out[coords] = 1
243
+
244
+ # Pepper mode
245
+ num_pepper = np.ceil(amount * image.size * (1. - s_vs_p))
246
+ coords = [np.random.randint(0, i - 1, int(num_pepper))
247
+ for i in image.shape]
248
+ out[coords] = 0
249
+ return out
250
+
251
+ elif noise_typ == 2: #"speckle":
252
+ row, col, ch = image.shape
253
+ gauss = np.random.randn(row, col, ch)
254
+ gauss = gauss.reshape(row, col, ch)
255
+ noisy = image + image * gauss
256
+ return noisy
257
+ else:
258
+ return image
259
+
260
+ def __reorder(self, input_arr):
261
+ out_arr = []
262
+ for i in range(68):
263
+ out_arr.append(input_arr[i])
264
+ k = 68 + i
265
+ out_arr.append(input_arr[k])
266
+ return np.array(out_arr)
267
+
268
+ def print_image_arr_heat(self, k, image):
269
+ plt.figure()
270
+ plt.imshow(image)
271
+ implot = plt.imshow(image)
272
+ plt.axis('off')
273
+ plt.savefig('heat' + str(k) + '.png', bbox_inches='tight')
274
+ plt.clf()
275
+
276
+ def print_image_arr(self, k, image, landmarks_x, landmarks_y):
277
+ plt.figure()
278
+ plt.imshow(image)
279
+ implot = plt.imshow(image)
280
+
281
+ plt.scatter(x=landmarks_x[:], y=landmarks_y[:], c='black', s=20)
282
+ plt.scatter(x=landmarks_x[:], y=landmarks_y[:], c='white', s=15)
283
+ plt.axis('off')
284
+ plt.savefig('sss' + str(k) + '.png', bbox_inches='tight')
285
+ # plt.show()
286
+ plt.clf()
287
+
288
+ def create_landmarks_from_normalized_original_img(self, img, landmarks, width, height, x_center, y_center, x1, y1, scale_x, scale_y):
289
+ # landmarks_splited = _landmarks.split(';')
290
+ landmark_arr_xy = []
291
+ landmark_arr_x = []
292
+ landmark_arr_y = []
293
+
294
+ for j in range(0, len(landmarks), 2):
295
+ x = ((x_center - float(landmarks[j]) * width)*scale_x) + x1
296
+ y = ((y_center - float(landmarks[j + 1]) * height)*scale_y) + y1
297
+
298
+ landmark_arr_xy.append(x)
299
+ landmark_arr_xy.append(y)
300
+
301
+ landmark_arr_x.append(x)
302
+ landmark_arr_y.append(y)
303
+
304
+ img = cv2.circle(img, (int(x), int(y)), 2, (255, 14, 74), 2)
305
+ img = cv2.circle(img, (int(x), int(y)), 1, (0, 255, 255), 1)
306
+
307
+ return landmark_arr_xy, landmark_arr_x, landmark_arr_y, img
308
+
309
+
310
+ def create_landmarks_from_normalized(self, landmarks, width, height, x_center, y_center):
311
+
312
+ # landmarks_splited = _landmarks.split(';')
313
+ landmark_arr_xy = []
314
+ landmark_arr_x = []
315
+ landmark_arr_y = []
316
+
317
+ for j in range(0, len(landmarks), 2):
318
+ x = x_center - float(landmarks[j]) * width
319
+ y = y_center - float(landmarks[j + 1]) * height
320
+
321
+ landmark_arr_xy.append(x)
322
+ landmark_arr_xy.append(y) # [ x1, y1, x2,y2 ]
323
+
324
+ landmark_arr_x.append(x) # [x1, x2]
325
+ landmark_arr_y.append(y) # [y1, y2]
326
+
327
+ return landmark_arr_xy, landmark_arr_x, landmark_arr_y
328
+
329
+ def create_landmarks(self, landmarks, scale_factor_x=1, scale_factor_y=1):
330
+ # landmarks_splited = _landmarks.split(';')
331
+ landmark_arr_xy = []
332
+ landmark_arr_x = []
333
+ landmark_arr_y = []
334
+ for j in range(0, len(landmarks), 2):
335
+
336
+ x = float(landmarks[j]) * scale_factor_x
337
+ y = float(landmarks[j + 1]) * scale_factor_y
338
+
339
+ landmark_arr_xy.append(x)
340
+ landmark_arr_xy.append(y) # [ x1, y1, x2,y2 ]
341
+
342
+ landmark_arr_x.append(x) # [x1, x2]
343
+ landmark_arr_y.append(y) # [y1, y2]
344
+
345
+ return landmark_arr_xy, landmark_arr_x, landmark_arr_y
346
+
347
+ def create_landmarks_aflw(self, landmarks, scale_factor_x, scale_factor_y):
348
+ # landmarks_splited = _landmarks.split(';')
349
+ landmark_arr_xy = []
350
+ landmark_arr_x = []
351
+ landmark_arr_y = []
352
+ for j in range(0, len(landmarks), 2):
353
+ if landmarks[j][0] == 1:
354
+ x = float(landmarks[j][1]) * scale_factor_x
355
+ y = float(landmarks[j][2]) * scale_factor_y
356
+
357
+ landmark_arr_xy.append(x)
358
+ landmark_arr_xy.append(y) # [ x1, y1, x2,y2 ]
359
+
360
+ landmark_arr_x.append(x) # [x1, x2]
361
+ landmark_arr_y.append(y) # [y1, y2]
362
+
363
+ return landmark_arr_xy, landmark_arr_x, landmark_arr_y
364
+
365
+ def random_augmentation(self, lbl, img):
366
+ # a = random.randint(0, 1)
367
+ # if a == 0:
368
+ # img, lbl = self.__add_margin(img, img.shape[0], lbl)
369
+
370
+ img, lbl = self.__add_margin(img, img.shape[0], lbl)
371
+
372
+ # else:
373
+ # img, lbl = self.__negative_crop(img, lbl)
374
+
375
+ # i = random.randint(0, 2)
376
+ # if i == 0:
377
+ # img, lbl = self.__rotate(img, lbl, 90, img.shape[0], img.shape[1])
378
+ # elif i == 1:
379
+ # img, lbl = self.__rotate(img, lbl, 180, img.shape[0], img.shape[1])
380
+ # else:
381
+ # img, lbl = self.__rotate(img, lbl, 270, img.shape[0], img.shape[1])
382
+
383
+ k = random.randint(0, 3)
384
+ if k > 0:
385
+ img = self.__change_color(img)
386
+
387
+ lbl = np.reshape(lbl, [136])
388
+ return lbl, img
389
+
390
+
391
+ def cropImg_2time(self, img, x_s, y_s):
392
+ min_x = max(0, int(min(x_s) - 150))
393
+ max_x = int(max(x_s) + 150)
394
+ min_y = max(0, int(min(y_s) - 150))
395
+ max_y = int(max(y_s) + 150)
396
+
397
+ crop = img[min_y:max_y, min_x:max_x]
398
+
399
+ new_x_s = []
400
+ new_y_s = []
401
+ new_xy_s = []
402
+
403
+ for i in range(len(x_s)):
404
+ new_x_s.append(x_s[i] - min_x)
405
+ new_y_s.append(y_s[i] - min_y)
406
+ new_xy_s.append(x_s[i] - min_x)
407
+ new_xy_s.append(y_s[i] - min_y)
408
+ return crop, new_xy_s
409
+
410
+ def cropImg(self, img, x_s, y_s, no_padding=False):
411
+ margin1 = random.randint(5, 20)
412
+ margin2 = random.randint(5, 20)
413
+ margin3 = random.randint(5, 20)
414
+ margin4 = random.randint(5, 20)
415
+
416
+ if no_padding:
417
+ min_x = max(0, int(min(x_s)))
418
+ max_x = int(max(x_s))
419
+ min_y = max(0, int(min(y_s)))
420
+ max_y = int(max(y_s))
421
+ else:
422
+ min_x = max(0, int(min(x_s) - margin1))
423
+ max_x = int(max(x_s) + margin2)
424
+ min_y = max(0, int(min(y_s) - margin3))
425
+ max_y = int(max(y_s) + margin4)
426
+
427
+ crop = img[min_y:max_y, min_x:max_x]
428
+
429
+ new_x_s = []
430
+ new_y_s = []
431
+ new_xy_s = []
432
+
433
+ for i in range(len(x_s)):
434
+ new_x_s.append(x_s[i] - min_x)
435
+ new_y_s.append(y_s[i] - min_y)
436
+ new_xy_s.append(x_s[i] - min_x)
437
+ new_xy_s.append(y_s[i] - min_y)
438
+
439
+ # imgpr.print_image_arr(k, crop, new_x_s, new_y_s)
440
+ # imgpr.print_image_arr_2(i, img, x_s, y_s, [min_x, max_x], [min_y, max_y])
441
+
442
+ return crop, new_xy_s
443
+
444
+ def __negative_crop(self, img, landmarks):
445
+
446
+ landmark_arr_xy, x_s, y_s = self.create_landmarks(landmarks, 1, 1)
447
+ min_x = img.shape[0] // random.randint(5, 15)
448
+ max_x = img.shape[0] - (img.shape[0] // random.randint(15, 20))
449
+ min_y = img.shape[0] // random.randint(5, 15)
450
+ max_y = img.shape[0] - (img.shape[0] // random.randint(15, 20))
451
+
452
+ crop = img[min_y:max_y, min_x:max_x]
453
+
454
+ new_x_s = []
455
+ new_y_s = []
456
+ new_xy_s = []
457
+
458
+ for i in range(len(x_s)):
459
+ new_x_s.append(x_s[i] - min_x)
460
+ new_y_s.append(y_s[i] - min_y)
461
+ new_xy_s.append(x_s[i] - min_x)
462
+ new_xy_s.append(y_s[i] - min_y)
463
+
464
+ # imgpr.print_image_arr(crop.shape[0], crop, new_x_s, new_y_s)
465
+ # imgpr.print_image_arr_2(crop.shape[0], crop, x_s, y_s, [min_x, max_x], [min_y, max_y])
466
+
467
+ return crop, new_xy_s
468
+
469
+ def __add_margin(self, img, img_w, lbl):
470
+ marging_width = img_w // random.randint(15, 20)
471
+ direction = random.randint(0, 4)
472
+
473
+ if direction == 1:
474
+ margings = np.random.random([img_w, int(marging_width), 3])
475
+ img = np.concatenate((img, margings), axis=1)
476
+
477
+ if direction == 2:
478
+ margings_1 = np.random.random([img_w, int(marging_width), 3])
479
+ img = np.concatenate((img, margings_1), axis=1)
480
+
481
+ marging_width_1 = img_w // random.randint(15, 20)
482
+ margings_2 = np.random.random([int(marging_width_1), img_w + int(marging_width), 3])
483
+ img = np.concatenate((img, margings_2), axis=0)
484
+
485
+ if direction == 3: # need chane labels
486
+ margings_1 = np.random.random([img_w, int(marging_width), 3])
487
+ img = np.concatenate((margings_1, img), axis=1)
488
+ lbl = self.__transfer_lbl(int(marging_width), lbl, [1, 0])
489
+
490
+ marging_width_1 = img_w // random.randint(15, 20)
491
+ margings_2 = np.random.random([int(marging_width_1), img_w + int(marging_width), 3])
492
+ img = np.concatenate((margings_2, img), axis=0)
493
+ lbl = self.__transfer_lbl(int(marging_width_1), lbl, [0, 1])
494
+
495
+ if direction == 4: # need chane labels
496
+ margings_1 = np.random.random([img_w, int(marging_width), 3])
497
+ img = np.concatenate((margings_1, img), axis=1)
498
+ lbl = self.__transfer_lbl(int(marging_width), lbl, [1, 0])
499
+ img_w1 = img_w + int(marging_width)
500
+
501
+ marging_width_1 = img_w // random.randint(15, 20)
502
+ margings_2 = np.random.random([int(marging_width_1), img_w1, 3])
503
+ img = np.concatenate((margings_2, img), axis=0)
504
+ lbl = self.__transfer_lbl(int(marging_width_1), lbl, [0, 1])
505
+ img_w2 = img_w + int(marging_width_1)
506
+
507
+ marging_width_1 = img_w // random.randint(15, 20)
508
+ margings_1 = np.random.random([img_w2, int(marging_width_1), 3])
509
+ img = np.concatenate((img, margings_1), axis=1)
510
+
511
+ marging_width_1 = img_w // random.randint(15, 20)
512
+ margings_2 = np.random.random([int(marging_width_1), img.shape[1], 3])
513
+ img = np.concatenate((img, margings_2), axis=0)
514
+
515
+ return img, lbl
516
+
517
+ def __void_image(self, img, img_w, ):
518
+ marging_width = int(img_w / random.randint(7, 16))
519
+ direction = random.randint(0, 1)
520
+ direction = 0
521
+ if direction == 0:
522
+ np.delete(img, 100, 1)
523
+ # img[:, 0:marging_width, :] = 0
524
+ elif direction == 1:
525
+ img[img_w - marging_width:img_w, :, :] = 0
526
+ if direction == 2:
527
+ img[:, img_w - marging_width:img_w, :] = 0
528
+
529
+ return img
530
+
531
+ def __change_color(self, img):
532
+ # color_arr = np.random.random([img.shape[0], img.shape[1]])
533
+ color_arr = np.zeros([img.shape[0], img.shape[1]])
534
+ axis = random.randint(0, 4)
535
+
536
+ if axis == 0: # red
537
+ img_mono = img[:, :, 0]
538
+ new_img = np.stack([img_mono, color_arr, color_arr], axis=2)
539
+ elif axis == 1: # green
540
+ img_mono = img[:, :, 1]
541
+ new_img = np.stack([color_arr, img_mono, color_arr], axis=2)
542
+ elif axis == 2: # blue
543
+ img_mono = img[:, :, 1]
544
+ new_img = np.stack([color_arr, img_mono, color_arr], axis=2)
545
+ elif axis == 3: # gray scale
546
+ img_mono = img[:, :, 0]
547
+ new_img = np.stack([img_mono, img_mono, img_mono], axis=2)
548
+ else: # random noise
549
+ color_arr = np.random.random([img.shape[0], img.shape[1]])
550
+ img_mono = img[:, :, 0]
551
+ new_img = np.stack([img_mono, img_mono, color_arr], axis=2)
552
+
553
+ return new_img
554
+
555
+ def __rotate_origin_only(self, xy_arr, radians, xs, ys):
556
+ """Only rotate a point around the origin (0, 0)."""
557
+ rotated = []
558
+ for xy in xy_arr:
559
+ x, y = xy
560
+ xx = x * math.cos(radians) + y * math.sin(radians)
561
+ yy = -x * math.sin(radians) + y * math.cos(radians)
562
+ rotated.append([xx + xs, yy + ys])
563
+ return np.array(rotated)
564
+
565
+ def __rotate(self, img, landmark_old, degree, img_w, img_h):
566
+ landmark_old = np.reshape(landmark_old, [68, 2])
567
+
568
+ theta = math.radians(degree)
569
+
570
+ if degree == 90:
571
+ landmark = self.__rotate_origin_only(landmark_old, theta, 0, img_h)
572
+ return np.rot90(img, 3, axes=(-2, 0)), landmark
573
+ elif degree == 180:
574
+ landmark = self.__rotate_origin_only(landmark_old, theta, img_h, img_w)
575
+ return np.rot90(img, 2, axes=(-2, 0)), landmark
576
+ elif degree == 270:
577
+ landmark = self.__rotate_origin_only(landmark_old, theta, img_w, 0)
578
+ return np.rot90(img, 1, axes=(-2, 0)), landmark
579
+
580
+ def __transfer_lbl(self, marging_width_1, lbl, axis_arr):
581
+ new_lbl = []
582
+ for i in range(0, len(lbl), 2):
583
+ new_lbl.append(lbl[i] + marging_width_1 * axis_arr[0])
584
+ new_lbl.append(lbl[i + 1] + marging_width_1 * axis_arr[1])
585
+ return np.array(new_lbl)
img_printer.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import matplotlib
3
+ matplotlib.use('agg')
4
+ import matplotlib.pyplot as plt
5
+ from pathlib import Path
6
+
7
+
8
+ def print_image(image_name, landmarks_x, landmarks_y):
9
+
10
+ my_file = Path(image_name)
11
+ if my_file.is_file():
12
+ im = plt.imread(image_name)
13
+ implot = plt.imshow(im)
14
+
15
+ plt.scatter(x=landmarks_x[:], y=landmarks_y[:], c='r', s=10)
16
+ plt.show()
17
+
18
+
19
+ def print_image_arr_heat(k, image, print_single=False):
20
+ import numpy as np
21
+ for i in range(image.shape[2]):
22
+ img = np.sum(image, axis=2)
23
+ if print_single:
24
+ plt.figure()
25
+ plt.imshow(image[:, :, i])
26
+ # implot = plt.imshow(image[:, :, i])
27
+ plt.axis('off')
28
+ plt.savefig('single_heat_' + str(i+(k*100)) + '.png', bbox_inches='tight')
29
+ plt.clf()
30
+
31
+ plt.figure()
32
+ plt.imshow(img, vmin=0, vmax=1)
33
+ plt.axis('off')
34
+ plt.savefig('heat_' + str(k) + '.png', bbox_inches='tight')
35
+ plt.clf()
36
+
37
+
38
+ def print_image_arr(k, image, landmarks_x, landmarks_y):
39
+ plt.figure()
40
+ plt.imshow(image)
41
+ implot = plt.imshow(image)
42
+
43
+ # for i in range(len(landmarks_x)):
44
+ # plt.text(landmarks_x[i], landmarks_y[i], str(i), fontsize=12, c='red',
45
+ # horizontalalignment='center', verticalalignment='center',
46
+ # bbox={'facecolor': 'blue', 'alpha': 0.3, 'pad': 0.0})
47
+
48
+ plt.scatter(x=landmarks_x[:], y=landmarks_y[:], c='#000000', s=15)
49
+ plt.scatter(x=landmarks_x[:], y=landmarks_y[:], c='#fddb3a', s=8)
50
+ # plt.axis('off')
51
+ plt.savefig('z_' + str(k) + '.png', bbox_inches='tight')
52
+ # plt.show()
53
+ plt.clf()
54
+
55
+
56
+ def print_image_arr_2(k, image, landmarks_x, landmarks_y, xs, ys):
57
+ plt.figure()
58
+ plt.imshow(image)
59
+ implot = plt.imshow(image)
60
+
61
+ # plt.scatter(x=landmarks_x[:], y=landmarks_y[:], c='b', s=5)
62
+ # for i in range(68):
63
+ # plt.annotate(str(i), (landmarks_x[i], landmarks_y[i]), fontsize=6)
64
+
65
+ plt.scatter(x=landmarks_x[:], y=landmarks_y[:], c='b', s=5)
66
+ plt.scatter(x=xs, y=ys, c='r', s=5)
67
+ plt.savefig('sss'+str(k)+'.png')
68
+ # plt.show()
69
+ plt.clf()
70
+
71
+
72
+ def print_two_landmarks(image, landmarks_1, landmarks_2):
73
+ plt.figure()
74
+ plt.imshow(image)
75
+ implot = plt.imshow(image)
76
+
77
+ plt.scatter(x=landmarks_1[:68], y=landmarks_1[68:], c='b', s=10)
78
+ plt.scatter(x=landmarks_2[:68], y=landmarks_2[68:], c='r', s=10)
79
+ # plt.savefig('a'+str(landmarks_x[0])+'.png')
80
+ plt.show()
81
+ plt.clf()
main.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from configuration import DatasetName, D300wConf, InputDataSize, CofwConf, WflwConf
3
+ from cnn_model import CNNModel
4
+ from pca_utility import PCAUtility
5
+ from image_utility import ImageUtility
6
+ from student_train import StudentTrainer
7
+ from test import Test
8
+ from teacher_trainer import TeacherTrainer
9
+
10
+ if __name__ == '__main__':
11
+
12
+ '''test models'''
13
+
14
+ '''train Teacher Networks'''
15
+ trainer = TeacherTrainer(dataset_name=DatasetName.w300)
16
+ trainer.train(arch='efficientNet',weight_path=None)
17
+
18
+
19
+ '''Training Student Network'''
20
+ '''300W'''
21
+ st_trainer = StudentTrainer(dataset_name=DatasetName.w300, use_augmneted=True)
22
+ st_trainer.train(arch_student='mobileNetV2', weight_path_student=None,
23
+ loss_weight_student=2.0,
24
+ arch_tough_teacher='efficientNet', weight_path_tough_teacher='./models/teachers/ds_300w_ef_tou.h5',
25
+ loss_weight_tough_teacher=1,
26
+ arch_tol_teacher='efficientNet', weight_path_tol_teacher='./models/teachers/ds_300w_ef_tol.h5',
27
+ loss_weight_tol_teacher=1)
28
+
29
+ '''COFW'''
30
+ st_trainer = StudentTrainer(dataset_name=DatasetName.cofw, use_augmneted=True)
31
+ st_trainer.train(arch_student='mobileNetV2', weight_path_student=None,
32
+ loss_weight_student=2.0,
33
+ arch_tough_teacher='efficientNet', weight_path_tough_teacher='./models/teachers/ds_cofw_ef_tou.h5',
34
+ loss_weight_tough_teacher=1,
35
+ arch_tol_teacher='efficientNet', weight_path_tol_teacher='./models/teachers/ds_cofw_ef_tol.h5',
36
+ loss_weight_tol_teacher=1)
37
+
38
+ '''WFLW'''
39
+ st_trainer = StudentTrainer(dataset_name=DatasetName.wflw, use_augmneted=True)
40
+ st_trainer.train(arch_student='mobileNetV2', weight_path_student=None,
41
+ loss_weight_student=2.0,
42
+ arch_tough_teacher='efficientNet', weight_path_tough_teacher='./models/teachers/ds_wflw_ef_tou.h5',
43
+ loss_weight_tough_teacher=1,
44
+ arch_tol_teacher='efficientNet', weight_path_tol_teacher='./models/teachers/ds_wflw_ef_tol.h5',
45
+ loss_weight_tol_teacher=1)
models/students/student_Net_300w.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5fc9c077037f4f7cc3df664b95f1f98397a11a2b298e121dc5e79f46837cb240
3
+ size 10197672
models/students/student_Net_COFW.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f3e8cc30f7f6bc3327cf3e59332552b2a321142376b27f14e3b957ffb94fb1da
3
+ size 9799400
models/students/student_Net_WFLW.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:61f3ee0c6188856377bd9e38109fdfa0aa97170086cfbdd2bc3c3f355f0f82cd
3
+ size 10504872
pca_utility.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from configuration import DatasetName, DatasetType,\
2
+ AffectnetConf, D300wConf, W300Conf, InputDataSize, LearningConfig, CofwConf, WflwConf
3
+ from image_utility import ImageUtility
4
+ from sklearn.decomposition import PCA, IncrementalPCA
5
+ from sklearn.decomposition import TruncatedSVD
6
+ import numpy as np
7
+ import pickle
8
+ import os
9
+ from tqdm import tqdm
10
+ from numpy import save, load
11
+ import math
12
+ from PIL import Image
13
+
14
+
15
+ class PCAUtility:
16
+ eigenvalues_prefix = "_eigenvalues_"
17
+ eigenvectors_prefix = "_eigenvectors_"
18
+ meanvector_prefix = "_meanvector_"
19
+
20
+ def create_pca_from_npy(self, dataset_name, pca_postfix):
21
+ lbl_arr = []
22
+ path = None
23
+ if dataset_name == DatasetName.ibug:
24
+ path = D300wConf.normalized_point # normalized
25
+ # elif dataset_name == DatasetName.cofw:
26
+ # path = CofwConf.normalized_points_npy_dir # normalized
27
+ # elif dataset_name == DatasetName.wflw:
28
+ # path = WflwConf.normalized_points_npy_dir # normalized
29
+
30
+ lbl_arr = []
31
+ for file in tqdm(os.listdir(path)):
32
+ if file.endswith(".npy"):
33
+ npy_file = os.path.join(path, file)
34
+ lbl_arr.append(load(npy_file))
35
+
36
+ lbl_arr = np.array(lbl_arr)
37
+
38
+ print('PCA calculation started')
39
+
40
+ ''' no normalization is needed, since we want to generate hm'''
41
+ reduced_lbl_arr, eigenvalues, eigenvectors = self._func_PCA(lbl_arr, pca_postfix)
42
+ mean_lbl_arr = np.mean(lbl_arr, axis=0)
43
+ eigenvectors = eigenvectors.T
44
+ #
45
+ # self.__save_obj(eigenvalues, dataset_name + self.__eigenvalues_prefix + str(pca_postfix))
46
+ # self.__save_obj(eigenvectors, dataset_name + self.__eigenvectors_prefix + str(pca_postfix))
47
+ # self.__save_obj(mean_lbl_arr, dataset_name + self.__meanvector_prefix + str(pca_postfix))
48
+ #
49
+ save('pca_obj/' + dataset_name + self.eigenvalues_prefix + str(pca_postfix), eigenvalues)
50
+ save('pca_obj/' + dataset_name + self.eigenvectors_prefix + str(pca_postfix), eigenvectors)
51
+ save('pca_obj/' + dataset_name + self.meanvector_prefix + str(pca_postfix), mean_lbl_arr)
52
+
53
+ def create_pca_from_points(self, dataset_name, pca_postfix):
54
+ lbl_arr = []
55
+ path = None
56
+ if dataset_name == DatasetName.ibug:
57
+ path = D300wConf.rotated_img_path_prefix # rotated is ok, since advs_aug is the same as rotated
58
+ num_of_landmarks = D300wConf.num_of_landmarks
59
+ elif dataset_name == DatasetName.cofw:
60
+ path = CofwConf.rotated_img_path_prefix
61
+ num_of_landmarks = CofwConf.num_of_landmarks
62
+ elif dataset_name == DatasetName.wflw:
63
+ path = WflwConf.rotated_img_path_prefix
64
+ num_of_landmarks = WflwConf.num_of_landmarks
65
+
66
+ for file in tqdm(os.listdir(path)):
67
+ if file.endswith(".pts"):
68
+ pts_file = os.path.join(path, file)
69
+
70
+ points_arr = []
71
+ with open(pts_file) as fp:
72
+ line = fp.readline()
73
+ cnt = 1
74
+ while line:
75
+ if 3 < cnt <= num_of_landmarks+3:
76
+ x_y_pnt = line.strip()
77
+ x = float(x_y_pnt.split(" ")[0])
78
+ y = float(x_y_pnt.split(" ")[1])
79
+ points_arr.append(x)
80
+ points_arr.append(y)
81
+ line = fp.readline()
82
+ cnt += 1
83
+ lbl_arr.append(points_arr)
84
+
85
+ lbl_arr = np.array(lbl_arr)
86
+
87
+ print('PCA calculation started')
88
+
89
+ ''' no normalization is needed, since we want to generate hm'''
90
+ reduced_lbl_arr, eigenvalues, eigenvectors = self._func_PCA(lbl_arr, pca_postfix)
91
+ mean_lbl_arr = np.mean(lbl_arr, axis=0)
92
+ eigenvectors = eigenvectors.T
93
+ #
94
+ # self.__save_obj(eigenvalues, dataset_name + self.__eigenvalues_prefix + str(pca_postfix))
95
+ # self.__save_obj(eigenvectors, dataset_name + self.__eigenvectors_prefix + str(pca_postfix))
96
+ # self.__save_obj(mean_lbl_arr, dataset_name + self.__meanvector_prefix + str(pca_postfix))
97
+ #
98
+ save('pca_obj/' + dataset_name + self.eigenvalues_prefix + str(pca_postfix), eigenvalues)
99
+ save('pca_obj/' + dataset_name + self.eigenvectors_prefix + str(pca_postfix), eigenvectors)
100
+ save('pca_obj/' + dataset_name + self.meanvector_prefix + str(pca_postfix), mean_lbl_arr)
101
+
102
+ def test_pca_validity(self, dataset_name, pca_postfix):
103
+ image_utility = ImageUtility()
104
+
105
+ eigenvalues = load('pca_obj/' + dataset_name + self.eigenvalues_prefix + str(pca_postfix)+".npy")
106
+ eigenvectors = load('pca_obj/' + dataset_name + self.eigenvectors_prefix + str(pca_postfix)+".npy")
107
+ meanvector = load('pca_obj/' + dataset_name + self.meanvector_prefix + str(pca_postfix)+".npy")
108
+
109
+ '''load data: '''
110
+ lbl_arr = []
111
+ img_arr = []
112
+ path = None
113
+
114
+ if dataset_name == DatasetName.ibug:
115
+ path = D300wConf.rotated_img_path_prefix # rotated is ok, since advs_aug is the same as rotated
116
+ num_of_landmarks = D300wConf.num_of_landmarks
117
+ elif dataset_name == DatasetName.cofw:
118
+ path = CofwConf.rotated_img_path_prefix
119
+ num_of_landmarks = CofwConf.num_of_landmarks
120
+ elif dataset_name == DatasetName.wflw:
121
+ path = WflwConf.rotated_img_path_prefix
122
+ num_of_landmarks = WflwConf.num_of_landmarks
123
+
124
+ for file in tqdm(os.listdir(path)):
125
+ if file.endswith(".pts"):
126
+ pts_file = os.path.join(path, file)
127
+ img_file = pts_file[:-3] + "jpg"
128
+ if not os.path.exists(img_file):
129
+ continue
130
+
131
+ points_arr = []
132
+ with open(pts_file) as fp:
133
+ line = fp.readline()
134
+ cnt = 1
135
+ while line:
136
+ if 3 < cnt <= num_of_landmarks+3:
137
+ x_y_pnt = line.strip()
138
+ x = float(x_y_pnt.split(" ")[0])
139
+ y = float(x_y_pnt.split(" ")[1])
140
+ points_arr.append(x)
141
+ points_arr.append(y)
142
+ line = fp.readline()
143
+ cnt += 1
144
+ lbl_arr.append(points_arr)
145
+ img_arr.append(Image.open(img_file))
146
+
147
+ for i in range(30):
148
+ b_vector_p = self.calculate_b_vector(lbl_arr[i], True, eigenvalues, eigenvectors, meanvector)
149
+ lbl_new = meanvector + np.dot(eigenvectors, b_vector_p)
150
+ lbl_new = lbl_new.tolist()
151
+
152
+ labels_true_transformed, landmark_arr_x_t, landmark_arr_y_t = image_utility. \
153
+ create_landmarks(lbl_arr[i], 1, 1)
154
+
155
+ labels_true_transformed_pca, landmark_arr_x_pca, landmark_arr_y_pca = image_utility. \
156
+ create_landmarks(lbl_new, 1, 1)
157
+
158
+ image_utility.print_image_arr(i, img_arr[i], landmark_arr_x_t, landmark_arr_y_t)
159
+ image_utility.print_image_arr(i * 1000, img_arr[i], landmark_arr_x_pca, landmark_arr_y_pca)
160
+
161
+ def calculate_b_vector(self, predicted_vector, correction, eigenvalues, eigenvectors, meanvector):
162
+ tmp1 = predicted_vector - meanvector
163
+ b_vector = np.dot(eigenvectors.T, tmp1)
164
+
165
+ # put b in -3lambda =>
166
+ if correction:
167
+ i = 0
168
+ for b_item in b_vector:
169
+ lambda_i_sqr = 3 * math.sqrt(eigenvalues[i])
170
+
171
+ if b_item > 0:
172
+ b_item = min(b_item, lambda_i_sqr)
173
+ else:
174
+ b_item = max(b_item, -1 * lambda_i_sqr)
175
+ b_vector[i] = b_item
176
+ i += 1
177
+
178
+ return b_vector
179
+
180
+ def create_pca(self, dataset_name, pca_postfix):
181
+ tf_record_util = TFRecordUtility()
182
+
183
+ lbl_arr = []
184
+ pose_arr = []
185
+ if dataset_name == DatasetName.ibug:
186
+ lbl_arr, img_arr, pose_arr = tf_record_util.retrieve_tf_record(D300wConf.tf_train_path,
187
+ D300wConf.sum_of_train_samples,
188
+ only_label=True, only_pose=True)
189
+ lbl_arr = np.array(lbl_arr)
190
+
191
+ print('PCA-retrieved')
192
+
193
+ '''need to be normalized based on the hyper face paper?'''
194
+
195
+ # reduced_lbl_arr, eigenvalues, eigenvectors = self.__svd_func(lbl_arr, pca_postfix)
196
+ reduced_lbl_arr, eigenvalues, eigenvectors = self.__func_PCA(lbl_arr, pca_postfix)
197
+ mean_lbl_arr = np.mean(lbl_arr, axis=0)
198
+ eigenvectors = eigenvectors.T
199
+
200
+ self.__save_obj(eigenvalues, dataset_name + self.eigenvalues_prefix + str(pca_postfix))
201
+ self.__save_obj(eigenvectors, dataset_name + self.eigenvectors_prefix + str(pca_postfix))
202
+ self.__save_obj(mean_lbl_arr, dataset_name + self.meanvector_prefix + str(pca_postfix))
203
+
204
+ '''calculate pose min max'''
205
+ p_1_arr = []
206
+ p_2_arr = []
207
+ p_3_arr = []
208
+
209
+ for p_item in pose_arr:
210
+ p_1_arr.append(p_item[0])
211
+ p_2_arr.append(p_item[1])
212
+ p_3_arr.append(p_item[2])
213
+
214
+ p_1_min = min(p_1_arr)
215
+ p_1_max = max(p_1_arr)
216
+
217
+ p_2_min = min(p_2_arr)
218
+ p_2_max = max(p_2_arr)
219
+
220
+ p_3_min = min(p_3_arr)
221
+ p_3_max = max(p_3_arr)
222
+
223
+ self.__save_obj(p_1_min, 'p_1_min')
224
+ self.__save_obj(p_1_max, 'p_1_max')
225
+
226
+ self.__save_obj(p_2_min, 'p_2_min')
227
+ self.__save_obj(p_2_max, 'p_2_max')
228
+
229
+ self.__save_obj(p_3_min, 'p_3_min')
230
+ self.__save_obj(p_3_max, 'p_3_max')
231
+
232
+ print('PCA-->done')
233
+
234
+ def __save_obj(self, obj, name):
235
+ with open('obj/' + name + '.pkl', 'wb') as f:
236
+ pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)
237
+
238
+ def load_pose_obj(self):
239
+ with open('obj/p_1_min.pkl', 'rb') as f:
240
+ p_1_min = pickle.load(f)
241
+ with open('obj/p_1_max.pkl', 'rb') as f:
242
+ p_1_max = pickle.load(f)
243
+
244
+ with open('obj/p_2_min.pkl', 'rb') as f:
245
+ p_2_min = pickle.load(f)
246
+ with open('obj/p_2_max.pkl', 'rb') as f:
247
+ p_2_max = pickle.load(f)
248
+
249
+ with open('obj/p_3_min.pkl', 'rb') as f:
250
+ p_3_min = pickle.load(f)
251
+ with open('obj/p_3_max.pkl', 'rb') as f:
252
+ p_3_max = pickle.load(f)
253
+
254
+ return p_1_min, p_1_max, p_2_min, p_2_max, p_3_min, p_3_max
255
+
256
+
257
+ def load_pca_obj(self, dataset_name, pca_postfix=97):
258
+ with open('obj/' + dataset_name + self.eigenvalues_prefix + str(pca_postfix) + '.pkl', 'rb') as f:
259
+ eigenvalues = pickle.load(f)
260
+ with open('obj/' + dataset_name + self.eigenvectors_prefix + str(pca_postfix) + '.pkl', 'rb') as f:
261
+ eigenvectors = pickle.load(f)
262
+ with open('obj/' + dataset_name + self.meanvector_prefix + str(pca_postfix) + '.pkl', 'rb') as f:
263
+ meanvector = pickle.load(f)
264
+ return eigenvalues, eigenvectors, meanvector
265
+
266
+ def _func_PCA(self, input_data, pca_postfix):
267
+ input_data = np.array(input_data)
268
+ pca = PCA(n_components=pca_postfix/100)
269
+ # pca = PCA(n_components=0.98)
270
+ # pca = IncrementalPCA(n_components=50, batch_size=50)
271
+ pca.fit(input_data)
272
+ pca_input_data = pca.transform(input_data)
273
+ eigenvalues = pca.explained_variance_
274
+ eigenvectors = pca.components_
275
+ return pca_input_data, eigenvalues, eigenvectors
276
+
277
+ def __svd_func(self, input_data, pca_postfix):
278
+ svd = TruncatedSVD(n_components=50)
279
+ svd.fit(input_data)
280
+ pca_input_data = svd.transform(input_data)
281
+ eigenvalues = svd.explained_variance_
282
+ eigenvectors = svd.components_
283
+ return pca_input_data, eigenvalues, eigenvectors
284
+ # U, S, VT = svd(input_data)
285
+
286
+
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # for cuda 9
2
+ # pip install torch==1.2.0+cu92 torchvision==0.4.0+cu92 -f https://download.pytorch.org/whl/torch_stable.html
3
+ #
4
+ #torch
5
+ #torchvision
6
+ bottleneck
7
+ numpy==1.19.2
8
+ #tensorflow==1.14.0
9
+ tensorflow==2.3.1
10
+ #tensorflow-gpu==1.14
11
+ # keras==2.2.4
12
+ keras==2.4.3
13
+ matplotlib
14
+ opencv-python
15
+ opencv-contrib-python
16
+ scipy
17
+ scikit-learn
18
+ scikit-image
19
+ Pillow
20
+ tqdm
21
+ efficientnet
22
+ # tfkerassurgeon
23
+ tensorboard
samples/KDLoss-1.jpg ADDED

Git LFS Details

  • SHA256: 44f5e71731f1d68ae3fae29bd43d5f594fe73226d6fb2c021e583480a8105a1e
  • Pointer size: 132 Bytes
  • Size of remote file: 1.35 MB
samples/KD_300W_samples-1.jpg ADDED

Git LFS Details

  • SHA256: fe90d36cdad1d8621a07a79bea60e480bfc96d06f1b3f3c6d710ca1e8445751f
  • Pointer size: 131 Bytes
  • Size of remote file: 442 kB
samples/KD_L2_L1-1.jpg ADDED

Git LFS Details

  • SHA256: 4e95ae07c955f87b631c704459897db8cf760186829828c567ad961a9374e94f
  • Pointer size: 130 Bytes
  • Size of remote file: 14.7 kB
samples/KD_WFLW_samples-1.jpg ADDED

Git LFS Details

  • SHA256: 10d58580fac95165ebe24e066d141269c309983676d30026f1cec765fda8a9f9
  • Pointer size: 131 Bytes
  • Size of remote file: 473 kB
samples/KD_cofw_samples-1.jpg ADDED

Git LFS Details

  • SHA256: 999aefe551102f69dfcf769a3bf0657308c9f3e8d15bc7a3c30ed63aba09f568
  • Pointer size: 131 Bytes
  • Size of remote file: 439 kB
samples/general_framework-1.jpg ADDED

Git LFS Details

  • SHA256: 8a5713e31eae394da258e736885dc0493069e1619f038b80b433b44f18a561eb
  • Pointer size: 130 Bytes
  • Size of remote file: 70.6 kB
samples/loss_weight-1.jpg ADDED

Git LFS Details

  • SHA256: 6c5e0e62b61552291fbdb555fd15e79ebed824538c0e2fe49d4d18931d4c6d46
  • Pointer size: 131 Bytes
  • Size of remote file: 727 kB
samples/main_loss-1.jpg ADDED

Git LFS Details

  • SHA256: 6436e81a6939bdeae249e900cf211c0e81955a2ba5d88cf68c91cc0f2104d36e
  • Pointer size: 131 Bytes
  • Size of remote file: 639 kB
samples/teacher_arch-1.jpg ADDED

Git LFS Details

  • SHA256: 47436c9ccf7b7506af6d3f1fff6714010d336a90dd7dd6cefe8312a1f6149a9e
  • Pointer size: 130 Bytes
  • Size of remote file: 54.4 kB
student_train.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from configuration import DatasetName, DatasetType, \
2
+ AffectnetConf, D300wConf, W300Conf, InputDataSize, LearningConfig, CofwConf, WflwConf
3
+ from tf_record_utility import TFRecordUtility
4
+ from clr_callback import CyclicLR
5
+ from cnn_model import CNNModel
6
+ from custom_Losses import Custom_losses
7
+ from Data_custom_generator import CustomHeatmapGenerator
8
+ from PW_Data_custom_generator import PWCustomHeatmapGenerator
9
+ from image_utility import ImageUtility
10
+ import img_printer as imgpr
11
+
12
+ import tensorflow as tf
13
+ import numpy as np
14
+ import matplotlib.pyplot as plt
15
+ import math
16
+ from datetime import datetime
17
+ from sklearn.utils import shuffle
18
+ from sklearn.model_selection import train_test_split
19
+ from numpy import save, load, asarray
20
+ import csv
21
+ from skimage.io import imread
22
+ import pickle
23
+
24
+
25
+ # tf.compat.v1.enable_eager_execution()
26
+
27
+
28
+ class StudentTrainer:
29
+
30
+ def __init__(self, dataset_name, use_augmneted):
31
+ self.dataset_name = dataset_name
32
+
33
+ if dataset_name == DatasetName.w300:
34
+ self.num_landmark = D300wConf.num_of_landmarks * 2
35
+ if use_augmneted:
36
+ self.img_path = D300wConf.augmented_train_image
37
+ self.annotation_path = D300wConf.augmented_train_annotation
38
+ else:
39
+ self.img_path = D300wConf.no_aug_train_image
40
+ self.annotation_path = D300wConf.no_aug_train_annotation
41
+
42
+ if dataset_name == DatasetName.cofw:
43
+ self.num_landmark = CofwConf.num_of_landmarks * 2
44
+ self.img_path = CofwConf.augmented_train_image
45
+ self.annotation_path = CofwConf.augmented_train_annotation
46
+
47
+ if dataset_name == DatasetName.wflw:
48
+ self.num_landmark = WflwConf.num_of_landmarks * 2
49
+ if use_augmneted:
50
+ self.img_path = WflwConf.augmented_train_image
51
+ self.annotation_path = WflwConf.augmented_train_annotation
52
+ else:
53
+ self.img_path = WflwConf.no_aug_train_image
54
+ self.annotation_path = WflwConf.no_aug_train_annotation
55
+
56
+ def train(self, arch_student, weight_path_student, loss_weight_student,
57
+ arch_tough_teacher, weight_path_tough_teacher, loss_weight_tough_teacher,
58
+ arch_tol_teacher, weight_path_tol_teacher, loss_weight_tol_teacher):
59
+ """"""
60
+ '''create loss'''
61
+ c_loss = Custom_losses()
62
+
63
+ '''create summary writer'''
64
+ summary_writer = tf.summary.create_file_writer(
65
+ "./train_logs/fit/" + datetime.now().strftime("%Y%m%d-%H%M%S"))
66
+
67
+ '''making models'''
68
+ model_student = self.make_model(arch=arch_student, w_path=weight_path_student, is_old=False)
69
+ model_tough_teacher = self.make_model(arch=arch_tough_teacher, w_path=weight_path_tough_teacher)
70
+ model_tol_teacher = self.make_model(arch=arch_tol_teacher, w_path=weight_path_tol_teacher)
71
+
72
+ '''create optimizer'''
73
+ _lr = 1e-3
74
+ optimizer_student = self._get_optimizer(lr=_lr)
75
+
76
+ '''create sample generator'''
77
+ x_train_filenames, x_val_filenames, y_train_filenames, y_val_filenames = self._create_generators()
78
+ # x_train_filenames, y_train_filenames = self._create_generators()
79
+
80
+ '''create train configuration'''
81
+ step_per_epoch = len(x_train_filenames) // LearningConfig.batch_size
82
+
83
+ '''start train:'''
84
+ for epoch in range(LearningConfig.epochs):
85
+ x_train_filenames, y_train_filenames = self._shuffle_data(x_train_filenames, y_train_filenames)
86
+ for batch_index in range(step_per_epoch):
87
+ '''load annotation and images'''
88
+ images, annotation_gr, annotation_tough_teacher, annotation_tol_teacher, annotation_student = self._get_batch_sample(
89
+ batch_index=batch_index, x_train_filenames=x_train_filenames,
90
+ y_train_filenames=y_train_filenames, model_tough_t=model_tough_teacher,
91
+ model_tol_t=model_tol_teacher, model_student=model_student)
92
+ '''convert to tensor'''
93
+ images = tf.cast(images, tf.float32)
94
+ annotation_gr = tf.cast(annotation_gr, tf.float32)
95
+ annotation_tough_teacher = tf.cast(annotation_tough_teacher, tf.float32)
96
+ annotation_tol_teacher = tf.cast(annotation_tol_teacher, tf.float32)
97
+
98
+ '''train step'''
99
+ self.train_step(epoch=epoch, step=batch_index, total_steps=step_per_epoch, images=images,
100
+ model_student=model_student,
101
+ annotation_gr=annotation_gr, annotation_tough_teacher=annotation_tough_teacher,
102
+ annotation_tol_teacher=annotation_tol_teacher,
103
+ l_w_stu=loss_weight_student, l_w_togh_t=loss_weight_tough_teacher,
104
+ loss_w_tol_t=loss_weight_tol_teacher,
105
+ optimizer=optimizer_student, summary_writer=summary_writer, c_loss=c_loss)
106
+ '''evaluating part'''
107
+ img_batch_eval, pn_batch_eval = self._create_evaluation_batch(x_val_filenames, y_val_filenames)
108
+ # loss_eval, loss_eval_tol_dif_stu, loss_eval_tol_dif_gt, loss_eval_tou_dif_stu, loss_eval_tou_dif_gt = \
109
+ loss_eval = self._eval_model(img_batch_eval, pn_batch_eval, model_student)
110
+ with summary_writer.as_default():
111
+ tf.summary.scalar('Eval-LOSS', loss_eval, step=epoch)
112
+ # tf.summary.scalar('Eval-loss_eval_tol_dif_stu', loss_eval_tol_dif_stu, step=epoch)
113
+ # tf.summary.scalar('Eval-loss_eval_tol_dif_gt', loss_eval_tol_dif_gt, step=epoch)
114
+ # tf.summary.scalar('Eval-loss_eval_tou_dif_stu', loss_eval_tou_dif_stu, step=epoch)
115
+ # tf.summary.scalar('Eval-loss_eval_tou_dif_gt', loss_eval_tou_dif_gt, step=epoch)
116
+ # model_student.save_weights('./models/stu_weight_' + '_' + str(epoch) + self.dataset_name + '_' + str(loss_eval) + '.h5')
117
+ '''save weights'''
118
+ model_student.save(
119
+ './models/stu_model_' + str(epoch) + '_' + self.dataset_name + '_' + str(loss_eval) + '.h5')
120
+ '''calculate Learning rate'''
121
+ # _lr = self._calc_learning_rate(iterations=epoch, step_size=20, base_lr=1e-5, max_lr=1e-1)
122
+ # optimizer_student = self._get_optimizer(lr=_lr)
123
+
124
+ def _calc_learning_rate(self, iterations, step_size, base_lr, max_lr):
125
+ cycle = np.floor(1 + iterations / (2 * step_size))
126
+ x = np.abs(iterations / step_size - 2 * cycle + 1)
127
+ lr = base_lr + (max_lr - base_lr) * np.maximum(0, (1 - x)) / float(2 ** (cycle - 1))
128
+ print('LR is: ' + str(lr))
129
+ return lr
130
+
131
+ # @tf.function
132
+ def train_step(self, epoch, step, total_steps, images, model_student, annotation_gr,
133
+ annotation_tough_teacher, annotation_tol_teacher, annotation_student,
134
+ l_w_stu, l_w_togh_t, loss_w_tol_t,
135
+ optimizer, summary_writer, c_loss, train_dif):
136
+ with tf.GradientTape() as tape_student:
137
+ '''create annotation_predicted'''
138
+ # annotation_predicted, pr_tol, pr_tol_dif_gt, pr_tou, pr_tou_dif_gt = model_student(
139
+ annotation_predicted = model_student(
140
+ images, training=True)
141
+ '''calculate loss'''
142
+ loss_total, loss_main, loss_tough_assist, loss_tol_assist = c_loss.kd_loss(x_pr=annotation_predicted,
143
+ x_gt=annotation_gr,
144
+ x_tough=annotation_tough_teacher,
145
+ x_tol=annotation_tol_teacher,
146
+ alpha_tough=1.9,
147
+ alpha_mi_tough=-0.45,
148
+ alpha_tol=1.8,
149
+ alpha_mi_tol=-0.4,
150
+ main_loss_weight=l_w_stu,
151
+ tough_loss_weight=l_w_togh_t,
152
+ tol_loss_weight=loss_w_tol_t)
153
+ '''calculate gradient'''
154
+ gradients_of_student = tape_student.gradient(loss_total, model_student.trainable_variables)
155
+ '''apply Gradients:'''
156
+ optimizer.apply_gradients(zip(gradients_of_student, model_student.trainable_variables))
157
+ '''printing loss Values: '''
158
+ tf.print("->EPOCH: ", str(epoch), "->STEP: ", str(step) + '/' + str(total_steps),
159
+ ' -> : LOSS: ', loss_total,
160
+ ' -> : loss_main: ', loss_main,
161
+ ' -> : loss_tough_assist: ', loss_tough_assist,
162
+ ' -> : loss_tol_assist: ', loss_tol_assist)
163
+ with summary_writer.as_default():
164
+ tf.summary.scalar('LOSS', loss_total, step=epoch)
165
+ tf.summary.scalar('loss_main', loss_main, step=epoch)
166
+ tf.summary.scalar('loss_tough_assist', loss_tough_assist, step=epoch)
167
+ tf.summary.scalar('loss_tol_assist', loss_tol_assist, step=epoch)
168
+
169
+ def make_model(self, arch, w_path, is_old=False):
170
+ cnn = CNNModel()
171
+ model = cnn.get_model(arch=arch, output_len=self.num_landmark, input_tensor=None, weight_path=w_path,
172
+ is_old=is_old)
173
+ if w_path is not None and arch != 'mobileNetV2_d' and not is_old:
174
+ model.load_weights(w_path)
175
+ # model.save('test_model'+arch+'.h5')
176
+ return model
177
+
178
+ def _eval_model(self, img_batch_eval, pn_batch_eval, model):
179
+ # annotation_predicted, pr_tol_dif_stu, pr_tol_dif_gt, pr_tou_dif_stu, pr_tou_dif_gt = model(img_batch_eval)
180
+ annotation_predicted = model(img_batch_eval)
181
+ loss_eval = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
182
+ # loss_eval_tol_dif_stu = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
183
+ # loss_eval_tol_dif_gt = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
184
+ # loss_eval_tou_dif_stu = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
185
+ # loss_eval_tou_dif_gt = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
186
+ # return loss_eval, loss_eval_tol_dif_stu, loss_eval_tol_dif_gt, loss_eval_tou_dif_stu, loss_eval_tou_dif_gt
187
+ return loss_eval
188
+
189
+ def _get_optimizer(self, lr=1e-2, beta_1=0.9, beta_2=0.999, decay=1e-4):
190
+ return tf.keras.optimizers.Adam(lr=lr, beta_1=beta_1, beta_2=beta_2, decay=decay)
191
+
192
+ def _shuffle_data(self, filenames, labels):
193
+ filenames_shuffled, y_labels_shuffled = shuffle(filenames, labels)
194
+ return filenames_shuffled, y_labels_shuffled
195
+
196
+ def _create_generators(self):
197
+ fn_prefix = './file_names/' + self.dataset_name + '_'
198
+ # x_trains_path = fn_prefix + 'x_train_fns.npy'
199
+ # x_validations_path = fn_prefix + 'x_val_fns.npy'
200
+
201
+ tf_utils = TFRecordUtility(number_of_landmark=self.num_landmark)
202
+
203
+ filenames, labels = tf_utils.create_image_and_labels_name(img_path=self.img_path,
204
+ annotation_path=self.annotation_path)
205
+ filenames_shuffled, y_labels_shuffled = shuffle(filenames, labels)
206
+ x_train_filenames, x_val_filenames, y_train, y_val = train_test_split(
207
+ filenames_shuffled, y_labels_shuffled, test_size=LearningConfig.batch_size, random_state=1)
208
+
209
+ # save(x_trains_path, filenames_shuffled)
210
+ # save(x_validations_path, y_labels_shuffled)
211
+
212
+ # save(x_trains_path, x_train_filenames)
213
+ # save(x_validations_path, x_val_filenames)
214
+ # save(y_trains_path, y_train)
215
+ # save(y_validations_path, y_val)
216
+
217
+ # return filenames_shuffled, y_labels_shuffled
218
+ return x_train_filenames, x_val_filenames, y_train, y_val
219
+
220
+ def _create_evaluation_batch(self, x_eval_filenames, y_eval_filenames):
221
+ img_path = self.img_path
222
+ pn_tr_path = self.annotation_path
223
+ '''create batch data and normalize images'''
224
+ batch_x = x_eval_filenames[0:LearningConfig.batch_size]
225
+ batch_y = y_eval_filenames[0:LearningConfig.batch_size]
226
+ '''create img and annotations'''
227
+ img_batch = np.array([imread(img_path + file_name) for file_name in batch_x]) / 255.0
228
+ if self.dataset_name == DatasetName.cofw: # this ds is not normalized
229
+ pn_batch = np.array([load(pn_tr_path + file_name) for file_name in batch_y])
230
+ else:
231
+ pn_batch = np.array([self._load_and_normalize(pn_tr_path + file_name) for file_name in batch_y])
232
+ return img_batch, pn_batch
233
+
234
+ def _get_batch_sample(self, batch_index, x_train_filenames, y_train_filenames, model_tough_t, model_tol_t, model_student,
235
+ train_dif):
236
+ img_path = self.img_path
237
+ pn_tr_path = self.annotation_path
238
+ '''create batch data and normalize images'''
239
+ batch_x = x_train_filenames[
240
+ batch_index * LearningConfig.batch_size:(batch_index + 1) * LearningConfig.batch_size]
241
+ batch_y = y_train_filenames[
242
+ batch_index * LearningConfig.batch_size:(batch_index + 1) * LearningConfig.batch_size]
243
+ img_batch = np.array([imread(img_path + file_name) for file_name in batch_x]) / 255.0
244
+ if self.dataset_name == DatasetName.cofw: # this ds is not normalized
245
+ pn_batch = np.array([load(pn_tr_path + file_name) for file_name in batch_y])
246
+ else:
247
+ pn_batch = np.array([self._load_and_normalize(pn_tr_path + file_name) for file_name in batch_y])
248
+ '''prediction to create tough and tolerant batches'''
249
+ pn_batch_tough = model_tough_t.predict_on_batch(img_batch)
250
+ if not train_dif:
251
+ pn_batch_tol = model_tol_t.predict_on_batch(img_batch)
252
+ pn_batch_stu = None
253
+ else:
254
+ pn_batch_tol = None
255
+ pn_batch_stu = model_tol_t.predict_on_batch(img_batch)
256
+
257
+
258
+ # pn_batch_tough = 0
259
+ # pn_batch_tol = 0
260
+
261
+ '''test: print'''
262
+ # image_utility = ImageUtility()
263
+ # if self.dataset_name == DatasetName.cofw: # this ds is not normalized
264
+ # gr_s, gr_px_1, gr_Py_1 = image_utility.create_landmarks(pn_batch[0])
265
+ # tou_s, tou_px_1, tou_Py_1 = image_utility.create_landmarks(pn_batch_tough[0])
266
+ # tol_s, tol_px_1, tol_Py_1 = image_utility.create_landmarks(pn_batch_tol[0])
267
+ # else:
268
+ # gr_s, gr_px_1, gr_Py_1 = image_utility.create_landmarks_from_normalized(pn_batch[0], 224, 224, 112, 112)
269
+ # tou_s, tou_px_1, tou_Py_1 = image_utility.create_landmarks_from_normalized(pn_batch_tough[0], 224, 224, 112, 112)
270
+ # tol_s, tol_px_1, tol_Py_1 = image_utility.create_landmarks_from_normalized(pn_batch_tol[0], 224, 224, 112, 112)
271
+ #
272
+ # imgpr.print_image_arr(str(batch_index)+'pts_gt', img_batch[0], gr_px_1, gr_Py_1)
273
+ # imgpr.print_image_arr(str(batch_index)+'pts_t100', img_batch[0], tou_px_1, tou_Py_1)
274
+ # imgpr.print_image_arr(str(batch_index)+'pts_t90', img_batch[0], tol_px_1, tol_Py_1)
275
+
276
+ return img_batch, pn_batch, pn_batch_tough, pn_batch_tol, pn_batch_stu
277
+
278
+ def _load_and_normalize(self, point_path):
279
+ annotation = load(point_path)
280
+
281
+ """for training we dont normalize COFW"""
282
+
283
+ '''normalize landmarks based on hyperface method'''
284
+ width = InputDataSize.image_input_size
285
+ height = InputDataSize.image_input_size
286
+ x_center = width / 2
287
+ y_center = height / 2
288
+ annotation_norm = []
289
+ for p in range(0, len(annotation), 2):
290
+ annotation_norm.append((x_center - annotation[p]) / width)
291
+ annotation_norm.append((y_center - annotation[p + 1]) / height)
292
+ return annotation_norm
teacher_trainer.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from configuration import DatasetName, DatasetType, \
2
+ AffectnetConf, D300wConf, W300Conf, InputDataSize, LearningConfig, CofwConf, WflwConf
3
+ from tf_record_utility import TFRecordUtility
4
+ from clr_callback import CyclicLR
5
+ from cnn_model import CNNModel
6
+ from custom_Losses import Custom_losses
7
+ from Data_custom_generator import CustomHeatmapGenerator
8
+ from PW_Data_custom_generator import PWCustomHeatmapGenerator
9
+ from image_utility import ImageUtility
10
+ import img_printer as imgpr
11
+
12
+ import tensorflow as tf
13
+ import numpy as np
14
+ import matplotlib.pyplot as plt
15
+ import math
16
+ from datetime import datetime
17
+ from sklearn.utils import shuffle
18
+ from sklearn.model_selection import train_test_split
19
+ from numpy import save, load, asarray
20
+ import csv
21
+ from skimage.io import imread
22
+ import pickle
23
+
24
+
25
+ # tf.compat.v1.enable_eager_execution()
26
+
27
+
28
+ class TeacherTrainer:
29
+
30
+ def __init__(self, dataset_name, use_augmneted):
31
+ self.dataset_name = dataset_name
32
+
33
+ if dataset_name == DatasetName.w300:
34
+ self.num_landmark = D300wConf.num_of_landmarks * 2
35
+ if use_augmneted:
36
+ self.img_path = D300wConf.augmented_train_image
37
+ self.annotation_path = D300wConf.augmented_train_annotation
38
+ else:
39
+ self.img_path = D300wConf.no_aug_train_image
40
+ self.annotation_path = D300wConf.no_aug_train_annotation
41
+
42
+ if dataset_name == DatasetName.cofw:
43
+ self.num_landmark = CofwConf.num_of_landmarks * 2
44
+ self.img_path = CofwConf.augmented_train_image
45
+ self.annotation_path = CofwConf.augmented_train_annotation
46
+
47
+ if dataset_name == DatasetName.wflw:
48
+ self.num_landmark = WflwConf.num_of_landmarks * 2
49
+ if use_augmneted:
50
+ self.img_path = WflwConf.augmented_train_image
51
+ self.annotation_path = WflwConf.augmented_train_annotation
52
+ else:
53
+ self.img_path = WflwConf.no_aug_train_image
54
+ self.annotation_path = WflwConf.no_aug_train_annotation
55
+
56
+ def train(self, arch, weight_path):
57
+ """"""
58
+ '''create loss'''
59
+ c_loss = Custom_losses()
60
+
61
+ '''create summary writer'''
62
+ summary_writer = tf.summary.create_file_writer(
63
+ "./train_logs/fit/" + datetime.now().strftime("%Y%m%d-%H%M%S"))
64
+
65
+ '''making models'''
66
+ model = self.make_model(arch=arch, w_path=weight_path, is_old=False)
67
+
68
+ '''create optimizer'''
69
+ _lr = 1e-3
70
+ optimizer_student = self._get_optimizer(lr=_lr)
71
+
72
+ '''create sample generator'''
73
+ x_train_filenames, x_val_filenames, y_train_filenames, y_val_filenames = self._create_generators()
74
+
75
+ '''create train configuration'''
76
+ step_per_epoch = len(x_train_filenames) // LearningConfig.batch_size
77
+
78
+ '''start train:'''
79
+ for epoch in range(LearningConfig.epochs):
80
+ x_train_filenames, y_train_filenames = self._shuffle_data(x_train_filenames, y_train_filenames)
81
+ for batch_index in range(step_per_epoch):
82
+ '''load annotation and images'''
83
+ images, annotation_gr = self._get_batch_sample(
84
+ batch_index=batch_index, x_train_filenames=x_train_filenames,
85
+ y_train_filenames=y_train_filenames, model=model)
86
+ '''convert to tensor'''
87
+ images = tf.cast(images, tf.float32)
88
+ annotation_gr = tf.cast(annotation_gr, tf.float32)
89
+
90
+ '''train step'''
91
+ self.train_step(epoch=epoch, step=batch_index, total_steps=step_per_epoch, images=images,
92
+ model=model,
93
+ annotation_gr=annotation_gr,
94
+ optimizer=optimizer_student,
95
+ summary_writer=summary_writer, c_loss=c_loss)
96
+ '''evaluating part'''
97
+ img_batch_eval, pn_batch_eval = self._create_evaluation_batch(x_val_filenames, y_val_filenames)
98
+ # loss_eval, loss_eval_tol_dif_stu, loss_eval_tol_dif_gt, loss_eval_tou_dif_stu, loss_eval_tou_dif_gt = \
99
+ loss_eval = self._eval_model(img_batch_eval, pn_batch_eval, model)
100
+ with summary_writer.as_default():
101
+ tf.summary.scalar('Eval-LOSS', loss_eval, step=epoch)
102
+ '''save weights'''
103
+ model.save(
104
+ './models/teacher_model_' + str(epoch) + '_' + self.dataset_name + '_' + str(loss_eval) + '.h5')
105
+
106
+ # @tf.function
107
+ def train_step(self, epoch, step, total_steps, images,
108
+ model, annotation_gr,
109
+ optimizer, summary_writer, c_loss):
110
+ with tf.GradientTape() as tape:
111
+ '''create annotation_predicted'''
112
+ annotation_predicted = model(images, training=True)
113
+ '''calculate loss'''
114
+ loss = c_loss.MSE(x_pr=annotation_predicted, x_gt=annotation_gr)
115
+ '''calculate gradient'''
116
+ gradients = tape.gradient(loss, model.trainable_variables)
117
+ '''apply Gradients:'''
118
+ optimizer.apply_gradients(zip(gradients, model.trainable_variables))
119
+ '''printing loss Values: '''
120
+ tf.print("->EPOCH: ", str(epoch), "->STEP: ", str(step) + '/' + str(total_steps), ' -> : LOSS: ', loss)
121
+ with summary_writer.as_default():
122
+ tf.summary.scalar('LOSS', loss, step=epoch)
123
+
124
+ def make_model(self, arch, w_path, is_old=False):
125
+ cnn = CNNModel()
126
+ model = cnn.get_model(arch=arch, output_len=self.num_landmark, input_tensor=None, weight_path=w_path,
127
+ is_old=is_old)
128
+ if w_path is not None and arch != 'mobileNetV2_d' and not is_old:
129
+ model.load_weights(w_path)
130
+ return model
131
+
132
+ def _eval_model(self, img_batch_eval, pn_batch_eval, model):
133
+ # annotation_predicted, pr_tol_dif_stu, pr_tol_dif_gt, pr_tou_dif_stu, pr_tou_dif_gt = model(img_batch_eval)
134
+ annotation_predicted = model(img_batch_eval)
135
+ loss_eval = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
136
+ # loss_eval_tol_dif_stu = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
137
+ # loss_eval_tol_dif_gt = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
138
+ # loss_eval_tou_dif_stu = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
139
+ # loss_eval_tou_dif_gt = np.array(tf.reduce_mean(tf.abs(pn_batch_eval - annotation_predicted)))
140
+ # return loss_eval, loss_eval_tol_dif_stu, loss_eval_tol_dif_gt, loss_eval_tou_dif_stu, loss_eval_tou_dif_gt
141
+ return loss_eval
142
+
143
+ def _get_optimizer(self, lr=1e-2, beta_1=0.9, beta_2=0.999, decay=1e-4):
144
+ return tf.keras.optimizers.Adam(lr=lr, beta_1=beta_1, beta_2=beta_2, decay=decay)
145
+
146
+ def _shuffle_data(self, filenames, labels):
147
+ filenames_shuffled, y_labels_shuffled = shuffle(filenames, labels)
148
+ return filenames_shuffled, y_labels_shuffled
149
+
150
+ def _create_generators(self):
151
+ fn_prefix = './file_names/' + self.dataset_name + '_'
152
+ # x_trains_path = fn_prefix + 'x_train_fns.npy'
153
+ # x_validations_path = fn_prefix + 'x_val_fns.npy'
154
+
155
+ tf_utils = TFRecordUtility(number_of_landmark=self.num_landmark)
156
+
157
+ filenames, labels = tf_utils.create_image_and_labels_name(img_path=self.img_path,
158
+ annotation_path=self.annotation_path)
159
+ filenames_shuffled, y_labels_shuffled = shuffle(filenames, labels)
160
+ x_train_filenames, x_val_filenames, y_train, y_val = train_test_split(
161
+ filenames_shuffled, y_labels_shuffled, test_size=LearningConfig.batch_size, random_state=1)
162
+
163
+ # save(x_trains_path, filenames_shuffled)
164
+ # save(x_validations_path, y_labels_shuffled)
165
+
166
+ # save(x_trains_path, x_train_filenames)
167
+ # save(x_validations_path, x_val_filenames)
168
+ # save(y_trains_path, y_train)
169
+ # save(y_validations_path, y_val)
170
+
171
+ # return filenames_shuffled, y_labels_shuffled
172
+ return x_train_filenames, x_val_filenames, y_train, y_val
173
+
174
+ def _create_evaluation_batch(self, x_eval_filenames, y_eval_filenames):
175
+ img_path = self.img_path
176
+ pn_tr_path = self.annotation_path
177
+ '''create batch data and normalize images'''
178
+ batch_x = x_eval_filenames[0:LearningConfig.batch_size]
179
+ batch_y = y_eval_filenames[0:LearningConfig.batch_size]
180
+ '''create img and annotations'''
181
+ img_batch = np.array([imread(img_path + file_name) for file_name in batch_x]) / 255.0
182
+ if self.dataset_name == DatasetName.cofw: # this ds is not normalized
183
+ pn_batch = np.array([load(pn_tr_path + file_name) for file_name in batch_y])
184
+ else:
185
+ pn_batch = np.array([self._load_and_normalize(pn_tr_path + file_name) for file_name in batch_y])
186
+ return img_batch, pn_batch
187
+
188
+ def _get_batch_sample(self, batch_index, x_train_filenames, y_train_filenames):
189
+ img_path = self.img_path
190
+ pn_tr_path = self.annotation_path
191
+ '''create batch data and normalize images'''
192
+ batch_x = x_train_filenames[
193
+ batch_index * LearningConfig.batch_size:(batch_index + 1) * LearningConfig.batch_size]
194
+ batch_y = y_train_filenames[
195
+ batch_index * LearningConfig.batch_size:(batch_index + 1) * LearningConfig.batch_size]
196
+ img_batch = np.array([imread(img_path + file_name) for file_name in batch_x]) / 255.0
197
+ pn_batch = np.array([self._load_and_normalize(pn_tr_path + file_name) for file_name in batch_y])
198
+
199
+ return img_batch, pn_batch
200
+
201
+ def _load_and_normalize(self, point_path):
202
+ annotation = load(point_path)
203
+
204
+ """for training we dont normalize COFW"""
205
+
206
+ '''normalize landmarks based on hyperface method'''
207
+ width = InputDataSize.image_input_size
208
+ height = InputDataSize.image_input_size
209
+ x_center = width / 2
210
+ y_center = height / 2
211
+ annotation_norm = []
212
+ for p in range(0, len(annotation), 2):
213
+ annotation_norm.append((x_center - annotation[p]) / width)
214
+ annotation_norm.append((y_center - annotation[p + 1]) / height)
215
+ return annotation_norm
test.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from configuration import DatasetName, DatasetType, \
2
+ AffectnetConf, D300wConf, W300Conf, InputDataSize, LearningConfig, CofwConf, WflwConf
3
+ from tf_record_utility import TFRecordUtility
4
+ from image_utility import ImageUtility
5
+ from skimage.transform import resize
6
+ import numpy as np
7
+ import math
8
+
9
+ import cv2
10
+ import os.path
11
+ import scipy.io as sio
12
+ from cnn_model import CNNModel
13
+ import img_printer as imgpr
14
+ from tqdm import tqdm
15
+
16
+
17
+ class Test:
18
+ def __init__(self, dataset_name, arch, num_output_layers, weight_fname, has_pose=False):
19
+ self.dataset_name = dataset_name
20
+ self.has_pose = has_pose
21
+
22
+ if dataset_name == DatasetName.w300:
23
+ self.output_len = D300wConf.num_of_landmarks * 2
24
+ elif dataset_name == DatasetName.cofw:
25
+ self.output_len = CofwConf.num_of_landmarks * 2
26
+ elif dataset_name == DatasetName.wflw:
27
+ self.output_len = WflwConf.num_of_landmarks * 2
28
+
29
+ cnn = CNNModel()
30
+ model = cnn.get_model(arch=arch, input_tensor=None, output_len=self.output_len)
31
+
32
+ model.load_weights(weight_fname)
33
+
34
+ img = None # load a cropped image
35
+
36
+ image_utility = ImageUtility()
37
+ pose_predicted = []
38
+ image = np.expand_dims(img, axis=0)
39
+
40
+ pose_predicted = model.predict(image)[1][0]