VladyslavTalakh commited on
Commit
6f90f9d
1 Parent(s): 7b62d5f
.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
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
 
5
 
6
- iface = gr.Interface(fn=greet, inputs="text", outputs="text")
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

  • SHA256: 7e3635201016e3175b811153e8e1557f2d2bdb44eb648c73140a2bb067bdd323
  • Pointer size: 132 Bytes
  • Size of remote file: 1.64 MB
examples/example_5.jpeg ADDED
examples/example_6.jpg ADDED
examples/img.png ADDED

Git LFS Details

  • SHA256: a314b1390ce6a35c50c77324856463c68789f892b704ff1ad07de683bf322f92
  • Pointer size: 131 Bytes
  • Size of remote file: 414 kB
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