CoolFace
Apppublic

somoncef/glamflow-model

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
run.py263 linesDownload Raw Back to root
1from flask import Flask, request, jsonify2from flask_cors import CORS3import numpy as np4import tensorflow.lite as tflite  # REMARQUE: tf.lite.Interpreter sera supprimé dans TF 2.20, voir migration: https://ai.google.dev/edge/litert/migration5import cv26from PIL import Image7import io8import os9from functools import wraps10import requests11from dotenv import load_dotenv12 13app = Flask(__name__)14# Configuration CORS spécifique pour le frontend déployé15CORS(app, resources={r"/*": {"origins": ["https://app.glamflow.ma", "https://analysepeaux.vercel.app"]}})16 17# Middleware pour vérifier l'origine des requêtes18def check_origin(f):19    @wraps(f)20    def decorated_function(*args, **kwargs):21        # Ignorer toutes les vérifications en mode debug22        if app.debug:23            return f(*args, **kwargs)24            25        origin = request.headers.get('Origin', '')26        referer = request.headers.get('Referer', '')27        28        # Autoriser les requêtes provenant des domaines autorisés29        if any(domain in origin or domain in referer for domain in ['https://app.glamflow.ma', 'https://analysepeaux.vercel.app']):30            return f(*args, **kwargs)31        else:32            return jsonify({'error': 'Unauthorized origin'}), 40333    return decorated_function34 35# Charger les variables d'environnement36load_dotenv()37GROQ_API_KEY = os.getenv('GROQ_API_KEY')38 39# Image Size40IMG_SIZE = (150, 150)41 42# Load TFLite models globally43models_dir = os.path.join(os.path.dirname(__file__), "saved_models")44interpreter_type = tflite.Interpreter(model_path=os.path.join(models_dir, "skin_type_model.tflite"))45interpreter_disease = tflite.Interpreter(model_path=os.path.join(models_dir, "skin_disease_model.tflite"))46interpreter_type.allocate_tensors()47interpreter_disease.allocate_tensors()48 49# Label mappings50skin_type_labels = {0: 'Sèche', 1: 'Normale', 2: 'Grasse'}51skin_disease_labels = {0: 'Acné', 1: 'Mélanome', 2: 'Psoriasis', 3: 'Rosacée', 4: 'Vitiligo'}52 53# Recommendations pour les différents types de peau54skin_type_recommendations = {55    "Sèche": "💧 Hydratez bien, évitez les douches chaudes et utilisez une crème hydratante épaisse.",56    "Grasse": "🧴 Utilisez des soins légers et sans huile. Nettoyez deux fois par jour.",57    "Normale": "🌿 Maintenez une routine équilibrée, utilisez un écran solaire et hydratez bien."58}59 60# Recommendations pour les différentes maladies de peau61disease_treatment = {62    "Acné": "🔬 Utilisez de l'acide salicylique, évitez de toucher votre visage et nettoyez bien.",63    "Mélanome": "🛀 Utilisez des crèmes sans parfum, évitez les allergènes et gérez votre stress.",64    "Psoriasis": "🩺 Hydratez souvent, utilisez des traitements médicamenteux et évitez les déclencheurs.",65    "Rosacée": "☀️ Évitez les aliments épicés, utilisez des soins doux et portez un écran solaire.",66    "Vitiligo": "🌞 Utilisez des sérums éclaircissants, évitez l'exposition au soleil et appliquez un SPF 50+."67}68 69# Charger le détecteur de visage Haar Cascade70face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')71 72def detect_face(image_array):73    """Détecte si l'image contient un visage"""74    gray = cv2.cvtColor(image_array, cv2.COLOR_RGB2GRAY)75    faces = face_cascade.detectMultiScale(gray, 1.1, 4)76    return len(faces) > 077 78def predict_skin_condition(image_array):79    img = cv2.resize(image_array, IMG_SIZE)80    img = img / 255.081    img = np.expand_dims(img, axis=0).astype(np.float32)82 83    # Skin Type Prediction84    input_tensor_index = interpreter_type.get_input_details()[0]['index']85    interpreter_type.set_tensor(input_tensor_index, img)86    interpreter_type.invoke()87    output_tensor_index = interpreter_type.get_output_details()[0]['index']88    type_pred = interpreter_type.get_tensor(output_tensor_index)89 90    # Skin Disease Prediction91    input_tensor_index = interpreter_disease.get_input_details()[0]['index']92    interpreter_disease.set_tensor(input_tensor_index, img)93    interpreter_disease.invoke()94    output_tensor_index = interpreter_disease.get_output_details()[0]['index']95    disease_pred = interpreter_disease.get_tensor(output_tensor_index)96 97    # Results processing98    skin_type = skin_type_labels[np.argmax(type_pred)]99    skin_disease = skin_disease_labels[np.argmax(disease_pred)]100    101    type_confidence = float(np.max(type_pred) * 100)102    disease_confidence = float(np.max(disease_pred) * 100)103    104    type_probabilities = {skin_type_labels[i]: float(prob * 100) for i, prob in enumerate(type_pred[0])}105    disease_probabilities = {skin_disease_labels[i]: float(prob * 100) for i, prob in enumerate(disease_pred[0])}106    107    return {108        'skin_type': {109            'prediction': skin_type,110            'confidence': round(type_confidence, 2),111            'probabilities': {k: round(v, 2) for k, v in type_probabilities.items()}112        },113        'skin_disease': {114            'prediction': skin_disease,115            'confidence': round(disease_confidence, 2),116            'probabilities': {k: round(v, 2) for k, v in disease_probabilities.items()}117        },118        'recommendations': {119            'skin_type': skin_type_recommendations[skin_type],120            'skin_disease': disease_treatment[skin_disease]121        }122    }123 124def get_ai_analysis(analysis_results):125    """Obtenir une analyse détaillée via Groq LLM"""126    headers = {127        "Content-Type": "application/json",128        "Authorization": f"Bearer {GROQ_API_KEY}"129    }130    131    prompt = f"""Analyse ces résultats dermatologiques et fournis une réponse structurée avec exactement les 3 sections suivantes, séparées par [SECTION]:132 133Type de peau: {analysis_results['skin_type']['prediction']} (confiance: {analysis_results['skin_type']['confidence']}%)134Condition détectée: {analysis_results['skin_disease']['prediction']} (confiance: {analysis_results['skin_disease']['confidence']}%)135 136Distributions des probabilités:137Types de peau: {analysis_results['skin_type']['probabilities']}138Conditions: {analysis_results['skin_disease']['probabilities']}139 140Réponds en utilisant exactement ce format:141[SECTION]142Interprétation détaillée des résultats dermatologiques143[SECTION]144Conseils supplémentaires personnalisés145[SECTION]146Recommandations pour une routine quotidienne"""147 148    try:149        response = requests.post(150            "https://api.groq.com/openai/v1/chat/completions",151            headers=headers,152            json={153                "model": "llama-3.3-70b-versatile",154                "messages": [{155                    "role": "user",156                    "content": prompt157                }]158            }159        )160        161        if response.status_code == 200:162            content = response.json()["choices"][0]["message"]["content"]163            164            # Diviser la réponse en sections165            sections = content.split("[SECTION]")166            sections = [s.strip() for s in sections if s.strip()]  # Nettoyer les sections167            168            # Structurer la réponse169            if len(sections) >= 3:170                return {171                    "interpretation": sections[0],172                    "conseils": sections[1],173                    "routine": sections[2]174                }175            else:176                return {177                    "interpretation": "Analyse non disponible",178                    "conseils": "Conseils non disponibles",179                    "routine": "Routine non disponible"180                }181            182        else:183            return {184                "interpretation": "Analyse non disponible",185                "conseils": "Conseils non disponibles",186                "routine": "Routine non disponible"187            }188            189    except Exception as e:190        print(f"Erreur lors de l'appel à Groq: {str(e)}")191        return {192            "interpretation": "Erreur lors de l'analyse",193            "conseils": "Conseils non disponibles",194            "routine": "Routine non disponible"195        }196 197@app.route('/')198@check_origin199def home():200    return jsonify({201        "message": "GlamFlow AI API - Services disponibles:",202        "endpoints": [203            "/predict - Analyse complète de la peau (type et maladie)",204            "/health - Vérification de l'état des modèles",205            "/preload - Préchargement des modèles"206        ]207    }), 200208 209@app.route('/predict', methods=['POST'])210@check_origin211def predict():212    if 'file' not in request.files:213        return jsonify({'error': 'Aucun fichier fourni'}), 400214    215    file = request.files['file']216    if file.filename == '':217        return jsonify({'error': 'Aucun fichier sélectionné'}), 400218    219    if file:220        try:221            # Lire l'image et la convertir en format compatible222            image_bytes = file.read()223            image = Image.open(io.BytesIO(image_bytes)).convert("RGB")224            image_array = np.array(image)225            226            # Vérifier la présence d'un visage227            if not detect_face(image_array):228                return jsonify({229                    'error': 'Aucun visage n\'est détecté dans l\'image. Veuillez choisir une photo claire de votre visage pour effectuer l\'analyse.'230                }), 400231            232            # Analyser l'image avec les deux modèles233            results = predict_skin_condition(image_array)234            235            # Obtenir l'analyse IA supplémentaire236            ai_analysis = get_ai_analysis(results)237            results['ai_analysis'] = ai_analysis238            239            return jsonify(results), 200240        except Exception as e:241            return jsonify({'error': str(e)}), 500242 243@app.route('/preload', methods=['GET'])244@check_origin245def preload_models():246    """Précharge les modèles pour éviter les délais lors de la première prédiction"""247    return jsonify({'status': 'Models preloaded successfully'}), 200248 249@app.route('/health', methods=['GET'])250@check_origin251def health_check():252    models_loaded = {253        'skin_type_model': True,254        'skin_disease_model': True255    }256    257    return jsonify({258        'status': 'healthy', 259        'models': models_loaded260    }), 200261 262if __name__ == '__main__':263    app.run(host='0.0.0.0', port=5000, debug=True)