Spaces:
Build error
Build error
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image | |
| import cv2 | |
| import dlib | |
| import os | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| # عنوان برنامه | |
| TITLE = "🔍 سامانه پیشرفته تشخیص هویت چهره" | |
| DESCRIPTION = """ | |
| 🎯 سیستم هوشمند تشخیص هویت با استفاده از نقاط کلیدی صورت | |
| ✨ امکانات: | |
| - تشخیص ۶۸ نقطه کلیدی روی چهره | |
| - مقایسه ساختار استخوانی و هندسی صورت | |
| - مقاوم در برابر تغییرات نور و آرایش | |
| - دقت بسیار بالا با الگوریتمهای پیشرفته | |
| 📌 راهنمایی: | |
| 1. تصویر اول را آپلود کنید | |
| 2. تصویر دوم را آپلود کنید | |
| 3. دقت مورد نظر را انتخاب کنید | |
| 4. روی دکمه "تشخیص هویت" کلیک کنید | |
| """ | |
| # بارگذاری مدلهای dlib | |
| print("🔄 در حال بارگذاری مدلهای تشخیص چهره...") | |
| try: | |
| # تشخیص نقاط کلیدی صورت | |
| predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat') | |
| detector = dlib.get_frontal_face_detector() | |
| face_rec_model = dlib.face_recognition_model_v1('dlib_face_recognition_resnet_model_v1.dat') | |
| print("✅ مدلها با موفقیت بارگذاری شدند!") | |
| MODELS_LOADED = True | |
| except Exception as e: | |
| print(f"⚠️ مدلها یافت نشدند. از روش جایگزین استفاده میکنیم... {e}") | |
| MODELS_LOADED = False | |
| # تابع تشخیص چهره و نقاط کلیدی با dlib | |
| def detect_face_landmarks_dlib(image_array): | |
| """تشخیص ۶۸ نقطه کلیدی صورت با dlib""" | |
| if not MODELS_LOADED: | |
| return None, None, None | |
| try: | |
| # تشخیص چهره | |
| faces = detector(image_array, 1) | |
| if len(faces) == 0: | |
| return None, None, None | |
| # گرفتن اولین چهره | |
| face = faces[0] | |
| # تشخیص نقاط کلیدی | |
| shape = predictor(image_array, face) | |
| # استخراج بردار ویژگی چهره | |
| face_descriptor = face_rec_model.compute_face_descriptor(image_array, shape) | |
| face_encoding = np.array(face_descriptor) | |
| # استخراج نقاط کلیدی | |
| landmarks = np.array([[p.x, p.y] for p in shape.parts()]) | |
| return face, landmarks, face_encoding | |
| except Exception as e: | |
| print(f"خطا در تشخیص نقاط کلیدی: {e}") | |
| return None, None, None | |
| # تابع تشخیص چهره جایگزین با OpenCV | |
| def detect_face_features_opencv(image_array): | |
| """تشخیص ویژگیهای صورت با OpenCV پیشرفته""" | |
| try: | |
| # تشخیص چهره | |
| gray = cv2.cvtColor(image_array, cv2.COLOR_RGB2GRAY) | |
| face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') | |
| faces = face_cascade.detectMultiScale(gray, 1.3, 5) | |
| if len(faces) == 0: | |
| return None, None | |
| # گرفتن اولین چهره | |
| x, y, w, h = faces[0] | |
| face_region = image_array[y:y+h, x:x+w] | |
| # استخراج ویژگیهای پیشرفته | |
| # ۱. هیستوگرام گرادیانها (HOG) | |
| gray_face = cv2.cvtColor(face_region, cv2.COLOR_RGB2GRAY) | |
| gray_face = cv2.resize(gray_face, (64, 128)) | |
| # محاسبه گرادیان | |
| gx = cv2.Sobel(gray_face, cv2.CV_32F, 1, 0) | |
| gy = cv2.Sobel(gray_face, cv2.CV_32F, 0, 1) | |
| mag, ang = cv2.cartToPolar(gx, gy) | |
| # هیستوگرام گرادیانهای جهتدار | |
| bins = np.int32(16 * ang / (2 * np.pi)) | |
| bin_cells = bins[:10, :10], bins[10:, :10], bins[:10, 10:], bins[10:, 10:] | |
| mag_cells = mag[:10, :10], mag[10:, :10], mag[:10, 10:], mag[10:, 10:] | |
| hists = [np.bincount(b.ravel(), m.ravel(), 16) for b, m in zip(bin_cells, mag_cells)] | |
| hist = np.hstack(hists) | |
| # ۲. ویژگیهای رنگ و بافت | |
| face_resized = cv2.resize(face_region, (64, 64)) | |
| # هیستوگرام رنگ | |
| color_hist = cv2.calcHist([face_resized], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256]).flatten() | |
| color_hist = color_hist[:128] # گرفتن ۱۲۸ مقدار اول | |
| # ویژگیهای آماری | |
| mean_color = face_resized.mean(axis=(0, 1)) | |
| std_color = face_resized.std(axis=(0, 1)) | |
| # ۳. ویژگیهای الگوی محلی (LBP) | |
| lbp_features = extract_lbp_features(gray_face)[:64] | |
| # ترکیب تمام ویژگیها | |
| features = np.concatenate([ | |
| hist / (hist.sum() + 1e-8), # نرمالسازی هیستوگرام | |
| color_hist / (color_hist.sum() + 1e-8), # نرمالسازی هیستوگرام رنگ | |
| mean_color / 255, | |
| std_color / 255, | |
| lbp_features | |
| ]) | |
| return faces[0], features | |
| except Exception as e: | |
| print(f"خطا در استخراج ویژگیهای OpenCV: {e}") | |
| return None, None | |
| def extract_lbp_features(gray_image): | |
| """استخراج ویژگیهای الگوی محلی باینری (LBP)""" | |
| try: | |
| # تغییر سایز به اندازه ثابت | |
| img = cv2.resize(gray_image, (64, 64)) | |
| # محاسبه LBP | |
| lbp = np.zeros_like(img, dtype=np.uint8) | |
| for i in range(1, img.shape[0]-1): | |
| for j in range(1, img.shape[1]-1): | |
| center = img[i, j] | |
| code = 0 | |
| code |= (img[i-1, j-1] >= center) << 7 | |
| code |= (img[i-1, j] >= center) << 6 | |
| code |= (img[i-1, j+1] >= center) << 5 | |
| code |= (img[i, j+1] >= center) << 4 | |
| code |= (img[i+1, j+1] >= center) << 3 | |
| code |= (img[i+1, j] >= center) << 2 | |
| code |= (img[i+1, j-1] >= center) << 1 | |
| code |= (img[i, j-1] >= center) << 0 | |
| lbp[i, j] = code | |
| # هیستوگرام LBP | |
| hist, _ = np.histogram(lbp.ravel(), bins=256, range=(0, 256)) | |
| return hist | |
| except Exception as e: | |
| print(f"خطا در استخراج LBP: {e}") | |
| return np.zeros(256) | |
| # تابع مقایسه پیشرفته | |
| def compare_faces_advanced(features1, features2, threshold): | |
| """مقایسه پیشرفته دو بردار ویژگی""" | |
| if features1 is None or features2 is None: | |
| return None, None, "❌ خطا در استخراج ویژگیها" | |
| try: | |
| # محاسبه فاصله اقلیدسی | |
| distance = np.linalg.norm(features1 - features2) | |
| # محاسبه شباهت کسینوسی | |
| cos_similarity = np.dot(features1, features2) / (np.linalg.norm(features1) * np.linalg.norm(features2) + 1e-10) | |
| # محاسبه شباهت کلی | |
| similarity = 1.0 / (1.0 + distance) | |
| # تصمیمگیری | |
| is_match = distance <= threshold | |
| return distance, similarity, is_match | |
| except Exception as e: | |
| print(f"خطا در مقایسه: {e}") | |
| return None, None, None | |
| # تابع رسم نقاط کلیدی | |
| def draw_landmarks_on_image(image_array, landmarks, face_rect): | |
| """رسم نقاط کلیدی و مستطیل چهره""" | |
| img_copy = image_array.copy() | |
| # رسم مستطیل دور چهره | |
| if face_rect is not None: | |
| if hasattr(face_rect, 'left'): # dlib rectangle | |
| cv2.rectangle(img_copy, (face_rect.left(), face_rect.top()), | |
| (face_rect.right(), face_rect.bottom()), (0, 255, 0), 3) | |
| else: # OpenCV rectangle | |
| x, y, w, h = face_rect | |
| cv2.rectangle(img_copy, (x, y), (x+w, y+h), (0, 255, 0), 3) | |
| # رسم نقاط کلیدی | |
| if landmarks is not None: | |
| for i, (x, y) in enumerate(landmarks): | |
| # رنگهای مختلف برای نقاط مختلف صورت | |
| if i < 17: # خط فک | |
| color = (255, 0, 0) | |
| elif i < 22: # ابروی راست | |
| color = (0, 255, 0) | |
| elif i < 27: # ابروی چپ | |
| color = (0, 0, 255) | |
| elif i < 36: # بینی | |
| color = (255, 255, 0) | |
| elif i < 48: # چشمها | |
| color = (255, 0, 255) | |
| else: # دهان | |
| color = (0, 255, 255) | |
| cv2.circle(img_copy, (int(x), int(y)), 3, color, -1) | |
| return img_copy | |
| # تابع اصلی پردازش | |
| def process_faces(image1, image2, precision_level): | |
| """پردازش و مقایسه دو تصویر چهره""" | |
| print(f"🔍 شروع پردازش با دقت: {precision_level}") | |
| # تبدیل تصاویر به آرایه | |
| if isinstance(image1, str): | |
| image1_array = np.array(Image.open(image1)) | |
| else: | |
| image1_array = np.array(image1) | |
| if isinstance(image2, str): | |
| image2_array = np.array(Image.open(image2)) | |
| else: | |
| image2_array = np.array(image2) | |
| # تنظیم آستانه | |
| thresholds = { | |
| 'high': 0.4, # دقت بالا | |
| 'medium': 0.5, # دقت متوسط | |
| 'low': 0.6 # دقت پایین | |
| } | |
| threshold = thresholds.get(precision_level, 0.5) | |
| print(f"🔧 آستانه انتخاب شده: {threshold}") | |
| # پردازش تصویر اول | |
| print("📸 پردازش تصویر اول...") | |
| if MODELS_LOADED: | |
| face1, landmarks1, features1 = detect_face_landmarks_dlib(image1_array) | |
| if face1 is None: | |
| features1 = None | |
| else: | |
| face1, features1 = detect_face_features_opencv(image1_array) | |
| landmarks1 = None | |
| # پردازش تصویر دوم | |
| print("📸 پردازش تصویر دوم...") | |
| if MODELS_LOADED: | |
| face2, landmarks2, features2 = detect_face_landmarks_dlib(image2_array) | |
| if face2 is None: | |
| features2 = None | |
| else: | |
| face2, features2 = detect_face_features_opencv(image2_array) | |
| landmarks2 = None | |
| # بررسی خطاها | |
| if features1 is None: | |
| return "❌ خطا: تشخیص چهره در تصویر اول ناموفق بود!", None, None | |
| if features2 is None: | |
| return "❌ خطا: تشخیص چهره در تصویر دوم ناموفق بود!", None, None | |
| # مقایسه چهرهها | |
| print("🔄 در حال مقایسه چهرهها...") | |
| distance, similarity, is_match = compare_faces_advanced(features1, features2, threshold) | |
| if distance is None: | |
| return "❌ خطا در مقایسه ویژگیها!", None, None | |
| # ایجاد تصاویر با نشانگر | |
| img1_processed = draw_landmarks_on_image(image1_array, landmarks1, face1) | |
| img2_processed = draw_landmarks_on_image(image2_array, landmarks2, face2) | |
| # تبدیل به RGB | |
| img1_processed = cv2.cvtColor(img1_processed, cv2.COLOR_BGR2RGB) | |
| img2_processed = cv2.cvtColor(img2_processed, cv2.COLOR_BGR2RGB) | |
| # ایجاد گزارش | |
| result_text = f""" | |
| 🎯 **نتایج تشخیص هویت چهره** | |
| 📊 **آمار فنی:** | |
| - فاصله بردار ویژگی: `{distance:.4f}` | |
| - میزان شباهت: `{similarity:.4f}` | |
| - آستانه انتخابی: `{threshold:.2f}` | |
| 🎭 **نتیجهگیری:** | |
| """ | |
| if is_match: | |
| if distance < 0.3: | |
| confidence = "🔒 **تطبیق با اطمینان بسیار بالا**" | |
| elif distance < 0.4: | |
| confidence = "✅ **تطبیق با اطمینان بالا**" | |
| else: | |
| confidence = "✓ **تطبیق با اطمینان متوسط**" | |
| result_text += f""" | |
| {confidence} | |
| 🟢 **این دو تصویر متعلق به یک شخص هستند** | |
| _تحلیل سیستم:_ | |
| - فاصله محاسبهشده ({distance:.4f}) کمتر از آستانه ({threshold:.2f}) است | |
| - ساختار صورت و ویژگیهای کلیدی بسیار مشابه هستند | |
| - احتمال اینکه این دو تصویر از یک شخص باشند: **{(1-distance)*100:.1f}%** | |
| """ | |
| else: | |
| if distance > 0.7: | |
| confidence = "🔒 **عدم تطبیق با اطمینان بسیار بالا**" | |
| else: | |
| confidence = "❌ **عدم تطبیق با اطمینان متوسط**" | |
| result_text += f""" | |
| {confidence} | |
| 🔴 **این دو تصویر متعلق به اشخاص مختلف هستند** | |
| _تحلیل سیستم:_ | |
| - فاصله محاسبهشده ({distance:.4f}) بیشتر از آستانه ({threshold:.2f}) است | |
| - ساختار صورت و ویژگیهای کلیدی تفاوت قابل توجهی دارند | |
| - شباهت: **{(1-distance)*100:.1f}%** | |
| """ | |
| result_text += f""" | |
| 📈 **جزئیات فنی:** | |
| """ | |
| if MODELS_LOADED: | |
| result_text += "- ✅ استفاده از مدل dlib با ۶۸ نقطه کلیدی\n" | |
| else: | |
| result_text += "- ⚠️ استفاده از روش OpenCV (دقت کمتر)\n" | |
| result_text += f""" | |
| - تعداد ویژگیهای استخراجشده: {len(features1)} | |
| - روش تشخیص: نقاط کلیدی صورت + ساختار هندسی | |
| - دامنه فاصله: ۰ (کاملاً مشابه) تا ۱ (کاملاً متفاوت) | |
| 🛠️ **توضیح فنی:** | |
| سیستم از نقاط کلیدی صورت (چشمها، بینی، دهان، فک) استفاده میکند | |
| و ساختار هندسی صورت را با دقت بالا مقایسه میکند. | |
| """ | |
| return result_text, img1_processed, img2_processed | |
| # ایجاد رابط Gradio | |
| with gr.Blocks(theme=gr.themes.Soft(), title=TITLE) as demo: | |
| # هدر برنامه | |
| gr.HTML(f""" | |
| <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px;"> | |
| <h1 style="font-size: 32px; margin-bottom: 10px;">{TITLE}</h1> | |
| <p style="font-size: 16px; margin: 0;">سامانه هوشمند تشخیص هویت با تحلیل نقاط کلیدی صورت</p> | |
| </div> | |
| """) | |
| # توضیحات | |
| with gr.Row(): | |
| gr.Markdown(DESCRIPTION) | |
| # بخش ورودیها | |
| with gr.Row(): | |
| with gr.Column(): | |
| image1_input = gr.Image(label="📸 تصویر چهره اول", type="pil", height=250) | |
| gr.HTML("<p style='text-align: center; color: gray; font-size: 12px;'>تصویر اول را انتخاب یا آپلود کنید</p>") | |
| with gr.Column(): | |
| image2_input = gr.Image(label="📸 تصویر چهره دوم", type="pil", height=250) | |
| gr.HTML("<p style='text-align: center; color: gray; font-size: 12px;'>تصویر دوم را انتخاب یا آپلود کنید</p>") | |
| # بخش تنظیمات | |
| with gr.Row(): | |
| with gr.Column(): | |
| precision_input = gr.Radio( | |
| choices=[ | |
| ("high", "🟢 دقت بالا (آستانه ۰.۴)"), | |
| ("medium", "🟡 دقت متوسط (آستانه ۰.۵)"), | |
| ("low", "🔴 دقت پایین (آستانه ۰.۶)") | |
| ], | |
| label="🎯 سطح دقت تشخیص", | |
| value='medium', | |
| type='value' | |
| ) | |
| # دکمه پردازش | |
| with gr.Row(): | |
| submit_btn = gr.Button("🚀 شروع تشخیص پیشرفته", variant="primary", size="lg") | |
| # بخش خروجی | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| output_text = gr.Markdown(label="📊 نتایج تشخیص") | |
| with gr.Row(): | |
| with gr.Column(): | |
| output_image1 = gr.Image(label="تصویر اول - نقاط کلیدی", type="numpy", height=250) | |
| with gr.Column(): | |
| output_image2 = gr.Image(label="تصویر دوم - نقاط کلیدی", type="numpy", height=250) | |
| # راهنمای رنگها | |
| gr.HTML(""" | |
| <div style="background: #f8f9fa; padding: 15px; border-radius: 8px; border-left: 4px solid #667eea;"> | |
| <h4 style="margin-top: 0;">🎨 راهنمای رنگ نقاط کلیدی:</h4> | |
| <ul style="margin-bottom: 0;"> | |
| <li>🔵 آبی: خط فک و چانه</li> | |
| <li>🟢 سبز: ابروی راست</li> | |
| <li>🔴 قرمز: ابروی چپ</li> | |
| <li>🟡 زرد: بینی</li> | |
| <li>🟣 بنفش: چشمها</li> | |
| <li>🟡 زرد-سبز: دهان و لبها</li> | |
| <li>🟢 مستطیل سبز: محدوده کل چهره</li> | |
| </ul> | |
| </div> | |
| """) | |
| # نکات مهم | |
| gr.HTML(""" | |
| <div style="background: #fff3cd; padding: 15px; border-radius: 8px; border-left: 4px solid #ffc107;"> | |
| <h4 style="margin-top: 0; color: #856404;">⚠️ نکات مهم:</h4> | |
| <ul style="margin-bottom: 0; color: #856404;"> | |
| <li>برای بهترین نتیجه، از تصاویر با کیفیت بالا و نور مناسب استفاده کنید</li> | |
| <li>چهره باید کامل در تصویر باشد و حداقل ۲۰۰x۲۰۰ پیکسل باشد</li> | |
| <li>این سیستم از ۶۸ نقطه کلیدی صورت برای تشخیص استفاده میکند</li> | |
| <li>در صورت عدم تشخیص، تصویر را کمی بزرگتر کنید</li> | |
| </ul> | |
| </div> | |
| """) | |
| # تنظیمات دکمه | |
| submit_btn.click( | |
| fn=process_faces, | |
| inputs=[image1_input, image2_input, precision_input], | |
| outputs=[output_text, output_image1, output_image2] | |
| ) | |
| # اجرای برنامه | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=True, | |
| show_error=True | |
| ) |