Spaces:
Runtime error
Runtime error
VladyslavTalakh
commited on
Commit
•
6f90f9d
1
Parent(s):
7b62d5f
examples
Browse files- .gitattributes +1 -0
- .idea/.gitignore +8 -0
- .idea/facial_beauty_analysis.iml +8 -0
- .idea/inspectionProfiles/profiles_settings.xml +6 -0
- .idea/misc.xml +7 -0
- .idea/modules.xml +8 -0
- .idea/vcs.xml +6 -0
- app.py +52 -4
- examples/example_1.jpg +0 -0
- examples/example_2.jpg +0 -0
- examples/example_3.jpeg +0 -0
- examples/example_4.png +3 -0
- examples/example_5.jpeg +0 -0
- examples/example_6.jpg +0 -0
- examples/img.png +3 -0
- facial_image.py +92 -0
- points.py +27 -0
- ratios.py +27 -0
- requirements.txt +0 -0
- svm_model.pkl +3 -0
- utils.py +55 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
.idea/.gitignore
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Default ignored files
|
2 |
+
/shelf/
|
3 |
+
/workspace.xml
|
4 |
+
# Editor-based HTTP Client requests
|
5 |
+
/httpRequests/
|
6 |
+
# Datasource local storage ignored files
|
7 |
+
/dataSources/
|
8 |
+
/dataSources.local.xml
|
.idea/facial_beauty_analysis.iml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<module type="PYTHON_MODULE" version="4">
|
3 |
+
<component name="NewModuleRootManager">
|
4 |
+
<content url="file://$MODULE_DIR$" />
|
5 |
+
<orderEntry type="inheritedJdk" />
|
6 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
7 |
+
</component>
|
8 |
+
</module>
|
.idea/inspectionProfiles/profiles_settings.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<settings>
|
3 |
+
<option name="USE_PROJECT_PROFILE" value="false" />
|
4 |
+
<version value="1.0" />
|
5 |
+
</settings>
|
6 |
+
</component>
|
.idea/misc.xml
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="Black">
|
4 |
+
<option name="sdkName" value="Python 3.11" />
|
5 |
+
</component>
|
6 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
7 |
+
</project>
|
.idea/modules.xml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="ProjectModuleManager">
|
4 |
+
<modules>
|
5 |
+
<module fileurl="file://$PROJECT_DIR$/.idea/facial_beauty_analysis.iml" filepath="$PROJECT_DIR$/.idea/facial_beauty_analysis.iml" />
|
6 |
+
</modules>
|
7 |
+
</component>
|
8 |
+
</project>
|
.idea/vcs.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="VcsDirectoryMappings">
|
4 |
+
<mapping directory="" vcs="Git" />
|
5 |
+
</component>
|
6 |
+
</project>
|
app.py
CHANGED
@@ -1,7 +1,55 @@
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
-
iface
|
7 |
-
iface.launch()
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
import cv2
|
4 |
import gradio as gr
|
5 |
+
import numpy as np
|
6 |
+
import joblib
|
7 |
+
|
8 |
+
from api import is_beautiful
|
9 |
+
from facial_image import FacialImage, convert_to_numpy_array
|
10 |
+
|
11 |
+
title = "Sytoss System: Beauty Recognition"
|
12 |
+
description = "A model to classify is face beautiful or not."
|
13 |
+
|
14 |
+
target_width = 512
|
15 |
+
facial = FacialImage()
|
16 |
+
loaded_svm_classifier = joblib.load('svm_model.pkl')
|
17 |
+
|
18 |
+
example_list = [["examples/" + example] for example in os.listdir("examples")]
|
19 |
+
|
20 |
+
|
21 |
+
def process_image(input_image: np.ndarray):
|
22 |
+
if input_image is not None:
|
23 |
+
|
24 |
+
output_image = input_image
|
25 |
+
|
26 |
+
input_height, input_width, _ = input_image.shape
|
27 |
+
if input_width > target_width:
|
28 |
+
scale_factor = float(target_width / input_width)
|
29 |
+
output_width = int(input_width * scale_factor)
|
30 |
+
output_height = int(input_height * scale_factor)
|
31 |
+
dsize = (output_width, output_height)
|
32 |
+
output_image = cv2.resize(input_image, dsize)
|
33 |
+
|
34 |
+
ratios, output_image = facial.calculate_ratios(output_image)
|
35 |
+
ratios_vector = convert_to_numpy_array(ratios)
|
36 |
+
|
37 |
+
class_probabilities = loaded_svm_classifier.predict_proba([ratios_vector])
|
38 |
+
|
39 |
+
predicted_class = np.argmax(class_probabilities)
|
40 |
+
|
41 |
+
probability_of_predicted_class = class_probabilities[0, predicted_class]
|
42 |
+
|
43 |
+
return output_image, predicted_class, probability_of_predicted_class
|
44 |
+
return None, None
|
45 |
+
|
46 |
|
47 |
+
iface = gr.Interface(
|
48 |
+
process_image,
|
49 |
+
inputs=gr.inputs.Image(),
|
50 |
+
outputs=["image", gr.Number(label="Predicted class"), gr.Number(label="Probability")],
|
51 |
+
title=title,
|
52 |
+
description=description,
|
53 |
+
examples=example_list)
|
54 |
|
55 |
+
iface.launch()
|
|
examples/example_1.jpg
ADDED
examples/example_2.jpg
ADDED
examples/example_3.jpeg
ADDED
examples/example_4.png
ADDED
Git LFS Details
|
examples/example_5.jpeg
ADDED
examples/example_6.jpg
ADDED
examples/img.png
ADDED
Git LFS Details
|
facial_image.py
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from math import dist
|
2 |
+
|
3 |
+
import cv2
|
4 |
+
import mediapipe as mp
|
5 |
+
|
6 |
+
from points import points
|
7 |
+
from ratios import Ratios
|
8 |
+
from utils import *
|
9 |
+
|
10 |
+
|
11 |
+
def convert_to_numpy_array(ratios: dict[str, float]) -> numpy.array:
|
12 |
+
arr = []
|
13 |
+
for k, v in ratios.items():
|
14 |
+
arr.append(v)
|
15 |
+
return numpy.array(arr)
|
16 |
+
|
17 |
+
|
18 |
+
class FacialImage:
|
19 |
+
def __init__(self):
|
20 |
+
self.landmarks = []
|
21 |
+
self.width = 0
|
22 |
+
self.height = 0
|
23 |
+
|
24 |
+
def get_point_coordinates(self, point_num):
|
25 |
+
point = self.landmarks[point_num]
|
26 |
+
return point.x * self.width, point.y * self.height
|
27 |
+
|
28 |
+
def get_distance_two_points(self, point_num_1, point_num_2):
|
29 |
+
first = self.get_point_coordinates(point_num_1)
|
30 |
+
second = self.get_point_coordinates(point_num_2)
|
31 |
+
return dist(first, second)
|
32 |
+
|
33 |
+
def get_ratio_between_two(self, ratio1_name, ratio2_name):
|
34 |
+
first = self.get_distance_two_points(points[ratio1_name][0], points[ratio1_name][1])
|
35 |
+
second = self.get_distance_two_points(points[ratio2_name][0], points[ratio2_name][1])
|
36 |
+
return first / second
|
37 |
+
|
38 |
+
def get_landmarks(self, image: numpy.ndarray):
|
39 |
+
mp_face_mesh = mp.solutions.face_mesh
|
40 |
+
face_mesh = mp_face_mesh.FaceMesh()
|
41 |
+
|
42 |
+
# Image
|
43 |
+
height, width, _ = image.shape
|
44 |
+
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
45 |
+
|
46 |
+
# Facial landmarks
|
47 |
+
result = face_mesh.process(rgb_image)
|
48 |
+
|
49 |
+
for facial_landmarks in result.multi_face_landmarks:
|
50 |
+
for i in range(0, 468):
|
51 |
+
pt1 = facial_landmarks.landmark[i]
|
52 |
+
x = int(pt1.x * width)
|
53 |
+
y = int(pt1.y * height)
|
54 |
+
cv2.circle(image, (x, y), 1, (255, 0, 0), -1)
|
55 |
+
|
56 |
+
self.landmarks = result.multi_face_landmarks[0].landmark
|
57 |
+
self.width = width
|
58 |
+
self.height = height
|
59 |
+
|
60 |
+
def calculate_ratios(self, image: numpy.ndarray, is_normalized=False):
|
61 |
+
self.get_landmarks(image)
|
62 |
+
|
63 |
+
ratios = {
|
64 |
+
'Under eyes/Interocular': self.get_ratio_between_two(Ratios.UNDER_EYES, Ratios.INTEROCULAR),
|
65 |
+
'Under eyes/Nose width': self.get_ratio_between_two(Ratios.UNDER_EYES, Ratios.NOSE_WIDTH),
|
66 |
+
'Mouth width/Interocular': self.get_ratio_between_two(Ratios.MOUTH_WIDTH, Ratios.INTEROCULAR),
|
67 |
+
'Upper lip-jaw/Interocular': self.get_ratio_between_two(Ratios.UPPER_LIP_TO_JAW, Ratios.INTEROCULAR),
|
68 |
+
'Upper lip-jaw/Nose width': self.get_ratio_between_two(Ratios.UPPER_LIP_TO_JAW, Ratios.NOSE_WIDTH),
|
69 |
+
'Interocular/Lip height': self.get_ratio_between_two(Ratios.INTEROCULAR, Ratios.LIPS_HEIGHT),
|
70 |
+
'Nose width/Interocular': self.get_ratio_between_two(Ratios.NOSE_WIDTH, Ratios.INTEROCULAR),
|
71 |
+
'Nose width/Upper lip height': self.get_ratio_between_two(Ratios.NOSE_WIDTH, Ratios.UPPER_LIP_HEIGHT),
|
72 |
+
'Interocular/Nose mouth height': self.get_ratio_between_two(Ratios.INTEROCULAR,
|
73 |
+
Ratios.NOSE_TO_MOUTH_HEIGHT),
|
74 |
+
'Face top-eyebrows/Eyebrows-Nose': self.get_ratio_between_two(Ratios.FACE_TOP_TO_EYEBROWS,
|
75 |
+
Ratios.EYEBROWS_TO_NOSE),
|
76 |
+
'Eyebrows-nose/Nose-jaw': self.get_ratio_between_two(Ratios.EYEBROWS_TO_NOSE, Ratios.NOSE_TO_JAW),
|
77 |
+
'Face top-eyebrows/Nose-Jaw': self.get_ratio_between_two(Ratios.FACE_TOP_TO_EYEBROWS, Ratios.NOSE_TO_JAW),
|
78 |
+
'Interocular/Nose width': self.get_ratio_between_two(Ratios.INTEROCULAR, Ratios.NOSE_WIDTH),
|
79 |
+
'Face height/Face width': self.get_ratio_between_two(Ratios.FACE_HEIGHT, Ratios.FACE_WIDTH),
|
80 |
+
'Lower eyebrow length': self.get_ratio_between_two(Ratios.LEFT_LOWER_EYEBROW_LENGTH,
|
81 |
+
Ratios.RIGHT_LOWER_EYEBROW_LENGTH),
|
82 |
+
'Lower lip length': self.get_ratio_between_two(Ratios.LEFT_LOWER_LIP_LENGTH, Ratios.RIGHT_LOWER_LIP_LENGTH),
|
83 |
+
'Upper eyebrow': self.get_ratio_between_two(Ratios.LEFT_UPPER_EYEBROW_LENGTH,
|
84 |
+
Ratios.RIGHT_UPPER_EYEBROW_LENGTH),
|
85 |
+
'Upper lip': self.get_ratio_between_two(Ratios.LEFT_UPPER_LIP_LENGTH, Ratios.RIGHT_UPPER_LIP_LENGTH),
|
86 |
+
'Nose': self.get_ratio_between_two(Ratios.LEFT_NOSE_WIDTH, Ratios.RIGHT_NOSE_WIDTH)
|
87 |
+
}
|
88 |
+
|
89 |
+
if is_normalized:
|
90 |
+
return normalization(ratios), image
|
91 |
+
|
92 |
+
return ratios, image
|
points.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from ratios import Ratios
|
2 |
+
|
3 |
+
points = {
|
4 |
+
Ratios.UNDER_EYES: (23, 253),
|
5 |
+
Ratios.INTEROCULAR: (243, 463),
|
6 |
+
Ratios.NOSE_WIDTH: (48, 278),
|
7 |
+
Ratios.MOUTH_WIDTH: (61, 293),
|
8 |
+
Ratios.UPPER_LIP_TO_JAW: (0, 152),
|
9 |
+
Ratios.LIPS_HEIGHT: (0, 17),
|
10 |
+
Ratios.NOSE_TO_MOUTH_HEIGHT: (2, 0),
|
11 |
+
Ratios.UPPER_LIP_HEIGHT: (0, 13),
|
12 |
+
Ratios.FACE_TOP_TO_EYEBROWS: (10, 9),
|
13 |
+
Ratios.EYEBROWS_TO_NOSE: (9, 2),
|
14 |
+
Ratios.NOSE_TO_JAW: (2, 152),
|
15 |
+
Ratios.FACE_HEIGHT: (10, 152),
|
16 |
+
Ratios.FACE_WIDTH: (227, 447),
|
17 |
+
Ratios.LEFT_LOWER_EYEBROW_LENGTH: (46, 55),
|
18 |
+
Ratios.RIGHT_LOWER_EYEBROW_LENGTH: (285, 276),
|
19 |
+
Ratios.LEFT_LOWER_LIP_LENGTH: (61, 17),
|
20 |
+
Ratios.RIGHT_LOWER_LIP_LENGTH: (17, 291),
|
21 |
+
Ratios.LEFT_UPPER_EYEBROW_LENGTH: (105, 9),
|
22 |
+
Ratios.RIGHT_UPPER_EYEBROW_LENGTH: (334, 9),
|
23 |
+
Ratios.LEFT_UPPER_LIP_LENGTH: (61, 0),
|
24 |
+
Ratios.RIGHT_UPPER_LIP_LENGTH: (17, 0),
|
25 |
+
Ratios.LEFT_NOSE_WIDTH: (48, 2),
|
26 |
+
Ratios.RIGHT_NOSE_WIDTH: (278, 2)
|
27 |
+
}
|
ratios.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import enum
|
2 |
+
|
3 |
+
|
4 |
+
class Ratios(enum.Enum):
|
5 |
+
UNDER_EYES = "under_eyes"
|
6 |
+
INTEROCULAR = "interocular"
|
7 |
+
NOSE_WIDTH = "nose_width"
|
8 |
+
MOUTH_WIDTH = "mouth_width"
|
9 |
+
UPPER_LIP_TO_JAW = "upper_lip_to_jaw"
|
10 |
+
LIPS_HEIGHT = "lips_height"
|
11 |
+
NOSE_TO_MOUTH_HEIGHT = "nose_to_mouth_height"
|
12 |
+
UPPER_LIP_HEIGHT = "upper_lip_height"
|
13 |
+
FACE_TOP_TO_EYEBROWS = "face_top_to_eyebrows"
|
14 |
+
EYEBROWS_TO_NOSE = "eyebrows_to_nose"
|
15 |
+
NOSE_TO_JAW = "nose_to_jaw"
|
16 |
+
FACE_HEIGHT = "face_height"
|
17 |
+
FACE_WIDTH = "face_width"
|
18 |
+
LEFT_LOWER_EYEBROW_LENGTH = "left_lower_eyebrow_length"
|
19 |
+
RIGHT_LOWER_EYEBROW_LENGTH = "right_lower_eyebrow_length"
|
20 |
+
LEFT_LOWER_LIP_LENGTH = "left_lower_lip_length"
|
21 |
+
RIGHT_LOWER_LIP_LENGTH = "right_lower_lip_length"
|
22 |
+
LEFT_UPPER_EYEBROW_LENGTH = "left_upper_eyebrow_length"
|
23 |
+
RIGHT_UPPER_EYEBROW_LENGTH = "right_upper_eyebrow_length"
|
24 |
+
LEFT_UPPER_LIP_LENGTH = "left_upper_lip_length"
|
25 |
+
RIGHT_UPPER_LIP_LENGTH = "right_upper_lip_length"
|
26 |
+
LEFT_NOSE_WIDTH = "nose_left_width"
|
27 |
+
RIGHT_NOSE_WIDTH = "nose_right_width"
|
requirements.txt
ADDED
Binary file (1.11 kB). View file
|
|
svm_model.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:26064833ae014765f7c01bed1704ff860a4ac16c1a3d77b39f1a1a32a8abef64
|
3 |
+
size 354762
|
utils.py
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy
|
2 |
+
import csv
|
3 |
+
|
4 |
+
|
5 |
+
def write_to_csv(ratios, file_name):
|
6 |
+
rows = []
|
7 |
+
|
8 |
+
for i in range(len(ratios)):
|
9 |
+
row = []
|
10 |
+
for key, value in ratios[i].items():
|
11 |
+
row.append(value)
|
12 |
+
rows.append(row)
|
13 |
+
|
14 |
+
with open(file_name, 'a', newline='') as csvfile:
|
15 |
+
csvwriter = csv.writer(csvfile)
|
16 |
+
csvwriter.writerows(rows)
|
17 |
+
csvfile.close()
|
18 |
+
|
19 |
+
|
20 |
+
def write_line_to_csv(ratios, file_name):
|
21 |
+
row = []
|
22 |
+
|
23 |
+
for key, value in ratios.items():
|
24 |
+
row.append(value)
|
25 |
+
|
26 |
+
with open(file_name, 'a', newline='') as csvfile:
|
27 |
+
csvwriter = csv.writer(csvfile)
|
28 |
+
csvwriter.writerow(row)
|
29 |
+
|
30 |
+
|
31 |
+
def standard_deviation(values):
|
32 |
+
_mean = numpy.mean(values)
|
33 |
+
differences = [(value - _mean) ** 2 for value in values]
|
34 |
+
sum_of_differences = sum(differences)
|
35 |
+
return (sum_of_differences / (len(values) - 1)) ** 0.5
|
36 |
+
|
37 |
+
|
38 |
+
def normalization(ratios_list):
|
39 |
+
_mean = numpy.mean(list(ratios_list.values()))
|
40 |
+
_deviation = standard_deviation(list(ratios_list.values()))
|
41 |
+
|
42 |
+
z_score = {}
|
43 |
+
for z_key, z_i in ratios_list.items():
|
44 |
+
z_score[z_key] = (z_i - _mean) / _deviation
|
45 |
+
|
46 |
+
lb = 0
|
47 |
+
ub = 1.618
|
48 |
+
_min = min(z_score.values())
|
49 |
+
_max = max(z_score.values())
|
50 |
+
|
51 |
+
linear = {}
|
52 |
+
for z_key, z_i in z_score.items():
|
53 |
+
linear[z_key] = lb + (z_i - _min) * (ub - lb) / (_max - _min)
|
54 |
+
|
55 |
+
return linear
|