Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, UploadFile, File | |
| import joblib | |
| import pandas as pd | |
| import numpy as np | |
| from pydantic import BaseModel | |
| import os | |
| from transformers import AutoImageProcessor, AutoModelForImageClassification | |
| from PIL import Image | |
| import torch | |
| import io | |
| app = FastAPI(title="Thaqafini API - Multi-Model Server") | |
| # ============================== | |
| # المتغيرات العالمية للموديلات | |
| # ============================== | |
| maternal_model = None | |
| genetic_model = None | |
| food_model = None | |
| food_processor = None | |
| # التعديل: استخدام النسخة المتخصصة في Food-101 (نفس الـ 101 صنف في الفايربيس) | |
| FOOD_MODEL_CHECKPOINT = "nateraw/vit-base-food101" | |
| # ============================== | |
| # تحميل الموديلات عند تشغيل السيرفر | |
| # ============================== | |
| async def load_models(): | |
| global maternal_model, genetic_model, food_model, food_processor | |
| # 1. تحميل موديل صحة الأم | |
| try: | |
| if os.path.exists("random_forest_model.joblib"): | |
| maternal_model = joblib.load("random_forest_model.joblib") | |
| print("✅ Maternal model loaded successfully") | |
| except Exception as e: | |
| print(f"❌ Error loading Maternal model: {e}") | |
| # 2. تحميل موديل الأمراض الوراثية | |
| try: | |
| if os.path.exists("thaqafni_model.pkl"): | |
| genetic_model = joblib.load("thaqafni_model.pkl") | |
| print("✅ Genetic model loaded successfully") | |
| except Exception as e: | |
| print(f"❌ Error loading Genetic model: {e}") | |
| # 3. تحميل موديل الطعام المخصص (Food-101) | |
| try: | |
| print(f"🔄 Loading specialized Food-101 model ({FOOD_MODEL_CHECKPOINT})...") | |
| food_processor = AutoImageProcessor.from_pretrained(FOOD_MODEL_CHECKPOINT) | |
| food_model = AutoModelForImageClassification.from_pretrained(FOOD_MODEL_CHECKPOINT) | |
| print("✅ Food-101 model (Specialized) loaded successfully") | |
| except Exception as e: | |
| print(f"❌ Error loading Food model: {e}") | |
| # ============================== | |
| # نماذج البيانات (Pydantic) | |
| # ============================== | |
| class MaternalInput(BaseModel): | |
| age: int | |
| systolic_bp: int | |
| diastolic_bp: int | |
| bs: float | |
| body_temp: float | |
| heart_rate: int | |
| class GeneticInput(BaseModel): | |
| age: int | |
| family_history: int | |
| hemoglobin: float | |
| fetal_hemoglobin: float | |
| sweat_chloride: float | |
| sickled_rbc_percent: float | |
| # ============================== | |
| # المسارات (Endpoints) | |
| # ============================== | |
| def home(): | |
| return { | |
| "status": "online", | |
| "models_status": { | |
| "maternal": "Ready" if maternal_model else "Not Loaded", | |
| "genetic": "Ready" if genetic_model else "Not Loaded", | |
| "food_101_specialized": "Ready" if food_model else "Not Loaded" | |
| } | |
| } | |
| # 1. توقع مخاطر الأم | |
| async def predict_maternal(data: MaternalInput): | |
| if not maternal_model: | |
| return {"error": "Maternal model is not available"} | |
| features = np.array([[ | |
| data.age, data.systolic_bp, data.diastolic_bp, | |
| data.bs, data.body_temp, data.heart_rate | |
| ]]) | |
| prediction = maternal_model.predict(features) | |
| return {"risk_level": int(prediction[0])} | |
| # 2. توقع الأمراض الوراثية | |
| async def predict_genetic(data: GeneticInput): | |
| if not genetic_model: | |
| return {"error": "Genetic model is not available"} | |
| input_data = pd.DataFrame([[ | |
| data.age, data.family_history, data.hemoglobin, | |
| data.fetal_hemoglobin, data.sweat_chloride, data.sickled_rbc_percent | |
| ]], columns=['Age', 'Family_History', 'Hemoglobin', 'Fetal_Hemoglobin', 'Sweat_Chloride', 'Sickled_RBC_Percent']) | |
| prediction = genetic_model.predict(input_data)[0] | |
| probabilities = genetic_model.predict_proba(input_data)[0] | |
| confidence = float(np.max(probabilities) * 100) | |
| ar_map = { | |
| "Thalassemia": "ثلاسيميا", | |
| "Normal": "سليم - طبيعي", | |
| "Sickle Cell Anemia": "فقر الدم المنجلي", | |
| "Cystic Fibrosis": "تليف كيسي", | |
| "High Risk": "معرض لخطورة عالية" | |
| } | |
| return { | |
| "diagnosis": prediction, | |
| "diagnosis_ar": ar_map.get(prediction, "غير معروف"), | |
| "confidence": f"{confidence:.2f}%" | |
| } | |
| # 3. التعرف على الطعام (النسخة المتوافقة مع Firestore) | |
| async def predict_food(file: UploadFile = File(...)): | |
| if not food_model or not food_processor: | |
| return {"error": "Food model is not available"} | |
| try: | |
| image_bytes = await file.read() | |
| image = Image.open(io.BytesIO(image_bytes)).convert("RGB") | |
| # المعالجة الخاصة بموديلات ViT | |
| inputs = food_processor(images=image, return_tensors="pt") | |
| with torch.no_grad(): | |
| outputs = food_model(**inputs) | |
| probs = torch.nn.functional.softmax(outputs.logits, dim=-1) | |
| # استخراج أفضل 3 احتمالات | |
| top_probs, top_indices = torch.topk(probs, 3) | |
| predictions = [] | |
| for i in range(3): | |
| label = food_model.config.id2label[top_indices[0][i].item()] | |
| # ملاحظة: الموديل قد يخرج اسماً بمسافات، نحولها لـ _ لتطابق Firestore IDs | |
| formatted_label = label.lower().replace(" ", "_") | |
| predictions.append({ | |
| "label": formatted_label, | |
| "confidence": f"{top_probs[0][i].item() * 100:.2f}%" | |
| }) | |
| return { | |
| "main_prediction": predictions[0]["label"], | |
| "all_predictions": predictions, | |
| "status": "success" | |
| } | |
| except Exception as e: | |
| return {"error": str(e), "status": "failed"} |