daliprf
commited on
Commit
•
ad1e0a0
1
Parent(s):
1a8030f
init
Browse files- .gitattributes +1 -0
- LICENSE +21 -0
- README.md +77 -3
- acr_loss.py +22 -0
- cnn.py +104 -0
- config.py +44 -0
- data_util.py +52 -0
- img/ACR_300w_samples.png +3 -0
- main.py +24 -0
- pca_utilities.py +76 -0
- pre_trained_models/300w/EF_0/300w_EF0_ACRLoss.h5 +3 -0
- pre_trained_models/300w/EF_0/300w_EF0_base.h5 +3 -0
- pre_trained_models/300w/EF_3/300w_EF3_ACRLoss.h5 +3 -0
- pre_trained_models/300w/EF_3/300w_EF3_base.h5 +3 -0
- pre_trained_models/300w/mnv2/300w_mnv2_ACRLoss.h5 +3 -0
- pre_trained_models/300w/mnv2/300w_mnv2_base.h5 +3 -0
- pre_trained_models/cofw/EF_0/cofw_EF0_base.h5 +3 -0
- pre_trained_models/cofw/EF_0/cofw_EF0_xxxLoss.h5 +3 -0
- pre_trained_models/cofw/EF_3/cofw_EF3_base.h5 +3 -0
- pre_trained_models/cofw/EF_3/cofw_EF3_xxxLoss.h5 +3 -0
- pre_trained_models/cofw/mnv2/cofw_mnv2_base.h5 +3 -0
- pre_trained_models/cofw/mnv2/cofw_mnv2_xxxLoss.h5 +3 -0
- requirements.txt +18 -0
- test.py +37 -0
- train.py +218 -0
.gitattributes
CHANGED
@@ -29,3 +29,4 @@ 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 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2022 Ali Pourramezan Fard
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
CHANGED
@@ -1,3 +1,77 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# [ACR-Loss](https://scholar.google.com/citations?view_op=view_citation&hl=en&user=96lS6HIAAAAJ&citation_for_view=96lS6HIAAAAJ:eQOLeE2rZwMC)
|
2 |
+
|
3 |
+
### Accepted in ICPR 2022
|
4 |
+
ACR Loss: Adaptive Coordinate-based Regression Loss for Face Alignment
|
5 |
+
|
6 |
+
#### Link to the paper:
|
7 |
+
https://arxiv.org/pdf/2203.15835.pdf
|
8 |
+
|
9 |
+
|
10 |
+
```diff
|
11 |
+
@@plaese STAR the repo if you like it.@@
|
12 |
+
```
|
13 |
+
|
14 |
+
```
|
15 |
+
Please cite this work as:
|
16 |
+
|
17 |
+
@article{fard2022acr,
|
18 |
+
title={ACR Loss: Adaptive Coordinate-based Regression Loss for Face Alignment},
|
19 |
+
author={Fard, Ali Pourramezan and Mahoor, Mohammah H},
|
20 |
+
journal={arXiv preprint arXiv:2203.15835},
|
21 |
+
year={2022}
|
22 |
+
}
|
23 |
+
```
|
24 |
+
|
25 |
+
![Samples](https://github.com/aliprf/ACR-Loss/blob/main/img/ACR_300w_samples.png?raw=true)
|
26 |
+
|
27 |
+
## Introduction
|
28 |
+
|
29 |
+
Although deep neural networks have achieved reasonable accuracy in solving face alignment, it is still a challenging task, specifically when we deal with facial images, under occlusion, or extreme head poses. Heatmap-based Regression (HBR) and Coordinate-based Regression (CBR) are among the two mainly used methods for face alignment. CBR methods require less computer memory, though their performance is less than HBR methods. In this paper, we propose an Adaptive Coordinatebased Regression (ACR) loss to improve the accuracy of CBR for face alignment. Inspired by the Active Shape Model (ASM), we generate Smooth-Face objects, a set of facial landmark points with less variations compared to the ground truth landmark points. We then introduce a method to estimate the level of difficulty in predicting each landmark point for the network by comparing the distribution of the ground truth landmark points
|
30 |
+
and the corresponding Smooth-Face objects. Our proposed ACR Loss can adaptively modify its curvature and the influence of the loss based on the difficulty level of predicting each landmark point in a face. Accordingly, the ACR Loss guides the network toward challenging points than easier points, which improves the accuracy of the face alignment task. Our extensive evaluation shows the capabilities of the proposed ACR Loss in predicting facial landmark points in various facial images.
|
31 |
+
|
32 |
+
We evaluated our ACR Loss using MobileNetV2, EfficientNetB0, and EfficientNet-B3 on widely used 300W, and COFW datasets and showed that the performance of face alignment using the ACR Loss is much better than the widely-used L2 loss. Moreover, on the COFW dataset, we achieved state-of-theart accuracy. In addition, on 300W the ACR Loss performance is comparable to the state-of-the-art methods. We also compared the performance of MobileNetV2 trained using the ACR Loss with the lightweight state-of-the-art methods, and we achieved the best accuracy, highlighting the effectiveness of our ACR Loss for face alignment specifically for the lightweight models.
|
33 |
+
|
34 |
+
|
35 |
+
----------------------------------------------------------------------------------------------------------------------------------
|
36 |
+
## Installing the requirements
|
37 |
+
In order to run the code you need to install python >= 3.5.
|
38 |
+
The requirements and the libraries needed to run the code can be installed using the following command:
|
39 |
+
|
40 |
+
```
|
41 |
+
pip install -r requirements.txt
|
42 |
+
```
|
43 |
+
|
44 |
+
|
45 |
+
## Using the pre-trained models
|
46 |
+
You can test and use the preetrained models using the following codes:
|
47 |
+
```
|
48 |
+
tester = Test()
|
49 |
+
tester.test_model(ds_name=DatasetName.w300,
|
50 |
+
pretrained_model_path='./pre_trained_models/ACRLoss/300w/EF_3/300w_EF3_ACRLoss.h5')
|
51 |
+
|
52 |
+
```
|
53 |
+
|
54 |
+
|
55 |
+
## Training Network from scratch
|
56 |
+
|
57 |
+
|
58 |
+
### Preparing Data
|
59 |
+
Data needs to be normalized and saved in npy format.
|
60 |
+
|
61 |
+
### PCA creation
|
62 |
+
you can you the pca_utility.py class to create the eigenvalues, eigenvectors, and the meanvector:
|
63 |
+
```
|
64 |
+
pca_calc = PCAUtility()
|
65 |
+
pca_calc.create_pca_from_npy(dataset_name=DatasetName.w300,
|
66 |
+
labels_npy_path='./data/w300/normalized_labels/',
|
67 |
+
pca_percentages=90)
|
68 |
+
|
69 |
+
```
|
70 |
+
### Training
|
71 |
+
The training implementation is located in train.py class. You can use the following code to start the training:
|
72 |
+
|
73 |
+
```
|
74 |
+
trainer = Train(arch=ModelArch.MNV2,
|
75 |
+
dataset_name=DatasetName.w300,
|
76 |
+
save_path='./')
|
77 |
+
```
|
acr_loss.py
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from config import DatasetName
|
2 |
+
import tensorflow as tf
|
3 |
+
|
4 |
+
|
5 |
+
class ACRLoss:
|
6 |
+
def acr_loss(self, x_pr, x_gt, phi, lambda_weight, ds_name):
|
7 |
+
low_map = tf.cast(tf.abs(x_pr - x_gt) <= 1.0, dtype=tf.float32)
|
8 |
+
high_map = tf.cast(tf.abs(x_pr - x_gt) > 1.0, dtype=tf.float32)
|
9 |
+
|
10 |
+
'''Big errors'''
|
11 |
+
ln_2 = tf.ones_like(x_pr, dtype=tf.float32) * tf.math.log(2.0)
|
12 |
+
C = tf.cast(tf.cast(phi, dtype=tf.double) * tf.cast(ln_2, dtype=tf.double) - 1.0, dtype=tf.float32)
|
13 |
+
loss_high = 100 * tf.reduce_mean(tf.math.multiply(high_map, (tf.square(x_pr - x_gt) + C)))
|
14 |
+
|
15 |
+
'''Small errors'''
|
16 |
+
power = tf.cast(2.0 - phi, tf.dtypes.float32)
|
17 |
+
ll = tf.pow(tf.abs(x_pr - x_gt), power)
|
18 |
+
loss_low = 100 * tf.reduce_mean(tf.math.multiply(low_map, (lambda_weight * tf.math.log(1.0 + ll))))
|
19 |
+
|
20 |
+
loss_total = loss_low + loss_high
|
21 |
+
|
22 |
+
return loss_total, loss_low, loss_high
|
cnn.py
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from config import DatasetName, DatasetType, W300Conf, InputDataSize, LearningConfig
|
2 |
+
import tensorflow as tf
|
3 |
+
from tensorflow import keras
|
4 |
+
from skimage.transform import resize
|
5 |
+
from keras.regularizers import l2, l1
|
6 |
+
from keras.models import Model
|
7 |
+
from keras.applications import mobilenet_v2, mobilenet, densenet
|
8 |
+
from keras.layers import Dense, MaxPooling2D, Conv2D, Flatten, \
|
9 |
+
BatchNormalization, Activation, GlobalAveragePooling2D, DepthwiseConv2D, Dropout, ReLU, Concatenate, Input, \
|
10 |
+
GlobalMaxPool2D
|
11 |
+
import efficientnet.tfkeras as efn
|
12 |
+
|
13 |
+
|
14 |
+
class CNNModel:
|
15 |
+
def get_model(self, arch, output_len):
|
16 |
+
|
17 |
+
if arch == 'mobileNetV2':
|
18 |
+
model = self.create_MobileNet(inp_shape=[224, 224, 3], output_len=output_len)
|
19 |
+
|
20 |
+
elif arch == 'efNb0':
|
21 |
+
model = self.create_efficientNet_b0(inp_shape=[224, 224, 3], input_tensor=None, output_len=output_len)
|
22 |
+
|
23 |
+
elif arch == 'efNb3':
|
24 |
+
model = self.create_efficientNet_b3(inp_shape=[224, 224, 3], input_tensor=None, output_len=output_len)
|
25 |
+
|
26 |
+
return
|
27 |
+
|
28 |
+
def create_MobileNet(self, inp_shape, output_len):
|
29 |
+
mobilenet_model = mobilenet_v2.MobileNetV2(input_shape=inp_shape,
|
30 |
+
alpha=1.0,
|
31 |
+
include_top=True,
|
32 |
+
weights=None,
|
33 |
+
input_tensor=None,
|
34 |
+
pooling=None)
|
35 |
+
mobilenet_model.layers.pop()
|
36 |
+
|
37 |
+
x = mobilenet_model.get_layer('global_average_pooling2d').output # 1280
|
38 |
+
x = Dropout(0.1)(x)
|
39 |
+
out_landmarks = Dense(output_len, activation=keras.activations.linear, kernel_initializer=initializer,
|
40 |
+
use_bias=True, name='O_L')(x)
|
41 |
+
inp = mobilenet_model.input
|
42 |
+
|
43 |
+
revised_model = Model(inp, [out_landmarks])
|
44 |
+
|
45 |
+
revised_model.summary()
|
46 |
+
model_json = revised_model.to_json()
|
47 |
+
|
48 |
+
with open("mobileNet_v2.json", "w") as json_file:
|
49 |
+
json_file.write(model_json)
|
50 |
+
|
51 |
+
return revised_model
|
52 |
+
|
53 |
+
def create_efficientNet_b0(self, inp_shape, input_tensor, output_len):
|
54 |
+
initializer = tf.keras.initializers.he_uniform()
|
55 |
+
|
56 |
+
eff_net = efn.EfficientNetB0(include_top=True,
|
57 |
+
weights=None,
|
58 |
+
input_tensor=input_tensor,
|
59 |
+
input_shape=inp_shape,
|
60 |
+
pooling=None)
|
61 |
+
eff_net.layers.pop()
|
62 |
+
inp = eff_net.input
|
63 |
+
|
64 |
+
x = eff_net.get_layer('top_activation').output
|
65 |
+
x = GlobalAveragePooling2D()(x)
|
66 |
+
x = keras.layers.Dropout(rate=0.5)(x)
|
67 |
+
output = Dense(output_len, activation='linear', use_bias=True, name='out',
|
68 |
+
kernel_initializer=initializer)(x)
|
69 |
+
|
70 |
+
eff_net = Model(inp, output)
|
71 |
+
eff_net.summary()
|
72 |
+
|
73 |
+
model_json = eff_net.to_json()
|
74 |
+
with open("eff_net_b0.json", "w") as json_file:
|
75 |
+
json_file.write(model_json)
|
76 |
+
|
77 |
+
return eff_net
|
78 |
+
|
79 |
+
def create_efficientNet_b3(self, inp_shape, input_tensor, output_len):
|
80 |
+
initializer = tf.keras.initializers.he_uniform()
|
81 |
+
|
82 |
+
eff_net = efn.EfficientNetB3(include_top=True,
|
83 |
+
weights=None,
|
84 |
+
input_tensor=input_tensor,
|
85 |
+
input_shape=inp_shape,
|
86 |
+
pooling=None)
|
87 |
+
eff_net.layers.pop()
|
88 |
+
inp = eff_net.input
|
89 |
+
|
90 |
+
x = eff_net.get_layer('top_activation').output
|
91 |
+
x = GlobalAveragePooling2D()(x)
|
92 |
+
x = keras.layers.Dropout(rate=0.5)(x)
|
93 |
+
|
94 |
+
output = Dense(output_len, activation='linear', use_bias=True, name='out',
|
95 |
+
kernel_initializer=initializer)(x)
|
96 |
+
|
97 |
+
eff_net = Model(inp, output)
|
98 |
+
eff_net.summary()
|
99 |
+
|
100 |
+
model_json = eff_net.to_json()
|
101 |
+
with open("eff_net_b3.json", "w") as json_file:
|
102 |
+
json_file.write(model_json)
|
103 |
+
|
104 |
+
return eff_net
|
config.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class DatasetName:
|
2 |
+
w300 = '300W'
|
3 |
+
cofw = 'COFW'
|
4 |
+
|
5 |
+
|
6 |
+
class ModelArch:
|
7 |
+
MNV2 = 'mobileNetV2'
|
8 |
+
EFNB0 = 'EfficientNet-B0'
|
9 |
+
EFNB3 = 'EfficientNet-B3'
|
10 |
+
|
11 |
+
|
12 |
+
class DatasetType:
|
13 |
+
data_type_train = 0
|
14 |
+
data_type_test = 1
|
15 |
+
|
16 |
+
|
17 |
+
class LearningConfig:
|
18 |
+
batch_size = 3
|
19 |
+
epochs = 150
|
20 |
+
|
21 |
+
|
22 |
+
class InputDataSize:
|
23 |
+
image_input_size = 224
|
24 |
+
|
25 |
+
|
26 |
+
class W300Conf:
|
27 |
+
W300W_prefix_path = './300W/'
|
28 |
+
|
29 |
+
train_annotation = W300W_prefix_path + 'train_set/annotations/'
|
30 |
+
train_image = W300W_prefix_path + 'train_set/images/'
|
31 |
+
|
32 |
+
test_annotation_path = W300W_prefix_path + 'test_set/annotations/'
|
33 |
+
test_image_path = W300W_prefix_path + 'test_set/images/'
|
34 |
+
num_of_landmarks = 68
|
35 |
+
|
36 |
+
class CofwConf:
|
37 |
+
cofw_prefix_path = './cofw/'
|
38 |
+
|
39 |
+
train_annotation = cofw_prefix_path + 'train_set/annotations/'
|
40 |
+
train_image = cofw_prefix_path + 'train_set/images/'
|
41 |
+
|
42 |
+
test_annotation_path = cofw_prefix_path + 'test_set/annotations/'
|
43 |
+
test_image_path = cofw_prefix_path + 'test_set/images/'
|
44 |
+
num_of_landmarks = 29
|
data_util.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pca_utilities import PCAUtility
|
2 |
+
from config import DatasetName, DatasetType, W300Conf, InputDataSize, LearningConfig, CofwConf
|
3 |
+
import tensorflow as tf
|
4 |
+
import numpy as np
|
5 |
+
import os
|
6 |
+
from skimage.transform import resize
|
7 |
+
import csv
|
8 |
+
import sys
|
9 |
+
from PIL import Image
|
10 |
+
from pathlib import Path
|
11 |
+
import sqlite3
|
12 |
+
import cv2
|
13 |
+
import os.path
|
14 |
+
from keras import backend as K
|
15 |
+
|
16 |
+
from scipy import misc
|
17 |
+
from scipy.ndimage import gaussian_filter, maximum_filter
|
18 |
+
from numpy import save, load, asarray
|
19 |
+
from tqdm import tqdm
|
20 |
+
import pickle
|
21 |
+
import PIL.ImageDraw as ImageDraw
|
22 |
+
import math
|
23 |
+
|
24 |
+
|
25 |
+
class DataUtil:
|
26 |
+
|
27 |
+
def __init__(self, number_of_landmark):
|
28 |
+
self.number_of_landmark = number_of_landmark
|
29 |
+
|
30 |
+
def get_asm(self, input, dataset_name, accuracy, alpha=1.0):
|
31 |
+
pca_utils = PCAUtility()
|
32 |
+
|
33 |
+
eigenvalues = load('pca_obj/' + dataset_name + pca_utils.eigenvalues_prefix + str(accuracy) + ".npy")
|
34 |
+
eigenvectors = load('pca_obj/' + dataset_name + pca_utils.eigenvectors_prefix + str(accuracy) + ".npy")
|
35 |
+
meanvector = load('pca_obj/' + dataset_name + pca_utils.meanvector_prefix + str(accuracy) + ".npy")
|
36 |
+
|
37 |
+
b_vector_p = pca_utils.calculate_b_vector(input, True, eigenvalues, eigenvectors, meanvector)
|
38 |
+
out = alpha * meanvector + np.dot(eigenvectors, b_vector_p)
|
39 |
+
return out
|
40 |
+
|
41 |
+
def create_image_and_labels_name(self, img_path, annotation_path):
|
42 |
+
img_filenames = []
|
43 |
+
lbls_filenames = []
|
44 |
+
|
45 |
+
for file in os.listdir(img_path):
|
46 |
+
if file.endswith(".jpg") or file.endswith(".png"):
|
47 |
+
lbl_file = str(file)[:-3] + "npy" # just name
|
48 |
+
if os.path.exists(annotation_path + lbl_file):
|
49 |
+
img_filenames.append(str(file))
|
50 |
+
lbls_filenames.append(lbl_file)
|
51 |
+
|
52 |
+
return np.array(img_filenames), np.array(lbls_filenames)
|
img/ACR_300w_samples.png
ADDED
Git LFS Details
|
main.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from train import Train
|
2 |
+
from test import Test
|
3 |
+
from config import DatasetName, ModelArch
|
4 |
+
from pca_utilities import PCAUtility
|
5 |
+
|
6 |
+
if __name__ == '__main__':
|
7 |
+
'''use the pretrained model'''
|
8 |
+
tester = Test()
|
9 |
+
tester.test_model(ds_name=DatasetName.w300,
|
10 |
+
pretrained_model_path='./pre_trained_models/ACRLoss/mnv2.h5')
|
11 |
+
|
12 |
+
'''training model from scratch'''
|
13 |
+
|
14 |
+
# pretrain prerequisites
|
15 |
+
# 1- PCA calculation:
|
16 |
+
pca_calc = PCAUtility()
|
17 |
+
pca_calc.create_pca_from_npy(dataset_name=DatasetName.w300,
|
18 |
+
labels_npy_path='./data/w300/normalized_labels/',
|
19 |
+
pca_percentages=90)
|
20 |
+
|
21 |
+
# Train:
|
22 |
+
trainer = Train(arch=ModelArch.MNV2,
|
23 |
+
dataset_name=DatasetName.w300,
|
24 |
+
save_path='./')
|
pca_utilities.py
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sklearn.decomposition import PCA, IncrementalPCA
|
2 |
+
from sklearn.decomposition import TruncatedSVD
|
3 |
+
import numpy as np
|
4 |
+
import pickle
|
5 |
+
import os
|
6 |
+
from tqdm import tqdm
|
7 |
+
from numpy import save, load
|
8 |
+
import math
|
9 |
+
from PIL import Image
|
10 |
+
from numpy import save, load
|
11 |
+
|
12 |
+
|
13 |
+
class PCAUtility:
|
14 |
+
eigenvalues_prefix = "_eigenvalues_"
|
15 |
+
eigenvectors_prefix = "_eigenvectors_"
|
16 |
+
meanvector_prefix = "_meanvector_"
|
17 |
+
|
18 |
+
def create_pca_from_npy(self, dataset_name, labels_npy_path, pca_percentages):
|
19 |
+
"""
|
20 |
+
generate and save eigenvalues, eigenvectors, meanvector
|
21 |
+
:param labels_npy_path: the path to the normalized labels that are save in npy format.
|
22 |
+
:param pca_percentages: % of eigenvalues that will be used
|
23 |
+
:return: generate
|
24 |
+
"""
|
25 |
+
path = labels_npy_path
|
26 |
+
print('PCA calculation started: loading labels')
|
27 |
+
|
28 |
+
lbl_arr = []
|
29 |
+
for file in tqdm(os.listdir(path)):
|
30 |
+
if file.endswith(".npy"):
|
31 |
+
npy_file = os.path.join(path, file)
|
32 |
+
lbl_arr.append(load(npy_file))
|
33 |
+
|
34 |
+
lbl_arr = np.array(lbl_arr)
|
35 |
+
|
36 |
+
reduced_lbl_arr, eigenvalues, eigenvectors = self._func_PCA(lbl_arr, pca_percentages)
|
37 |
+
mean_lbl_arr = np.mean(lbl_arr, axis=0)
|
38 |
+
eigenvectors = eigenvectors.T
|
39 |
+
|
40 |
+
save('./pca_obj/' + dataset_name + self.eigenvalues_prefix + str(pca_percentages), eigenvalues)
|
41 |
+
save('./pca_obj/' + dataset_name + self.eigenvectors_prefix + str(pca_percentages), eigenvectors)
|
42 |
+
save('./pca_obj/' + dataset_name + self.meanvector_prefix + str(pca_percentages), mean_lbl_arr)
|
43 |
+
|
44 |
+
def load_pca_obj(self, dataset_name, pca_percentages):
|
45 |
+
eigenvalues = np.load('./pca_obj/' + dataset_name + self.eigenvalues_prefix + str(pca_percentages))
|
46 |
+
eigenvectors = np.load('./pca_obj/' + dataset_name + self.eigenvectors_prefix + str(pca_percentages))
|
47 |
+
meanvector = np.load('./pca_obj/' + dataset_name + self.meanvector_prefix + str(pca_percentages))
|
48 |
+
return eigenvalues, eigenvectors, meanvector
|
49 |
+
|
50 |
+
def calculate_b_vector(self, predicted_vector, correction, eigenvalues, eigenvectors, meanvector):
|
51 |
+
tmp1 = predicted_vector - meanvector
|
52 |
+
b_vector = np.dot(eigenvectors.T, tmp1)
|
53 |
+
|
54 |
+
# put b in -3lambda =>
|
55 |
+
if correction:
|
56 |
+
i = 0
|
57 |
+
for b_item in b_vector:
|
58 |
+
lambda_i_sqr = 3 * math.sqrt(eigenvalues[i])
|
59 |
+
|
60 |
+
if b_item > 0:
|
61 |
+
b_item = min(b_item, lambda_i_sqr)
|
62 |
+
else:
|
63 |
+
b_item = max(b_item, -1 * lambda_i_sqr)
|
64 |
+
b_vector[i] = b_item
|
65 |
+
i += 1
|
66 |
+
|
67 |
+
return b_vector
|
68 |
+
|
69 |
+
def _func_PCA(self, input_data, pca_postfix):
|
70 |
+
input_data = np.array(input_data)
|
71 |
+
pca = PCA(n_components=pca_postfix / 100)
|
72 |
+
pca.fit(input_data)
|
73 |
+
pca_input_data = pca.transform(input_data)
|
74 |
+
eigenvalues = pca.explained_variance_
|
75 |
+
eigenvectors = pca.components_
|
76 |
+
return pca_input_data, eigenvalues, eigenvectors
|
pre_trained_models/300w/EF_0/300w_EF0_ACRLoss.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:937fc62cb7517a427905dc5e0c39711615269f481b080eb04b6bc30e2c22d7dd
|
3 |
+
size 17518832
|
pre_trained_models/300w/EF_0/300w_EF0_base.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:55a7836679d92933220c96e0b0563466aa4d24a4866b6ec3e0b5f44714a85e9a
|
3 |
+
size 17519192
|
pre_trained_models/300w/EF_3/300w_EF3_ACRLoss.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:abd7b1bc7e43cd4489fbde4f83a4dfb3722596f0dcd3e909256d4720af2b1920
|
3 |
+
size 44972528
|
pre_trained_models/300w/EF_3/300w_EF3_base.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f465c57e83bf4a8ec901baa0995b3d29a83fdf49af13432cdb7ccd99dccc2a53
|
3 |
+
size 44972528
|
pre_trained_models/300w/mnv2/300w_mnv2_ACRLoss.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:97eabc914cf24ffe6fd32442748c7c35d12297950feab8c20204b0a2b872d304
|
3 |
+
size 10197672
|
pre_trained_models/300w/mnv2/300w_mnv2_base.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e1ac8c5431a516e1d5218e7da3a86ebde77b71c6054d2654d76445d2e5f44ac0
|
3 |
+
size 10195168
|
pre_trained_models/cofw/EF_0/cofw_EF0_base.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:90283e68ac497f2baaec7c02c605be3bb8b9eb283fcd452e0c93c3c174c7a476
|
3 |
+
size 17119832
|
pre_trained_models/cofw/EF_0/cofw_EF0_xxxLoss.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:3c1ff7991fa3b0434e3b75ea32127d77a3bd4c0e3ba241aa54276eb3dff67460
|
3 |
+
size 17119832
|
pre_trained_models/cofw/EF_3/cofw_EF3_base.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:36f7006d1215031b737a8072b4c67685308203eea59d431f5073d7687903ea4f
|
3 |
+
size 44491224
|
pre_trained_models/cofw/EF_3/cofw_EF3_xxxLoss.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e495279c4c60de5ad07e1aa29db1a361c51029f260f0cf0ea0a0b73fb260c59e
|
3 |
+
size 44491224
|
pre_trained_models/cofw/mnv2/cofw_mnv2_base.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5002926568613fe55a3e005987e4f9e41ef9859f9dfae9c2a0e2b19bdbc300f6
|
3 |
+
size 9799400
|
pre_trained_models/cofw/mnv2/cofw_mnv2_xxxLoss.h5
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b4a2623fc19644604e401fc22c2104f1293645672464927fee72e37ad8609724
|
3 |
+
size 9800464
|
requirements.txt
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
numpy
|
2 |
+
|
3 |
+
# Mac M1:
|
4 |
+
tensorflow-macos
|
5 |
+
#linux
|
6 |
+
# tensorflow
|
7 |
+
|
8 |
+
keras
|
9 |
+
matplotlib
|
10 |
+
opencv-python
|
11 |
+
opencv-contrib-python
|
12 |
+
scipy
|
13 |
+
scikit-learn
|
14 |
+
scikit-image
|
15 |
+
Pillow
|
16 |
+
tqdm
|
17 |
+
efficientnet
|
18 |
+
tensorboard
|
test.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from config import DatasetName, W300Conf, DatasetType, LearningConfig, InputDataSize, CofwConf
|
2 |
+
import tensorflow as tf
|
3 |
+
|
4 |
+
import cv2
|
5 |
+
import os.path
|
6 |
+
import scipy.io as sio
|
7 |
+
from cnn import CNNModel
|
8 |
+
from tqdm import tqdm
|
9 |
+
import numpy as np
|
10 |
+
from os import listdir
|
11 |
+
from os.path import isfile, join
|
12 |
+
from scipy.integrate import simps
|
13 |
+
from scipy.integrate import trapz
|
14 |
+
import matplotlib.pyplot as plt
|
15 |
+
from skimage.io import imread
|
16 |
+
|
17 |
+
|
18 |
+
class Test:
|
19 |
+
def test_model(self, pretrained_model_path, ds_name):
|
20 |
+
if ds_name == DatasetName.w300:
|
21 |
+
test_annotation_path = W300Conf.test_annotation_path
|
22 |
+
test_image_path = W300Conf.test_image_path
|
23 |
+
elif ds_name == DatasetName.cofw:
|
24 |
+
test_annotation_path = CofwConf.test_annotation_path
|
25 |
+
test_image_path = CofwConf.test_image_path
|
26 |
+
|
27 |
+
model = tf.keras.models.load_model(pretrained_model_path)
|
28 |
+
|
29 |
+
for i, file in tqdm(enumerate(os.listdir(test_image_path))):
|
30 |
+
# load image and then normalize it
|
31 |
+
img = imread(test_image_path + file) / 255.0
|
32 |
+
|
33 |
+
# prediction
|
34 |
+
prediction = model.predict(np.expand_dims(img, axis=0))
|
35 |
+
|
36 |
+
# the first dimension is landmark point
|
37 |
+
landmark_predicted = prediction
|
train.py
ADDED
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from config import DatasetName, W300Conf, DatasetType, LearningConfig, InputDataSize, CofwConf
|
2 |
+
from cnn import CNNModel
|
3 |
+
import tensorflow as tf
|
4 |
+
from tensorflow import keras
|
5 |
+
import numpy as np
|
6 |
+
import matplotlib.pyplot as plt
|
7 |
+
import math
|
8 |
+
from datetime import datetime
|
9 |
+
from sklearn.utils import shuffle
|
10 |
+
from sklearn.model_selection import train_test_split
|
11 |
+
from numpy import save, load, asarray
|
12 |
+
import csv
|
13 |
+
from skimage.io import imread
|
14 |
+
import pickle
|
15 |
+
from tqdm import tqdm
|
16 |
+
import os
|
17 |
+
from data_util import DataUtil
|
18 |
+
from acr_loss import ACRLoss
|
19 |
+
|
20 |
+
|
21 |
+
class Train:
|
22 |
+
def __init__(self, arch, dataset_name, save_path, lambda_weight):
|
23 |
+
|
24 |
+
self.lambda_weight = lambda_weight
|
25 |
+
self.dataset_name = dataset_name
|
26 |
+
self.save_path = save_path
|
27 |
+
self.arch = arch
|
28 |
+
self.base_lr = 1e-3
|
29 |
+
self.max_lr = 5e-3
|
30 |
+
if dataset_name == DatasetName.w300:
|
31 |
+
self.num_landmark = W300Conf.num_of_landmarks * 2
|
32 |
+
self.img_path = W300Conf.train_image
|
33 |
+
self.annotation_path = W300Conf.train_annotation
|
34 |
+
'''evaluation path:'''
|
35 |
+
self.eval_img_path = W300Conf.test_image_path + 'challenging/'
|
36 |
+
self.eval_annotation_path = W300Conf.test_annotation_path + 'challenging/'
|
37 |
+
|
38 |
+
if dataset_name == DatasetName.cofw:
|
39 |
+
self.num_landmark = CofwConf.num_of_landmarks * 2
|
40 |
+
self.img_path = CofwConf.train_image
|
41 |
+
self.annotation_path = CofwConf.train_annotation
|
42 |
+
'''evaluation path:'''
|
43 |
+
self.eval_img_path = CofwConf.test_image_path
|
44 |
+
self.eval_annotation_path = CofwConf.test_annotation_path
|
45 |
+
|
46 |
+
def train(self, weight_path):
|
47 |
+
"""
|
48 |
+
:param weight_path:
|
49 |
+
:return:
|
50 |
+
"""
|
51 |
+
'''create loss'''
|
52 |
+
c_loss = ACRLoss()
|
53 |
+
|
54 |
+
'''create summary writer'''
|
55 |
+
summary_writer = tf.summary.create_file_writer(
|
56 |
+
"./train_logs/fit/" + datetime.now().strftime("%Y%m%d-%H%M%S"))
|
57 |
+
|
58 |
+
'''create sample generator'''
|
59 |
+
x_train_filenames, y_train_filenames = self._create_generators()
|
60 |
+
|
61 |
+
'''making models'''
|
62 |
+
model = self.make_model(arch=self.arch, w_path=weight_path)
|
63 |
+
|
64 |
+
'''create train configuration'''
|
65 |
+
step_per_epoch = len(x_train_filenames) // LearningConfig.batch_size
|
66 |
+
|
67 |
+
lr = 1e-3
|
68 |
+
for epoch in range(LearningConfig.epochs):
|
69 |
+
'''calculate Learning rate'''
|
70 |
+
optimizer = self._get_optimizer(lr=lr)
|
71 |
+
''''''
|
72 |
+
x_train_filenames, y_train_filenames = self._shuffle_data(x_train_filenames, y_train_filenames)
|
73 |
+
for batch_index in range(step_per_epoch):
|
74 |
+
'''load annotation and images'''
|
75 |
+
images, annotation_gr = self._get_batch_sample(
|
76 |
+
batch_index=batch_index, x_train_filenames=x_train_filenames,
|
77 |
+
y_train_filenames=y_train_filenames)
|
78 |
+
|
79 |
+
phi = self.calculate_adoptive_weight(epoch=epoch, batch_index=batch_index,
|
80 |
+
y_train_filenames=y_train_filenames,
|
81 |
+
weight_path=weight_path)
|
82 |
+
|
83 |
+
'''convert to tensor'''
|
84 |
+
images = tf.cast(images, tf.float32)
|
85 |
+
annotation_gr = tf.cast(annotation_gr, tf.float32)
|
86 |
+
'''train step'''
|
87 |
+
loss_total, loss_low, loss_high = self.train_step(
|
88 |
+
epoch=epoch, step=batch_index,
|
89 |
+
total_steps=step_per_epoch,
|
90 |
+
images=images,
|
91 |
+
model=model,
|
92 |
+
annotation_gr=annotation_gr,
|
93 |
+
phi=phi,
|
94 |
+
lambda_weight=self.lambda_weight,
|
95 |
+
optimizer=optimizer,
|
96 |
+
summary_writer=summary_writer, c_loss=c_loss)
|
97 |
+
|
98 |
+
with summary_writer.as_default():
|
99 |
+
tf.summary.scalar('loss_total', loss_total, step=epoch)
|
100 |
+
tf.summary.scalar('loss_low', loss_low, step=epoch)
|
101 |
+
tf.summary.scalar('loss_high', loss_high, step=epoch)
|
102 |
+
'''save weights'''
|
103 |
+
model.save(self.save_path + str(epoch) + '_' + self.dataset_name + '.h5')
|
104 |
+
|
105 |
+
# @tf.function
|
106 |
+
def train_step(self, epoch, step, total_steps, images, model, annotation_gr, phi,
|
107 |
+
optimizer, summary_writer, c_loss, lambda_weight):
|
108 |
+
with tf.GradientTape() as tape:
|
109 |
+
'''create annotation_predicted'''
|
110 |
+
annotation_predicted = model(images, training=True)
|
111 |
+
'''calculate loss'''
|
112 |
+
loss_total, loss_low, loss_high = c_loss.acr_loss(x_pr=annotation_predicted,
|
113 |
+
x_gt=annotation_gr,
|
114 |
+
phi=phi,
|
115 |
+
lambda_weight=lambda_weight,
|
116 |
+
ds_name=self.dataset_name)
|
117 |
+
'''calculate gradient'''
|
118 |
+
gradients_of_model = tape.gradient(loss_total, model.trainable_variables)
|
119 |
+
'''apply Gradients:'''
|
120 |
+
optimizer.apply_gradients(zip(gradients_of_model, model.trainable_variables))
|
121 |
+
'''printing loss Values: '''
|
122 |
+
tf.print("->EPOCH: ", str(epoch), "->STEP: ", str(step) + '/' + str(total_steps),
|
123 |
+
' -> : LOSS: ', loss_total,
|
124 |
+
' -> : loss_low: ', loss_low,
|
125 |
+
' -> : loss_high: ', loss_high
|
126 |
+
)
|
127 |
+
# print('==--==--==--==--==--==--==--==--==--')
|
128 |
+
with summary_writer.as_default():
|
129 |
+
tf.summary.scalar('loss_total', loss_total, step=epoch)
|
130 |
+
tf.summary.scalar('loss_low', loss_low, step=epoch)
|
131 |
+
tf.summary.scalar('loss_high', loss_high, step=epoch)
|
132 |
+
return loss_total, loss_low, loss_high
|
133 |
+
|
134 |
+
def calculate_adoptive_weight(self, epoch, batch_index, y_train_filenames, weight_path):
|
135 |
+
|
136 |
+
dt_utils = DataUtil(self.num_landmark)
|
137 |
+
batch_y = y_train_filenames[
|
138 |
+
batch_index * LearningConfig.batch_size:(batch_index + 1) * LearningConfig.batch_size]
|
139 |
+
asm_acc = None
|
140 |
+
if 0 <= epoch <= 15:
|
141 |
+
asm_acc = 80
|
142 |
+
elif 15 < epoch <= 30:
|
143 |
+
asm_acc = 85
|
144 |
+
elif 30 < epoch <= 70:
|
145 |
+
asm_acc = 90
|
146 |
+
elif 70 < epoch <= 100:
|
147 |
+
asm_acc = 95
|
148 |
+
|
149 |
+
pn_batch = np.array([self._load_and_normalize(self.annotation_path + file_name) for file_name in batch_y])
|
150 |
+
pn_batch_asm = np.array([dt_utils.get_asm(input=self._load_and_normalize(self.annotation_path + file_name),
|
151 |
+
dataset_name=self.dataset_name, accuracy=asm_acc)
|
152 |
+
for file_name in batch_y])
|
153 |
+
|
154 |
+
delta = np.array(abs(pn_batch - pn_batch_asm))
|
155 |
+
|
156 |
+
phi = np.array([delta[i] / np.max(delta[i]) for i in range(len(pn_batch))]) # bs * num_lnd
|
157 |
+
return phi
|
158 |
+
|
159 |
+
def _get_optimizer(self, lr=1e-1, beta_1=0.9, beta_2=0.999, decay=1e-5):
|
160 |
+
return tf.keras.optimizers.Adam(lr=lr, beta_1=beta_1, beta_2=beta_2, decay=decay)
|
161 |
+
|
162 |
+
def make_model(self, arch, w_path):
|
163 |
+
cnn = CNNModel()
|
164 |
+
model = cnn.get_model(arch=arch, output_len=self.num_landmark)
|
165 |
+
if w_path is not None:
|
166 |
+
model.load_weights(w_path)
|
167 |
+
return model
|
168 |
+
|
169 |
+
def _shuffle_data(self, filenames, labels):
|
170 |
+
filenames_shuffled, y_labels_shuffled = shuffle(filenames, labels)
|
171 |
+
return filenames_shuffled, y_labels_shuffled
|
172 |
+
|
173 |
+
def _create_generators(self, img_path=None, annotation_path=None):
|
174 |
+
tf_utils = DataUtil(number_of_landmark=self.num_landmark)
|
175 |
+
if img_path is None:
|
176 |
+
filenames, labels = tf_utils.create_image_and_labels_name(img_path=self.img_path,
|
177 |
+
annotation_path=self.annotation_path)
|
178 |
+
else:
|
179 |
+
filenames, labels = tf_utils.create_image_and_labels_name(img_path=img_path,
|
180 |
+
annotation_path=annotation_path)
|
181 |
+
filenames_shuffled, y_labels_shuffled = shuffle(filenames, labels)
|
182 |
+
return filenames_shuffled, y_labels_shuffled
|
183 |
+
|
184 |
+
def _get_batch_sample(self, batch_index, x_train_filenames, y_train_filenames, is_eval=False, batch_size=None):
|
185 |
+
if is_eval:
|
186 |
+
batch_x = x_train_filenames[
|
187 |
+
batch_index * batch_size:(batch_index + 1) * batch_size]
|
188 |
+
batch_y = y_train_filenames[
|
189 |
+
batch_index * batch_size:(batch_index + 1) * batch_size]
|
190 |
+
|
191 |
+
img_batch = np.array([imread(self.eval_img_path + file_name) for file_name in batch_x]) / 255.0
|
192 |
+
pn_batch = np.array([load(self.eval_annotation_path + file_name) for file_name in
|
193 |
+
batch_y])
|
194 |
+
else:
|
195 |
+
img_path = self.img_path
|
196 |
+
pn_tr_path = self.annotation_path
|
197 |
+
'''create batch data and normalize images'''
|
198 |
+
batch_x = x_train_filenames[
|
199 |
+
batch_index * LearningConfig.batch_size:(batch_index + 1) * LearningConfig.batch_size]
|
200 |
+
batch_y = y_train_filenames[
|
201 |
+
batch_index * LearningConfig.batch_size:(batch_index + 1) * LearningConfig.batch_size]
|
202 |
+
'''create img and annotations'''
|
203 |
+
img_batch = np.array([imread(img_path + file_name) for file_name in batch_x]) / 255.0
|
204 |
+
pn_batch = np.array([self._load_and_normalize(pn_tr_path + file_name) for file_name in batch_y])
|
205 |
+
|
206 |
+
return img_batch, pn_batch
|
207 |
+
|
208 |
+
def _load_and_normalize(self, point_path):
|
209 |
+
annotation = load(point_path)
|
210 |
+
width = InputDataSize.image_input_size
|
211 |
+
height = InputDataSize.image_input_size
|
212 |
+
x_center = width / 2
|
213 |
+
y_center = height / 2
|
214 |
+
annotation_norm = []
|
215 |
+
for p in range(0, len(annotation), 2):
|
216 |
+
annotation_norm.append((x_center - annotation[p]) / width)
|
217 |
+
annotation_norm.append((y_center - annotation[p + 1]) / height)
|
218 |
+
return annotation_norm
|