# ========== تثبيت المكتبات بالترتيب الصحيح ========== import subprocess import sys def install(*args): subprocess.check_call([ sys.executable, "-m", "pip", "install", "--quiet", "--root-user-action=ignore", *args ]) print("🔧 جاري تثبيت المكتبات...") libs = [ ["numpy"], ["requests"], ["Pillow", "--upgrade"], ["torch", "--index-url", "https://download.pytorch.org/whl/cpu"], ["torchvision", "--index-url", "https://download.pytorch.org/whl/cpu"], ["transformers"], ["timm"], ["accelerate"], ["hf_transfer"], ["opencv-python-headless"], ["flask"], ] for lib in libs: try: install(*lib) print(f"✅ {lib[0]}") except Exception as e: print(f"⚠️ فشل {lib[0]}: {e}") print("✅ انتهى تثبيت المكتبات\n") # ========== الاستيرادات ========== import cv2 import numpy as np from flask import Flask, request, jsonify from PIL import Image from transformers import pipeline from collections import Counter app = Flask(__name__) app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 # 10MB API_KEY = "hkhlasjoaj5464hjsks" # ========== تحميل النماذج ========== def load_model(model_name): try: m = pipeline("image-classification", model=model_name) print(f"✅ تم تحميل: {model_name}") return m except Exception as e: print(f"⚠️ فشل تحميل {model_name}: {e}") return None # ── أقوى 4 نماذج لكشف الوجه (SOTA) ── # تم اختيار نماذج متخصصة في كشف البشر والعوائق لضمان دقة "no face" face_detection_models = { "Human-Detection-SOTA": load_model("prithivMLmods/Human-vs-NonHuman-Detection"), "Face-Obstruction-SOTA": load_model("dima806/face_obstruction_image_detection"), "Attractive-Faces-SOTA": load_model("dima806/attractive_faces_celebs_detection"), } # OpenCV Haar Cascade كمرجع إضافي face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml") # ── أقوى 9 نماذج لتصنيف الجنس (SOTA) ── # تم اختيار النماذج الأكثر تحميلاً ودقة على HuggingFace gender_models = { "SigLIP2-Gender": load_model("prithivMLmods/Realistic-Gender-Classification"), "Gender-Classification-1": load_model("rizvandwiki/gender-classification"), "Gender-Classification-2": load_model("rizvandwiki/gender-classification-2"), "Age-Gender-SOTA": load_model("abhilash88/age-gender-prediction"), "FairFace-Gender": load_model("dima806/fairface_gender_image_detection"), "Man-Woman-Face": load_model("dima806/man_woman_face_image_detection"), "Gender-Mini-SOTA": load_model("prithivMLmods/Gender-Classifier-Mini"), "Pedestrian-Gender": load_model("NTQAI/pedestrian_gender_recognition"), "Fashion-Gender-ViT": load_model("touchtech/fashion-images-gender-age-vit-large-patch16-384-v1"), } # نماذج NSFW nsfw_models = { "Falconsai": load_model("Falconsai/nsfw_image_detection"), "AdamCodd": load_model("AdamCodd/vit-base-nsfw-detector"), "carbon225": load_model("carbon225/vit-base-patch16-224-hentai"), "giacomoarienti": load_model("giacomoarienti/nsfw-classifier"), } # ========== توحيد النتائج ========== FACE_LABELS = {"human 𖨆", "sunglasses", "glasses", "mask", "hand", "none", "attractive", "not attractive"} NO_FACE_LABELS = {"non human メ", "other"} def normalize_face_detection(label: str) -> str: clean = label.strip().lower() if clean in FACE_LABELS: return "Face" if clean in NO_FACE_LABELS: return "No Face" return "Unknown" MALE_LABELS = {"male", "man", "men", "m", "boy", "0", "non-female", "male ♂"} FEMALE_LABELS = {"female", "woman", "women", "f", "girl", "1", "non-male", "female ♀"} def normalize_gender(label: str) -> str: clean = label.strip().lower() if clean in MALE_LABELS: return "Male" if clean in FEMALE_LABELS: return "Female" if "male" in clean and "fe" not in clean: return "Male" if "female" in clean or "woman" in clean or "girl" in clean: return "Female" return "Unknown" NSFW_POSITIVE = {"nsfw", "explicit", "porn", "hentai", "sexy", "adult", "unsafe", "1"} SFW_POSITIVE = {"sfw", "normal", "safe", "neutral", "drawings", "0"} def normalize_nsfw(label: str) -> str: clean = label.strip().lower() if clean in NSFW_POSITIVE: return "Yes" if clean in SFW_POSITIVE: return "No" if any(k in clean for k in ["nsfw","explicit","porn","adult","naked","nude"]): return "Yes" return "No" # ========== endpoint الرئيسي ========== @app.route("/api/analyze", methods=["POST"]) def analyze(): key = request.headers.get("api_key") or request.args.get("api_key") if key != API_KEY: return jsonify({"error": "🔒 مفتاح API غير صالح"}), 403 file = request.files.get("image") if not file or file.filename == "": return jsonify({"error": "❌ لم يتم اختيار أي ملف"}), 400 try: img = Image.open(file).convert("RGB") # ── 1. كشف الوجه (نظام تصويت صارم بـ 4 نماذج) ── face_votes = [] face_details = {} # أ. كشف OpenCV img_array = np.array(img) gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY) faces = face_cascade.detectMultiScale(gray, 1.1, 4, minSize=(30, 30)) if len(faces) > 0: face_votes.append("Face") face_details["OpenCV"] = {"detected": True, "count": len(faces)} else: face_details["OpenCV"] = {"detected": False} # ب. كشف نماذج HuggingFace SOTA for name, model in face_detection_models.items(): if model is None: continue try: best = max(model(img), key=lambda x: x["score"]) vote = normalize_face_detection(best["label"]) if vote != "Unknown": face_votes.append(vote) face_details[name] = { "label": best["label"], "normalized": vote, "confidence": round(best["score"] * 100, 2) } except Exception as e: face_details[name] = {"error": str(e)} face_counter = Counter(face_votes) # نظام تصويت صارم: يجب أن يؤكد نموذجان على الأقل وجود وجه، أو نموذج واحد بثقة عالية جداً (>90%) has_face = face_counter.get("Face", 0) >= 2 # فحص إضافي للثقة العالية إذا كان هناك صوت واحد فقط if not has_face and face_counter.get("Face", 0) == 1: for detail in face_details.values(): if detail.get("normalized") == "Face" and detail.get("confidence", 0) > 90: has_face = True break face_vote_count = dict(face_counter) # ── 2. فحص NSFW ── nsfw_votes, nsfw_details = [], {} for name, model in nsfw_models.items(): if model is None: continue try: best = max(model(img), key=lambda x: x["score"]) vote = normalize_nsfw(best["label"]) nsfw_votes.append(vote) nsfw_details[name] = { "label": best["label"], "normalized": vote, "confidence": round(best["score"] * 100, 2) } except Exception as e: nsfw_details[name] = {"error": str(e)} nsfw_counter = Counter(nsfw_votes) nsfw_final = nsfw_counter.most_common(1)[0][0] if nsfw_votes else "Unknown" # ── 3. تصنيف الجنس (نظام تصويت بـ 9 نماذج SOTA) ── gender_final = "no_face" gender_vote_count = {} gender_details = {} if has_face: gender_votes = [] for name, model in gender_models.items(): if model is None: continue try: best = max(model(img), key=lambda x: x["score"]) vote = normalize_gender(best["label"]) if vote != "Unknown": gender_votes.append(vote) gender_details[name] = { "label": best["label"], "normalized": vote, "confidence": round(best["score"] * 100, 2) } except Exception as e: gender_details[name] = {"error": str(e)} gender_counter = Counter(gender_votes) gender_final = gender_counter.most_common(1)[0][0] if gender_votes else "Unknown" gender_vote_count = dict(gender_counter) # ── توحيد النتائج النهائية ── if not has_face: final_result = "no face" elif gender_final == "Female": final_result = "female portrait" elif gender_final == "Male": final_result = "male portrait" else: final_result = "unknown" return jsonify({ "final_result": final_result, "nsfw_check": nsfw_final, "has_face": has_face, "face_vote": face_vote_count, "face_details": face_details, "gender_vote": gender_vote_count, "gender_details": gender_details, "nsfw_vote": dict(nsfw_counter), }) except Exception as e: return jsonify({"error": f"❌ حدث خطأ: {e}"}), 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=7860, debug=True, threaded=True)