bhavinmatariya commited on
Commit
19ac182
·
verified ·
1 Parent(s): 2b3f68c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +299 -299
app.py CHANGED
@@ -1,299 +1,299 @@
1
- from fastapi import FastAPI, File, UploadFile, HTTPException
2
- from fastapi.responses import JSONResponse
3
- from fastapi.middleware.cors import CORSMiddleware
4
- import os
5
- import tempfile
6
- import json
7
- import requests
8
-
9
- # Import deepface with error handling
10
- try:
11
- from deepface import DeepFace
12
- import cv2
13
- import numpy as np
14
- DEEPFACE_AVAILABLE = True
15
- except ImportError as e:
16
- DEEPFACE_AVAILABLE = False
17
- print(f"Warning: DeepFace not available. Please install dependencies: {e}")
18
-
19
-
20
- def convert_to_serializable(obj):
21
- """Convert numpy types and other non-serializable types to native Python types"""
22
- if DEEPFACE_AVAILABLE:
23
- if isinstance(obj, np.integer):
24
- return int(obj)
25
- elif isinstance(obj, np.floating):
26
- return float(obj)
27
- elif isinstance(obj, np.ndarray):
28
- return obj.tolist()
29
-
30
- # Handle dict and list recursively
31
- if isinstance(obj, dict):
32
- return {key: convert_to_serializable(value) for key, value in obj.items()}
33
- elif isinstance(obj, list):
34
- return [convert_to_serializable(item) for item in obj]
35
-
36
- # Try to convert to float if it's a number-like object
37
- try:
38
- if hasattr(obj, 'item'): # numpy scalar
39
- return obj.item()
40
- except (AttributeError, ValueError):
41
- pass
42
-
43
- return obj
44
-
45
-
46
- app = FastAPI(title="Age, Emotion, and Gender Detection API", version="1.0.0")
47
-
48
- # Add CORS middleware to allow requests from React app
49
- app.add_middleware(
50
- CORSMiddleware,
51
- allow_origins=["*"], # In production, replace with specific origins
52
- allow_credentials=True,
53
- allow_methods=["*"],
54
- allow_headers=["*"],
55
- )
56
-
57
-
58
- @app.get("/")
59
- async def root():
60
- return {
61
- "message": "Age, Emotion, and Gender Detection API",
62
- "endpoints": {
63
- "/analyze": "POST - Upload an image to detect age, emotion, and gender",
64
- "/skin-analysis": "POST - Upload an image for comprehensive skin analysis"
65
- }
66
- }
67
-
68
-
69
- @app.post("/analyze")
70
- async def analyze_image(file: UploadFile = File(...)):
71
- """
72
- Upload an image and get age, emotion, and gender detection results.
73
-
74
- Args:
75
- file: Image file to analyze (supports common image formats)
76
-
77
- Returns:
78
- JSON response with age, gender, and emotion information
79
- """
80
- # Check if DeepFace is available
81
- if not DEEPFACE_AVAILABLE:
82
- raise HTTPException(
83
- status_code=503,
84
- detail="DeepFace module not available. Please install dependencies: pip install -r requirements.txt"
85
- )
86
-
87
- # Validate file type
88
- if not file.content_type or not file.content_type.startswith('image/'):
89
- raise HTTPException(status_code=400, detail="File must be an image")
90
-
91
- # Create a temporary file to save the uploaded image
92
- tmp_file_path = None
93
- try:
94
- # Read file contents
95
- contents = await file.read()
96
-
97
- # Create temporary file and write contents
98
- with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp_file:
99
- tmp_file.write(contents)
100
- tmp_file_path = tmp_file.name
101
-
102
- # Analyze the image
103
- try:
104
- results = DeepFace.analyze(
105
- img_path=tmp_file_path,
106
- actions=['age', 'gender', 'emotion'],
107
- enforce_detection=False # Continue even if face detection fails
108
- )
109
-
110
- # Handle both single result and list of results
111
- if isinstance(results, list):
112
- result = results[0]
113
- else:
114
- result = results
115
-
116
- # Extract age (convert to native Python int)
117
- age = int(result.get("age", 0))
118
-
119
- # Extract gender and convert numpy types
120
- gender_dict = result.get("gender", {})
121
- gender_dict = convert_to_serializable(gender_dict)
122
- if gender_dict:
123
- dominant_gender = max(gender_dict, key=gender_dict.get)
124
- gender_confidence = float(gender_dict[dominant_gender])
125
- else:
126
- dominant_gender = "Unknown"
127
- gender_confidence = 0.0
128
-
129
- # Extract emotion and convert numpy types
130
- emotion_dict = result.get("emotion", {})
131
- emotion_dict = convert_to_serializable(emotion_dict)
132
- if emotion_dict:
133
- dominant_emotion = max(emotion_dict, key=emotion_dict.get)
134
- emotion_confidence = float(emotion_dict[dominant_emotion])
135
- else:
136
- dominant_emotion = "Unknown"
137
- emotion_confidence = 0.0
138
-
139
- # Prepare response with all values converted to native Python types
140
- response = {
141
- "success": True,
142
- "age": age,
143
- "gender": {
144
- "prediction": dominant_gender,
145
- "confidence": round(gender_confidence, 2),
146
- "all_predictions": gender_dict
147
- },
148
- "emotion": {
149
- "prediction": dominant_emotion,
150
- "confidence": round(emotion_confidence, 2),
151
- "all_predictions": emotion_dict
152
- }
153
- }
154
-
155
- return JSONResponse(content=response)
156
-
157
- except Exception as e:
158
- raise HTTPException(
159
- status_code=500,
160
- detail=f"Error analyzing image: {str(e)}"
161
- )
162
-
163
- finally:
164
- # Clean up temporary file (close it first on Windows)
165
- if tmp_file_path and os.path.exists(tmp_file_path):
166
- try:
167
- # On Windows, we need to ensure the file is closed before deletion
168
- import time
169
- time.sleep(0.1) # Small delay to ensure file is released
170
- os.unlink(tmp_file_path)
171
- except (PermissionError, OSError) as e:
172
- # If deletion fails, try to delete on next attempt or ignore
173
- # The OS will clean up temp files eventually
174
- pass
175
-
176
-
177
- @app.post("/skin-analysis")
178
- async def skin_analysis(file: UploadFile = File(...)):
179
- """
180
- Upload an image and get comprehensive skin analysis results.
181
-
182
- Args:
183
- file: Image file to analyze (supports common image formats)
184
-
185
- Returns:
186
- JSON response with detailed skin analysis information
187
- """
188
- # Validate file type
189
- if not file.content_type or not file.content_type.startswith('image/'):
190
- raise HTTPException(status_code=400, detail="File must be an image")
191
-
192
- # Get API key from environment variable
193
- api_key = os.getenv("AILABAPI_API_KEY", "")
194
-
195
- # Create a temporary file to save the uploaded image
196
- tmp_file_path = None
197
- try:
198
- # Read file contents
199
- contents = await file.read()
200
-
201
- # Create temporary file and write contents
202
- with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp_file:
203
- tmp_file.write(contents)
204
- tmp_file_path = tmp_file.name
205
-
206
- # Prepare request to ailabapi
207
- url = "https://www.ailabapi.com/api/portrait/analysis/skin-analysis"
208
-
209
- # Open the temporary file for the request
210
- with open(tmp_file_path, 'rb') as image_file:
211
- files = {"image": (file.filename or "image.jpg", image_file, file.content_type)}
212
- headers = {"ailabapi-api-key": api_key}
213
-
214
- # Make request to ailabapi
215
- response = requests.post(url, files=files, headers=headers)
216
-
217
- if response.status_code != 200:
218
- raise HTTPException(
219
- status_code=response.status_code,
220
- detail=f"External API error: {response.text}"
221
- )
222
-
223
- data = response.json()
224
-
225
- # Mapping dictionaries
226
- yes_no_mapping = {0: "No", 1: "Yes"}
227
- eyelids_mapping = {
228
- 0: "Single eyelids",
229
- 1: "Parallel Double Eyelids",
230
- 2: "Scalloped Double Eyelids"
231
- }
232
- skin_type_mapping = {
233
- 0: "Oily skin",
234
- 1: "Dry skin",
235
- 2: "Neutral skin",
236
- 3: "Combination skin"
237
- }
238
-
239
- # Fields that use Yes/No mapping
240
- yes_no_fields = [
241
- "pores_left_cheek", "nasolabial_fold", "eye_pouch", "forehead_wrinkle",
242
- "skin_spot", "acne", "pores_forehead", "pores_jaw", "eye_finelines",
243
- "dark_circle", "crows_feet", "pores_right_cheek", "blackhead",
244
- "glabella_wrinkle", "mole"
245
- ]
246
-
247
- # Transform the result data
248
- if "result" in data:
249
- result = data["result"]
250
-
251
- # Transform Yes/No fields
252
- for field in yes_no_fields:
253
- if field in result and "value" in result[field]:
254
- result[field]["value_label"] = yes_no_mapping.get(result[field]["value"], "Unknown")
255
-
256
- # Transform eyelid fields
257
- if "left_eyelids" in result and "value" in result["left_eyelids"]:
258
- result["left_eyelids"]["value_label"] = eyelids_mapping.get(result["left_eyelids"]["value"], "Unknown")
259
-
260
- if "right_eyelids" in result and "value" in result["right_eyelids"]:
261
- result["right_eyelids"]["value_label"] = eyelids_mapping.get(result["right_eyelids"]["value"], "Unknown")
262
-
263
- # Transform skin_type
264
- if "skin_type" in result:
265
- if "skin_type" in result["skin_type"]:
266
- result["skin_type"]["skin_type_label"] = skin_type_mapping.get(result["skin_type"]["skin_type"], "Unknown")
267
- if "details" in result["skin_type"]:
268
- for detail in result["skin_type"]["details"]:
269
- if "value" in detail:
270
- detail["value_label"] = skin_type_mapping.get(detail["value"], "Unknown")
271
-
272
- return JSONResponse(content=data)
273
-
274
- except requests.exceptions.RequestException as e:
275
- raise HTTPException(
276
- status_code=500,
277
- detail=f"Error calling external API: {str(e)}"
278
- )
279
- except Exception as e:
280
- raise HTTPException(
281
- status_code=500,
282
- detail=f"Error processing image: {str(e)}"
283
- )
284
- finally:
285
- # Clean up temporary file
286
- if tmp_file_path and os.path.exists(tmp_file_path):
287
- try:
288
- import time
289
- time.sleep(0.1)
290
- os.unlink(tmp_file_path)
291
- except (PermissionError, OSError):
292
- pass
293
-
294
-
295
- @app.get("/health")
296
- async def health_check():
297
- """Health check endpoint"""
298
- return {"status": "healthy"}
299
-
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException
2
+ from fastapi.responses import JSONResponse
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ import os
5
+ import tempfile
6
+ import json
7
+ import requests
8
+
9
+ # Import deepface with error handling
10
+ try:
11
+ from deepface import DeepFace
12
+ import cv2
13
+ import numpy as np
14
+ DEEPFACE_AVAILABLE = True
15
+ except ImportError as e:
16
+ DEEPFACE_AVAILABLE = False
17
+ print(f"Warning: DeepFace not available. Please install dependencies: {e}")
18
+
19
+
20
+ def convert_to_serializable(obj):
21
+ """Convert numpy types and other non-serializable types to native Python types"""
22
+ if DEEPFACE_AVAILABLE:
23
+ if isinstance(obj, np.integer):
24
+ return int(obj)
25
+ elif isinstance(obj, np.floating):
26
+ return float(obj)
27
+ elif isinstance(obj, np.ndarray):
28
+ return obj.tolist()
29
+
30
+ # Handle dict and list recursively
31
+ if isinstance(obj, dict):
32
+ return {key: convert_to_serializable(value) for key, value in obj.items()}
33
+ elif isinstance(obj, list):
34
+ return [convert_to_serializable(item) for item in obj]
35
+
36
+ # Try to convert to float if it's a number-like object
37
+ try:
38
+ if hasattr(obj, 'item'): # numpy scalar
39
+ return obj.item()
40
+ except (AttributeError, ValueError):
41
+ pass
42
+
43
+ return obj
44
+
45
+
46
+ app = FastAPI(title="Age, Emotion, and Gender Detection API", version="1.0.0")
47
+
48
+ # Add CORS middleware to allow requests from React app
49
+ app.add_middleware(
50
+ CORSMiddleware,
51
+ allow_origins=["*"], # In production, replace with specific origins
52
+ allow_credentials=True,
53
+ allow_methods=["*"],
54
+ allow_headers=["*"],
55
+ )
56
+
57
+
58
+ @app.get("/")
59
+ async def root():
60
+ return {
61
+ "message": "Age, Emotion, and Gender Detection API",
62
+ "endpoints": {
63
+ "/analyze": "POST - Upload an image to detect age, emotion, and gender",
64
+ "/skin-analysis": "POST - Upload an image for comprehensive skin analysis"
65
+ }
66
+ }
67
+
68
+
69
+ @app.post("/analyze")
70
+ async def analyze_image(file: UploadFile = File(...)):
71
+ """
72
+ Upload an image and get age, emotion, and gender detection results.
73
+
74
+ Args:
75
+ file: Image file to analyze (supports common image formats)
76
+
77
+ Returns:
78
+ JSON response with age, gender, and emotion information
79
+ """
80
+ # Check if DeepFace is available
81
+ if not DEEPFACE_AVAILABLE:
82
+ raise HTTPException(
83
+ status_code=503,
84
+ detail="DeepFace module not available. Please install dependencies: pip install -r requirements.txt"
85
+ )
86
+
87
+ # Validate file type
88
+ if not file.content_type or not file.content_type.startswith('image/'):
89
+ raise HTTPException(status_code=400, detail="File must be an image")
90
+
91
+ # Create a temporary file to save the uploaded image
92
+ tmp_file_path = None
93
+ try:
94
+ # Read file contents
95
+ contents = await file.read()
96
+
97
+ # Create temporary file and write contents
98
+ with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp_file:
99
+ tmp_file.write(contents)
100
+ tmp_file_path = tmp_file.name
101
+
102
+ # Analyze the image
103
+ try:
104
+ results = DeepFace.analyze(
105
+ img_path=tmp_file_path,
106
+ actions=['age', 'gender', 'emotion'],
107
+ enforce_detection=False # Continue even if face detection fails
108
+ )
109
+
110
+ # Handle both single result and list of results
111
+ if isinstance(results, list):
112
+ result = results[0]
113
+ else:
114
+ result = results
115
+
116
+ # Extract age (convert to native Python int)
117
+ age = int(result.get("age", 0))
118
+
119
+ # Extract gender and convert numpy types
120
+ gender_dict = result.get("gender", {})
121
+ gender_dict = convert_to_serializable(gender_dict)
122
+ if gender_dict:
123
+ dominant_gender = max(gender_dict, key=gender_dict.get)
124
+ gender_confidence = float(gender_dict[dominant_gender])
125
+ else:
126
+ dominant_gender = "Unknown"
127
+ gender_confidence = 0.0
128
+
129
+ # Extract emotion and convert numpy types
130
+ emotion_dict = result.get("emotion", {})
131
+ emotion_dict = convert_to_serializable(emotion_dict)
132
+ if emotion_dict:
133
+ dominant_emotion = max(emotion_dict, key=emotion_dict.get)
134
+ emotion_confidence = float(emotion_dict[dominant_emotion])
135
+ else:
136
+ dominant_emotion = "Unknown"
137
+ emotion_confidence = 0.0
138
+
139
+ # Prepare response with all values converted to native Python types
140
+ response = {
141
+ "success": True,
142
+ "age": age,
143
+ "gender": {
144
+ "prediction": dominant_gender,
145
+ "confidence": round(gender_confidence, 2),
146
+ "all_predictions": gender_dict
147
+ },
148
+ "emotion": {
149
+ "prediction": dominant_emotion,
150
+ "confidence": round(emotion_confidence, 2),
151
+ "all_predictions": emotion_dict
152
+ }
153
+ }
154
+
155
+ return JSONResponse(content=response)
156
+
157
+ except Exception as e:
158
+ raise HTTPException(
159
+ status_code=500,
160
+ detail=f"Error analyzing image: {str(e)}"
161
+ )
162
+
163
+ finally:
164
+ # Clean up temporary file (close it first on Windows)
165
+ if tmp_file_path and os.path.exists(tmp_file_path):
166
+ try:
167
+ # On Windows, we need to ensure the file is closed before deletion
168
+ import time
169
+ time.sleep(0.1) # Small delay to ensure file is released
170
+ os.unlink(tmp_file_path)
171
+ except (PermissionError, OSError) as e:
172
+ # If deletion fails, try to delete on next attempt or ignore
173
+ # The OS will clean up temp files eventually
174
+ pass
175
+
176
+
177
+ # @app.post("/skin-analysis")
178
+ # async def skin_analysis(file: UploadFile = File(...)):
179
+ # """
180
+ # Upload an image and get comprehensive skin analysis results.
181
+
182
+ # Args:
183
+ # file: Image file to analyze (supports common image formats)
184
+
185
+ # Returns:
186
+ # JSON response with detailed skin analysis information
187
+ # """
188
+ # # Validate file type
189
+ # if not file.content_type or not file.content_type.startswith('image/'):
190
+ # raise HTTPException(status_code=400, detail="File must be an image")
191
+
192
+ # # Get API key from environment variable
193
+ # api_key = os.getenv("AILABAPI_API_KEY", "")
194
+
195
+ # # Create a temporary file to save the uploaded image
196
+ # tmp_file_path = None
197
+ # try:
198
+ # # Read file contents
199
+ # contents = await file.read()
200
+
201
+ # # Create temporary file and write contents
202
+ # with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp_file:
203
+ # tmp_file.write(contents)
204
+ # tmp_file_path = tmp_file.name
205
+
206
+ # # Prepare request to ailabapi
207
+ # url = "https://www.ailabapi.com/api/portrait/analysis/skin-analysis"
208
+
209
+ # # Open the temporary file for the request
210
+ # with open(tmp_file_path, 'rb') as image_file:
211
+ # files = {"image": (file.filename or "image.jpg", image_file, file.content_type)}
212
+ # headers = {"ailabapi-api-key": api_key}
213
+
214
+ # # Make request to ailabapi
215
+ # response = requests.post(url, files=files, headers=headers)
216
+
217
+ # if response.status_code != 200:
218
+ # raise HTTPException(
219
+ # status_code=response.status_code,
220
+ # detail=f"External API error: {response.text}"
221
+ # )
222
+
223
+ # data = response.json()
224
+
225
+ # # Mapping dictionaries
226
+ # yes_no_mapping = {0: "No", 1: "Yes"}
227
+ # eyelids_mapping = {
228
+ # 0: "Single eyelids",
229
+ # 1: "Parallel Double Eyelids",
230
+ # 2: "Scalloped Double Eyelids"
231
+ # }
232
+ # skin_type_mapping = {
233
+ # 0: "Oily skin",
234
+ # 1: "Dry skin",
235
+ # 2: "Neutral skin",
236
+ # 3: "Combination skin"
237
+ # }
238
+
239
+ # # Fields that use Yes/No mapping
240
+ # yes_no_fields = [
241
+ # "pores_left_cheek", "nasolabial_fold", "eye_pouch", "forehead_wrinkle",
242
+ # "skin_spot", "acne", "pores_forehead", "pores_jaw", "eye_finelines",
243
+ # "dark_circle", "crows_feet", "pores_right_cheek", "blackhead",
244
+ # "glabella_wrinkle", "mole"
245
+ # ]
246
+
247
+ # # Transform the result data
248
+ # if "result" in data:
249
+ # result = data["result"]
250
+
251
+ # # Transform Yes/No fields
252
+ # for field in yes_no_fields:
253
+ # if field in result and "value" in result[field]:
254
+ # result[field]["value_label"] = yes_no_mapping.get(result[field]["value"], "Unknown")
255
+
256
+ # # Transform eyelid fields
257
+ # if "left_eyelids" in result and "value" in result["left_eyelids"]:
258
+ # result["left_eyelids"]["value_label"] = eyelids_mapping.get(result["left_eyelids"]["value"], "Unknown")
259
+
260
+ # if "right_eyelids" in result and "value" in result["right_eyelids"]:
261
+ # result["right_eyelids"]["value_label"] = eyelids_mapping.get(result["right_eyelids"]["value"], "Unknown")
262
+
263
+ # # Transform skin_type
264
+ # if "skin_type" in result:
265
+ # if "skin_type" in result["skin_type"]:
266
+ # result["skin_type"]["skin_type_label"] = skin_type_mapping.get(result["skin_type"]["skin_type"], "Unknown")
267
+ # if "details" in result["skin_type"]:
268
+ # for detail in result["skin_type"]["details"]:
269
+ # if "value" in detail:
270
+ # detail["value_label"] = skin_type_mapping.get(detail["value"], "Unknown")
271
+
272
+ # return JSONResponse(content=data)
273
+
274
+ # except requests.exceptions.RequestException as e:
275
+ # raise HTTPException(
276
+ # status_code=500,
277
+ # detail=f"Error calling external API: {str(e)}"
278
+ # )
279
+ # except Exception as e:
280
+ # raise HTTPException(
281
+ # status_code=500,
282
+ # detail=f"Error processing image: {str(e)}"
283
+ # )
284
+ # finally:
285
+ # # Clean up temporary file
286
+ # if tmp_file_path and os.path.exists(tmp_file_path):
287
+ # try:
288
+ # import time
289
+ # time.sleep(0.1)
290
+ # os.unlink(tmp_file_path)
291
+ # except (PermissionError, OSError):
292
+ # pass
293
+
294
+
295
+ @app.get("/health")
296
+ async def health_check():
297
+ """Health check endpoint"""
298
+ return {"status": "healthy"}
299
+