anujakkulkarni commited on
Commit
4d81a3d
·
verified ·
1 Parent(s): 0206100

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +262 -41
app.py CHANGED
@@ -1,59 +1,280 @@
 
1
  from flask import Flask, request, jsonify
2
- from concurrent.futures import ThreadPoolExecutor
 
 
3
 
4
- from skin_analysis import (
5
- get_hydration_factors, compute_hydration_score,
6
- get_pigmentation_factors, compute_pigmentation_score,
7
- get_acne_factors, compute_acne_score,
8
- get_fitzpatrick_type, get_eye_age, get_skin_age
 
9
  )
10
 
11
  app = Flask(__name__)
12
 
13
- executor = ThreadPoolExecutor(max_workers=6)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  @app.route("/")
16
  def home():
17
- return {"status": "Skin Analysis API Running"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  @app.route("/analyze", methods=["POST"])
20
  def analyze():
 
 
 
 
21
  if "image" not in request.files:
22
  return jsonify({"error": "No image uploaded"}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
 
 
 
 
 
 
 
 
 
24
  img = request.files["image"]
25
- img.save("temp.jpg")
26
-
27
- # Launch all 6 tasks in parallel
28
- hydration_f = executor.submit(get_hydration_factors, "temp.jpg")
29
- pigmentation_f = executor.submit(get_pigmentation_factors, "temp.jpg")
30
- acne_f = executor.submit(get_acne_factors, "temp.jpg")
31
- fitz_f = executor.submit(get_fitzpatrick_type, "temp.jpg")
32
- eye_f = executor.submit(get_eye_age, "temp.jpg")
33
- skin_f = executor.submit(get_skin_age, "temp.jpg")
34
-
35
- hydration = hydration_f.result()
36
- pigmentation = pigmentation_f.result()
37
- acne = acne_f.result()
38
- fitz_type = fitz_f.result()
39
- eye_age = eye_f.result()
40
- skin_age = skin_f.result()
41
-
42
- result = {
43
- "hydration_score": compute_hydration_score(hydration),
44
- "pigmentation_score": 100 - compute_pigmentation_score(pigmentation),
45
- "acne_score": 100 - compute_acne_score(acne),
46
-
47
- "hydration_factors": hydration,
48
- "pigmentation_factors": pigmentation,
49
- "acne_factors": acne,
50
-
51
- "fitzpatrick_type": fitz_type,
52
- "eye_age": eye_age,
53
- "skin_age": skin_age
54
- }
55
-
56
- return jsonify(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  if __name__ == "__main__":
59
- app.run(host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
1
+ import os
2
  from flask import Flask, request, jsonify
3
+ from werkzeug.utils import secure_filename
4
+ import uuid
5
+ from pathlib import Path
6
 
7
+ from skin_analysis_optimized_v2 import (
8
+ get_comprehensive_analysis,
9
+ get_pores_breakdown,
10
+ get_wrinkle_breakdown,
11
+ get_usage_stats,
12
+ clear_cache
13
  )
14
 
15
  app = Flask(__name__)
16
 
17
+ # Configuration
18
+ UPLOAD_FOLDER = "temp_uploads"
19
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'webp'}
20
+ MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
21
+
22
+ # Create upload folder if it doesn't exist
23
+ Path(UPLOAD_FOLDER).mkdir(parents=True, exist_ok=True)
24
+
25
+ def allowed_file(filename):
26
+ """Check if file extension is allowed"""
27
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
28
+
29
+ def cleanup_temp_file(filepath):
30
+ """Delete temporary file"""
31
+ try:
32
+ if os.path.exists(filepath):
33
+ os.remove(filepath)
34
+ except Exception as e:
35
+ print(f"Warning: Could not delete temp file {filepath}: {e}")
36
 
37
  @app.route("/")
38
  def home():
39
+ """Health check endpoint"""
40
+ return jsonify({
41
+ "status": "Skin Analysis API Running",
42
+ "version": "2.0",
43
+ "features": [
44
+ "hydration",
45
+ "pigmentation",
46
+ "acne",
47
+ "pores",
48
+ "wrinkles",
49
+ "age_analysis"
50
+ ],
51
+ "endpoints": {
52
+ "analyze": "/analyze (POST)",
53
+ "analyze_detailed": "/analyze/detailed (POST)",
54
+ "stats": "/stats (GET)",
55
+ "clear_cache": "/cache/clear (POST)"
56
+ }
57
+ })
58
 
59
  @app.route("/analyze", methods=["POST"])
60
  def analyze():
61
+ """
62
+ Main analysis endpoint - returns scores and basic data
63
+ """
64
+ # Validate request
65
  if "image" not in request.files:
66
  return jsonify({"error": "No image uploaded"}), 400
67
+
68
+ img = request.files["image"]
69
+
70
+ if img.filename == '':
71
+ return jsonify({"error": "No image selected"}), 400
72
+
73
+ if not allowed_file(img.filename):
74
+ return jsonify({
75
+ "error": f"Invalid file type. Allowed types: {', '.join(ALLOWED_EXTENSIONS)}"
76
+ }), 400
77
+
78
+ # Check file size
79
+ img.seek(0, os.SEEK_END)
80
+ file_size = img.tell()
81
+ img.seek(0)
82
+
83
+ if file_size > MAX_FILE_SIZE:
84
+ return jsonify({
85
+ "error": f"File too large. Maximum size: {MAX_FILE_SIZE / (1024*1024):.1f}MB"
86
+ }), 400
87
+
88
+ # Save image with unique filename
89
+ filename = secure_filename(img.filename)
90
+ unique_filename = f"{uuid.uuid4()}_{filename}"
91
+ temp_path = os.path.join(UPLOAD_FOLDER, unique_filename)
92
+
93
+ try:
94
+ img.save(temp_path)
95
+
96
+ # Perform comprehensive analysis (single API call)
97
+ analysis = get_comprehensive_analysis(temp_path)
98
+
99
+ if not analysis:
100
+ return jsonify({
101
+ "error": "Analysis failed. Please try again or contact support."
102
+ }), 500
103
+
104
+ # Build response
105
+ result = {
106
+ "success": True,
107
+ "scores": {
108
+ "hydration": analysis['scores']['hydration'],
109
+ "pigmentation": 100 - analysis['scores']['pigmentation'], # Invert (higher = better)
110
+ "acne": 100 - analysis['scores']['acne'], # Invert (higher = better)
111
+ "pores": 100 - analysis['scores']['pores'], # Invert (higher = better)
112
+ "wrinkles": 100 - analysis['scores']['wrinkles'] # Invert (higher = better)
113
+ },
114
+ "raw_factors": {
115
+ "hydration": analysis['raw_data']['hydration'],
116
+ "pigmentation": analysis['raw_data']['pigmentation'],
117
+ "acne": analysis['raw_data']['acne'],
118
+ "pores": analysis['raw_data']['pores'],
119
+ "wrinkles": analysis['raw_data']['wrinkles']
120
+ },
121
+ "age_analysis": {
122
+ "fitzpatrick_type": analysis['age_analysis']['fitzpatrick_type'],
123
+ "eye_age": analysis['age_analysis']['eye_age'],
124
+ "skin_age": analysis['age_analysis']['skin_age']
125
+ },
126
+ "metadata": analysis['metadata']
127
+ }
128
+
129
+ return jsonify(result)
130
+
131
+ except Exception as e:
132
+ return jsonify({
133
+ "error": f"Server error: {str(e)}"
134
+ }), 500
135
+
136
+ finally:
137
+ # Cleanup temp file
138
+ cleanup_temp_file(temp_path)
139
 
140
+ @app.route("/analyze/detailed", methods=["POST"])
141
+ def analyze_detailed():
142
+ """
143
+ Detailed analysis endpoint - includes breakdowns for pores and wrinkles
144
+ """
145
+ # Validate request
146
+ if "image" not in request.files:
147
+ return jsonify({"error": "No image uploaded"}), 400
148
+
149
  img = request.files["image"]
150
+
151
+ if img.filename == '':
152
+ return jsonify({"error": "No image selected"}), 400
153
+
154
+ if not allowed_file(img.filename):
155
+ return jsonify({
156
+ "error": f"Invalid file type. Allowed types: {', '.join(ALLOWED_EXTENSIONS)}"
157
+ }), 400
158
+
159
+ # Save image
160
+ filename = secure_filename(img.filename)
161
+ unique_filename = f"{uuid.uuid4()}_{filename}"
162
+ temp_path = os.path.join(UPLOAD_FOLDER, unique_filename)
163
+
164
+ try:
165
+ img.save(temp_path)
166
+
167
+ # Perform comprehensive analysis
168
+ analysis = get_comprehensive_analysis(temp_path)
169
+
170
+ if not analysis:
171
+ return jsonify({
172
+ "error": "Analysis failed. Please try again."
173
+ }), 500
174
+
175
+ # Get detailed breakdowns
176
+ pores_detail = get_pores_breakdown(temp_path)
177
+ wrinkles_detail = get_wrinkle_breakdown(temp_path)
178
+
179
+ # Build detailed response
180
+ result = {
181
+ "success": True,
182
+ "scores": {
183
+ "hydration": analysis['scores']['hydration'],
184
+ "pigmentation": 100 - analysis['scores']['pigmentation'],
185
+ "acne": 100 - analysis['scores']['acne'],
186
+ "pores": 100 - analysis['scores']['pores'],
187
+ "wrinkles": 100 - analysis['scores']['wrinkles']
188
+ },
189
+ "raw_factors": {
190
+ "hydration": analysis['raw_data']['hydration'],
191
+ "pigmentation": analysis['raw_data']['pigmentation'],
192
+ "acne": analysis['raw_data']['acne'],
193
+ "pores": analysis['raw_data']['pores'],
194
+ "wrinkles": analysis['raw_data']['wrinkles']
195
+ },
196
+ "age_analysis": analysis['age_analysis'],
197
+ "detailed_analysis": {
198
+ "pores": pores_detail,
199
+ "wrinkles": wrinkles_detail
200
+ },
201
+ "metadata": analysis['metadata']
202
+ }
203
+
204
+ return jsonify(result)
205
+
206
+ except Exception as e:
207
+ return jsonify({
208
+ "error": f"Server error: {str(e)}"
209
+ }), 500
210
+
211
+ finally:
212
+ cleanup_temp_file(temp_path)
213
+
214
+ @app.route("/stats", methods=["GET"])
215
+ def stats():
216
+ """
217
+ Get API usage statistics
218
+ """
219
+ try:
220
+ usage_stats = get_usage_stats()
221
+ return jsonify({
222
+ "success": True,
223
+ "usage": usage_stats
224
+ })
225
+ except Exception as e:
226
+ return jsonify({
227
+ "error": f"Could not retrieve stats: {str(e)}"
228
+ }), 500
229
+
230
+ @app.route("/cache/clear", methods=["POST"])
231
+ def clear_analysis_cache():
232
+ """
233
+ Clear the analysis cache (admin endpoint)
234
+ """
235
+ try:
236
+ clear_cache()
237
+ return jsonify({
238
+ "success": True,
239
+ "message": "Cache cleared successfully"
240
+ })
241
+ except Exception as e:
242
+ return jsonify({
243
+ "error": f"Could not clear cache: {str(e)}"
244
+ }), 500
245
+
246
+ @app.route("/health", methods=["GET"])
247
+ def health_check():
248
+ """
249
+ Health check endpoint for monitoring
250
+ """
251
+ return jsonify({
252
+ "status": "healthy",
253
+ "service": "Skin Analysis API",
254
+ "version": "2.0"
255
+ })
256
+
257
+ @app.errorhandler(413)
258
+ def request_entity_too_large(error):
259
+ """Handle file too large error"""
260
+ return jsonify({
261
+ "error": f"File too large. Maximum size: {MAX_FILE_SIZE / (1024*1024):.1f}MB"
262
+ }), 413
263
+
264
+ @app.errorhandler(500)
265
+ def internal_server_error(error):
266
+ """Handle internal server errors"""
267
+ return jsonify({
268
+ "error": "Internal server error. Please try again later."
269
+ }), 500
270
 
271
  if __name__ == "__main__":
272
+ # For production, use a proper WSGI server like gunicorn
273
+ # gunicorn -w 4 -b 0.0.0.0:7860 app:app
274
+
275
+ # Development server
276
+ app.run(
277
+ host="0.0.0.0",
278
+ port=7860,
279
+ debug=False # Set to False in production
280
+ )