andevs commited on
Commit
59ed525
·
verified ·
1 Parent(s): c10db71

Delete cosmetics_matching.py

Browse files
Files changed (1) hide show
  1. cosmetics_matching.py +0 -274
cosmetics_matching.py DELETED
@@ -1,274 +0,0 @@
1
- from fastapi import FastAPI, File, UploadFile, HTTPException, Depends
2
- import numpy as np
3
- import cv2
4
- from sklearn.cluster import KMeans
5
- import colorsys
6
-
7
- class SkinToneAnalyzer:
8
- def __init__(self):
9
- self.skin_tone_ranges = {
10
- 'fair': (0, 30),
11
- 'light': (30, 45),
12
- 'medium': (45, 60),
13
- 'tan': (60, 75),
14
- 'deep': (75, 90),
15
- 'dark': (90, 100)
16
- }
17
-
18
- self.undertone_rules = {
19
- 'warm': {'g/r_ratio': (0.4, 0.6), 'b/g_ratio': (0.5, 0.7)},
20
- 'cool': {'g/r_ratio': (0.6, 0.8), 'b/g_ratio': (0.7, 0.9)},
21
- 'neutral': {'g/r_ratio': (0.5, 0.7), 'b/g_ratio': (0.6, 0.8)},
22
- 'olive': {'g/r_ratio': (0.5, 0.7), 'b/g_ratio': (0.4, 0.6)}
23
- }
24
-
25
- def analyze_skin_tone(self, image_bytes: bytes) -> dict:
26
- """
27
- Analyze skin tone from image
28
- """
29
- # Convert to OpenCV format
30
- nparr = np.frombuffer(image_bytes, np.uint8)
31
- img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
32
- img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
33
-
34
- # Detect skin regions using face detection
35
- face_cascade = cv2.CascadeClassifier(
36
- cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
37
- )
38
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
39
- faces = face_cascade.detectMultiScale(gray, 1.1, 4)
40
-
41
- if len(faces) == 0:
42
- return {"error": "No face detected"}
43
-
44
- # Extract skin samples from cheeks
45
- skin_samples = []
46
- for (x, y, w, h) in faces:
47
- # Left cheek area
48
- left_cheek = img_rgb[y + h//3:y + h//2, x + w//8:x + w//4]
49
- # Right cheek area
50
- right_cheek = img_rgb[y + h//3:y + h//2, x + 3*w//4:x + 7*w//8]
51
-
52
- skin_samples.extend(left_cheek.reshape(-1, 3))
53
- skin_samples.extend(right_cheek.reshape(-1, 3))
54
-
55
- # Cluster to find dominant skin tone
56
- kmeans = KMeans(n_clusters=3, random_state=42)
57
- kmeans.fit(skin_samples)
58
-
59
- # Get the most representative cluster
60
- dominant_color = kmeans.cluster_centers_[np.argmax(np.bincount(kmeans.labels_))]
61
- r, g, b = dominant_color
62
-
63
- # Calculate melanin level
64
- melanin_level = self._calculate_melanin(r, g, b)
65
-
66
- # Determine primary tone
67
- primary_tone = self._get_primary_tone(melanin_level)
68
-
69
- # Determine undertone
70
- undertone = self._get_undertone(r, g, b)
71
-
72
- # Calculate RGB values
73
- rgb_values = {
74
- 'r': float(r),
75
- 'g': float(g),
76
- 'b': float(b),
77
- 'hex': f"{int(r):02x}{int(g):02x}{int(b):02x}"
78
- }
79
-
80
- return {
81
- 'primary_tone': primary_tone,
82
- 'undertone': undertone,
83
- 'surface_tone': self._get_surface_tone(r, g, b),
84
- 'melanin_level': float(melanin_level),
85
- 'rgb_values': rgb_values,
86
- 'foundation_match': self._get_foundation_match(primary_tone, undertone),
87
- 'analyzed_at': datetime.utcnow().isoformat()
88
- }
89
-
90
- def _calculate_melanin(self, r, g, b):
91
- """
92
- Calculate melanin level using ITA (Individual Typology Angle)
93
- """
94
- # Convert to LAB color space
95
- rgb_normalized = np.array([[[r/255, g/255, b/255]]], dtype=np.float32)
96
- lab = cv2.cvtColor(rgb_normalized, cv2.COLOR_RGB2LAB)
97
- l, a, _ = lab[0][0]
98
-
99
- # ITA calculation
100
- ita = np.arctan((l - 50) / a) * 180 / np.pi
101
-
102
- # Convert ITA to melanin level (0-100)
103
- melanin = 100 - ((ita + 90) / 180 * 100)
104
- return max(0, min(100, melanin))
105
-
106
- def _get_primary_tone(self, melanin):
107
- """
108
- Determine primary skin tone based on melanin level
109
- """
110
- for tone, (low, high) in self.skin_tone_ranges.items():
111
- if low <= melanin <= high:
112
- return tone
113
- return 'medium' # default
114
-
115
- def _get_undertone(self, r, g, b):
116
- """
117
- Determine undertone using color ratios
118
- """
119
- g_r_ratio = g / r if r > 0 else 0
120
- b_g_ratio = b / g if g > 0 else 0
121
-
122
- for undertone, rules in self.undertone_rules.items():
123
- g_ratio_min, g_ratio_max = rules['g/r_ratio']
124
- b_ratio_min, b_ratio_max = rules['b/g_ratio']
125
-
126
- if (g_ratio_min <= g_r_ratio <= g_ratio_max and
127
- b_ratio_min <= b_g_ratio <= b_ratio_max):
128
- return undertone
129
-
130
- return 'neutral' # default
131
-
132
- def _get_surface_tone(self, r, g, b):
133
- """
134
- Get surface complexion (may differ from undertone)
135
- """
136
- # Convert to HSV for surface analysis
137
- h, s, v = colorsys.rgb_to_hsv(r/255, g/255, b/255)
138
-
139
- if v > 0.8:
140
- return 'bright'
141
- elif v < 0.3:
142
- return 'deep'
143
- else:
144
- return 'natural'
145
-
146
- def _get_foundation_match(self, primary_tone, undertone):
147
- """
148
- Suggest foundation match based on skin analysis
149
- """
150
- return f"{primary_tone.capitalize()} with {undertone} undertone"
151
-
152
- class CosmeticMatcher:
153
- def __init__(self):
154
- self.product_database = load_cosmetics_database()
155
-
156
- def find_matches(self, skin_tone: dict, conditions: list) -> list:
157
- """
158
- Find cosmetic matches based on skin analysis
159
- """
160
- matches = []
161
-
162
- for product in self.product_database:
163
- score = self._calculate_match_score(product, skin_tone, conditions)
164
-
165
- if score > 70:
166
- matches.append({
167
- 'product': product,
168
- 'score': score,
169
- 'reason': self._get_match_reason(product, score),
170
- 'shades': self._find_best_shades(product, skin_tone)
171
- })
172
-
173
- # Sort by score
174
- matches.sort(key=lambda x: x['score'], reverse=True)
175
- return matches[:20] # Top 20 matches
176
-
177
- def _calculate_match_score(self, product, skin_tone, conditions):
178
- """
179
- Calculate how well a product matches the user
180
- """
181
- score = 50 # Base score
182
-
183
- # Undertone compatibility
184
- if product.get('undertone') == skin_tone['undertone']:
185
- score += 20
186
- elif product.get('undertone') == 'neutral':
187
- score += 10
188
- else:
189
- score -= 10
190
-
191
- # Skin tone range compatibility
192
- if skin_tone['primary_tone'] in product.get('skin_tone_ranges', []):
193
- score += 15
194
- elif 'all' in product.get('skin_tone_ranges', []):
195
- score += 10
196
-
197
- # Skin condition safety
198
- for condition in conditions:
199
- if self._is_safe_for_condition(product, condition):
200
- score += 5
201
- else:
202
- score -= 20 # Big penalty for unsafe products
203
-
204
- return max(0, min(100, score))
205
-
206
- def _find_best_shades(self, product, skin_tone):
207
- """
208
- Find the best shade matches within a product line
209
- """
210
- best_shades = []
211
-
212
- for shade in product.get('shades', []):
213
- shade_score = self._calculate_shade_match(shade, skin_tone)
214
-
215
- if shade_score > 80:
216
- best_shades.append({
217
- 'shade': shade,
218
- 'score': shade_score,
219
- 'reason': self._get_shade_reason(shade, skin_tone)
220
- })
221
-
222
- # Sort by score and return top 3
223
- best_shades.sort(key=lambda x: x['score'], reverse=True)
224
- return best_shades[:3]
225
-
226
- def _calculate_shade_match(self, shade, skin_tone):
227
- """
228
- Calculate how well a specific shade matches
229
- """
230
- score = 70 # Base score
231
-
232
- # Compare undertone
233
- if shade.get('undertone') == skin_tone['undertone']:
234
- score += 15
235
- elif self._are_complementary(shade.get('undertone'), skin_tone['undertone']):
236
- score += 10 # Complementary colors make features pop
237
-
238
- # Depth match
239
- melanin = skin_tone['melanin_level'] / 100
240
- depth_diff = abs(shade.get('depth', 0.5) - melanin)
241
- score -= depth_diff * 30
242
-
243
- return max(0, min(100, score))
244
-
245
- def _are_complementary(self, shade_undertone, skin_undertone):
246
- """
247
- Check if undertones are complementary (make each other pop)
248
- """
249
- complementary_pairs = {
250
- 'warm': 'cool',
251
- 'cool': 'warm',
252
- 'neutral': 'all'
253
- }
254
- return complementary_pairs.get(shade_undertone) == skin_undertone
255
-
256
- def _is_safe_for_condition(self, product, condition):
257
- """
258
- Check if product is safe for specific skin conditions
259
- """
260
- unsafe_ingredients = {
261
- 'oily_skin': ['mineral oil', 'coconut oil'],
262
- 'dry_skin': ['alcohol', 'fragrance'],
263
- 'sensitive_skin': ['fragrance', 'essential oils', 'alcohol'],
264
- 'acne': ['isopropyl myristate', 'lanolin']
265
- }
266
-
267
- condition_id = condition.get('condition')
268
- if condition_id in unsafe_ingredients:
269
- product_ingredients = product.get('ingredients', [])
270
- for ingredient in unsafe_ingredients[condition_id]:
271
- if ingredient in str(product_ingredients).lower():
272
- return False
273
-
274
- return True