hydration / app.py
anujakkulkarni's picture
Update app.py
66a6664 verified
import os
from flask import Flask, request, jsonify
from werkzeug.utils import secure_filename
import uuid
from pathlib import Path
from skin_analysis import (
get_comprehensive_analysis,
get_pores_breakdown,
get_wrinkle_breakdown,
get_usage_stats,
clear_cache
)
app = Flask(__name__)
# Configuration
UPLOAD_FOLDER = "temp_uploads"
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'webp'}
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
# Create upload folder if it doesn't exist
Path(UPLOAD_FOLDER).mkdir(parents=True, exist_ok=True)
def allowed_file(filename):
"""Check if file extension is allowed"""
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def cleanup_temp_file(filepath):
"""Delete temporary file"""
try:
if os.path.exists(filepath):
os.remove(filepath)
except Exception as e:
print(f"Warning: Could not delete temp file {filepath}: {e}")
@app.route("/")
def home():
"""Health check endpoint"""
return jsonify({
"status": "Skin Analysis API Running",
"version": "2.0",
"features": [
"hydration",
"pigmentation",
"acne",
"pores",
"wrinkles",
"age_analysis"
],
"endpoints": {
"analyze": "/analyze (POST)",
"analyze_detailed": "/analyze/detailed (POST)",
"stats": "/stats (GET)",
"clear_cache": "/cache/clear (POST)"
}
})
@app.route("/analyze", methods=["POST"])
def analyze():
"""
Main analysis endpoint - returns scores and basic data
"""
# Validate request
if "image" not in request.files:
return jsonify({"error": "No image uploaded"}), 400
img = request.files["image"]
if img.filename == '':
return jsonify({"error": "No image selected"}), 400
if not allowed_file(img.filename):
return jsonify({
"error": f"Invalid file type. Allowed types: {', '.join(ALLOWED_EXTENSIONS)}"
}), 400
# Check file size
img.seek(0, os.SEEK_END)
file_size = img.tell()
img.seek(0)
if file_size > MAX_FILE_SIZE:
return jsonify({
"error": f"File too large. Maximum size: {MAX_FILE_SIZE / (1024*1024):.1f}MB"
}), 400
# Save image with unique filename
filename = secure_filename(img.filename)
unique_filename = f"{uuid.uuid4()}_{filename}"
temp_path = os.path.join(UPLOAD_FOLDER, unique_filename)
try:
img.save(temp_path)
# Perform comprehensive analysis (single API call)
analysis = get_comprehensive_analysis(temp_path)
if not analysis:
return jsonify({
"error": "Analysis failed. Please try again or contact support."
}), 500
# Build response
result = {
"success": True,
"scores": {
"hydration": analysis['scores']['hydration'],
"pigmentation": 100 - analysis['scores']['pigmentation'], # Invert (higher = better)
"acne": 100 - analysis['scores']['acne'], # Invert (higher = better)
"pores": 100 - analysis['scores']['pores'], # Invert (higher = better)
"wrinkles": 100 - analysis['scores']['wrinkles'] # Invert (higher = better)
},
"raw_factors": {
"hydration": analysis['raw_data']['hydration'],
"pigmentation": analysis['raw_data']['pigmentation'],
"acne": analysis['raw_data']['acne'],
"pores": analysis['raw_data']['pores'],
"wrinkles": analysis['raw_data']['wrinkles']
},
"age_analysis": {
"fitzpatrick_type": analysis['age_analysis']['fitzpatrick_type'],
"eye_age": analysis['age_analysis']['eye_age'],
"skin_age": analysis['age_analysis']['skin_age']
},
"metadata": analysis['metadata']
}
return jsonify(result)
except Exception as e:
return jsonify({
"error": f"Server error: {str(e)}"
}), 500
finally:
# Cleanup temp file
cleanup_temp_file(temp_path)
@app.route("/analyze/detailed", methods=["POST"])
def analyze_detailed():
"""
Detailed analysis endpoint - includes breakdowns for pores and wrinkles
"""
# Validate request
if "image" not in request.files:
return jsonify({"error": "No image uploaded"}), 400
img = request.files["image"]
if img.filename == '':
return jsonify({"error": "No image selected"}), 400
if not allowed_file(img.filename):
return jsonify({
"error": f"Invalid file type. Allowed types: {', '.join(ALLOWED_EXTENSIONS)}"
}), 400
# Save image
filename = secure_filename(img.filename)
unique_filename = f"{uuid.uuid4()}_{filename}"
temp_path = os.path.join(UPLOAD_FOLDER, unique_filename)
try:
img.save(temp_path)
# Perform comprehensive analysis
analysis = get_comprehensive_analysis(temp_path)
if not analysis:
return jsonify({
"error": "Analysis failed. Please try again."
}), 500
# Get detailed breakdowns
pores_detail = get_pores_breakdown(temp_path)
wrinkles_detail = get_wrinkle_breakdown(temp_path)
# Build detailed response
result = {
"success": True,
"scores": {
"hydration": analysis['scores']['hydration'],
"pigmentation": 100 - analysis['scores']['pigmentation'],
"acne": 100 - analysis['scores']['acne'],
"pores": 100 - analysis['scores']['pores'],
"wrinkles": 100 - analysis['scores']['wrinkles']
},
"raw_factors": {
"hydration": analysis['raw_data']['hydration'],
"pigmentation": analysis['raw_data']['pigmentation'],
"acne": analysis['raw_data']['acne'],
"pores": analysis['raw_data']['pores'],
"wrinkles": analysis['raw_data']['wrinkles']
},
"age_analysis": analysis['age_analysis'],
"detailed_analysis": {
"pores": pores_detail,
"wrinkles": wrinkles_detail
},
"metadata": analysis['metadata']
}
return jsonify(result)
except Exception as e:
return jsonify({
"error": f"Server error: {str(e)}"
}), 500
finally:
cleanup_temp_file(temp_path)
@app.route("/stats", methods=["GET"])
def stats():
"""
Get API usage statistics
"""
try:
usage_stats = get_usage_stats()
return jsonify({
"success": True,
"usage": usage_stats
})
except Exception as e:
return jsonify({
"error": f"Could not retrieve stats: {str(e)}"
}), 500
@app.route("/cache/clear", methods=["POST"])
def clear_analysis_cache():
"""
Clear the analysis cache (admin endpoint)
"""
try:
clear_cache()
return jsonify({
"success": True,
"message": "Cache cleared successfully"
})
except Exception as e:
return jsonify({
"error": f"Could not clear cache: {str(e)}"
}), 500
@app.route("/health", methods=["GET"])
def health_check():
"""
Health check endpoint for monitoring
"""
return jsonify({
"status": "healthy",
"service": "Skin Analysis API",
"version": "2.0"
})
@app.errorhandler(413)
def request_entity_too_large(error):
"""Handle file too large error"""
return jsonify({
"error": f"File too large. Maximum size: {MAX_FILE_SIZE / (1024*1024):.1f}MB"
}), 413
@app.errorhandler(500)
def internal_server_error(error):
"""Handle internal server errors"""
return jsonify({
"error": "Internal server error. Please try again later."
}), 500
if __name__ == "__main__":
# For production, use a proper WSGI server like gunicorn
# gunicorn -w 4 -b 0.0.0.0:7860 app:app
# Development server
app.run(
host="0.0.0.0",
port=7860,
debug=False # Set to False in production
)