EngReem85 commited on
Commit
06d2fa0
·
verified ·
1 Parent(s): 16c0125

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -47
app.py CHANGED
@@ -5,9 +5,12 @@ import cv2
5
  from tensorflow.keras import datasets, layers, models
6
  import os
7
  import spaces
 
 
 
8
 
9
  # ============================================
10
- # 1. تحميل النموذج (مع تدريب أفضل إن لزم)
11
  # ============================================
12
  model_path = 'mnist_cnn_model.keras'
13
 
@@ -43,65 +46,106 @@ else:
43
  test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255
44
 
45
  # ============================================
46
- # 3. دالة التنبؤ (ذكية وتظهر معلومات التصحيح)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  # ============================================
48
  @spaces.GPU
49
  def predict_sketch(image):
50
  try:
51
- # 3.1 التأكد من أن الصورة بصيغة numpy
52
- if isinstance(image, dict):
53
- image = image['composite']
54
-
55
- # 3.2 تحويل إلى تدرج رمادي
56
- if len(image.shape) == 3:
57
- gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
58
- else:
59
- gray = image
60
-
61
- # 3.3 تغيير الحجم إلى 28x28
62
- resized = cv2.resize(gray, (28, 28))
63
-
64
- # 3.4 اكتشاف اتجاه الألوان تلقائياً
65
- mean_val = np.mean(resized)
66
- if mean_val > 127:
67
- # الخلفية فاتحة، نقلب الألوان
68
- resized = 255 - resized
69
- flip_status = "تم قلب الألوان (خلفية فاتحة → خلفية سوداء)"
70
- else:
71
- flip_status = "الإبقاء على الألوان (خلفية سوداء بالفعل)"
72
 
73
- # 3.5 تطبيق عتبة لتحسين الرسم
74
- _, thresh = cv2.threshold(resized, 128, 255, cv2.THRESH_BINARY)
75
-
76
- # 3.6 تطبيع وإعادة تشكيل
77
- normalized = thresh.astype('float32') / 255.0
78
- reshaped = normalized.reshape(1, 28, 28, 1)
79
-
80
- # 3.7 التنبؤ
81
  pred = model.predict(reshaped, verbose=0)[0]
82
  predicted_class = int(np.argmax(pred))
83
  confidence = float(np.max(pred))
84
 
85
- # 3.8 بناء معلومات التصحيح
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  top3_idx = np.argsort(pred)[-3:][::-1]
87
  top3_str = "\n".join([f" {i+1}. {idx}: {pred[idx]:.2%}" for i, idx in enumerate(top3_idx)])
 
88
  debug_info = f"""
89
  📊 معلومات المعالجة:
90
- - متوسط السطوع الأصلي: {mean_val:.1f}
91
- - {flip_status}
92
  - الفئة المتوقعة: {predicted_class}
93
  - نسبة الثقة: {confidence:.2%}
94
  - أعلى 3 احتمالات:
95
  {top3_str}
 
 
96
  """
97
- # 3.9 إرجاع الاحتمالات ومعلومات التصحيح
98
- return {str(i): float(pred[i]) for i in range(10)}, debug_info
 
 
 
 
 
99
 
100
  except Exception as e:
101
- return {"خطأ": str(e)}, f"❌ خطأ: {str(e)}"
 
 
 
 
102
 
103
  # ============================================
104
- # 4. دالة المثال العشوائي
105
  # ============================================
106
  def random_example():
107
  idx = np.random.randint(0, len(test_images))
@@ -112,28 +156,48 @@ def random_example():
112
  return img_rgb, f"الرقم الحقيقي: {test_labels[idx]}"
113
 
114
  # ============================================
115
- # 5. بناء واجهة Gradio (بدون وسائط غير مدعومة)
 
 
 
 
 
 
 
 
 
 
 
116
  # ============================================
117
  with gr.Blocks(title="MNIST Recognizer") as demo:
118
  gr.Markdown("# 🧠 التعرف على الأرقام المكتوبة بخط اليد")
119
  gr.Markdown("ارسم رقماً (0-9) في المربع، أو اضغط على زر **مثال عشوائي**.")
120
-
121
  with gr.Row():
122
  with gr.Column(scale=1):
123
- # 🔥 حذف brush_radius و brush_color نهائياً
124
  sketch = gr.Sketchpad(label="✏️ ارسم هنا")
125
  with gr.Row():
126
  submit_btn = gr.Button("🔮 توقع", variant="primary")
127
  random_btn = gr.Button("🎲 مثال عشوائي", variant="secondary")
128
- info = gr.Textbox(label="📌 معلومات التصحيح", interactive=False, lines=10)
129
-
130
  with gr.Column(scale=1):
131
  output = gr.Label(num_top_classes=3, label="📊 الاحتمالات")
132
-
133
- submit_btn.click(fn=predict_sketch, inputs=sketch, outputs=[output, info])
134
- random_btn.click(fn=random_example, inputs=[], outputs=[sketch, info])
 
 
 
 
 
 
 
 
 
 
135
 
136
  # ============================================
137
- # 6. تشغيل التطبيق (في النطاق العام لـ ZeroGPU)
138
  # ============================================
139
  demo.launch()
 
5
  from tensorflow.keras import datasets, layers, models
6
  import os
7
  import spaces
8
+ import matplotlib.pyplot as plt
9
+ from io import BytesIO
10
+ import base64
11
 
12
  # ============================================
13
+ # 1. تحميل النموذج
14
  # ============================================
15
  model_path = 'mnist_cnn_model.keras'
16
 
 
46
  test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255
47
 
48
  # ============================================
49
+ # 3. دالة معالجة الصورة وعرضها
50
+ # ============================================
51
+ def process_image_for_mnist(image):
52
+ """
53
+ تعالج الصورة لتطابق تنسيق MNIST وتعيد الصورة المعالجة + المصفوفة
54
+ """
55
+ # 3.1 التأكد من الشكل
56
+ if isinstance(image, dict):
57
+ image = image['composite']
58
+
59
+ # 3.2 تحويل إلى تدرج رمادي
60
+ if len(image.shape) == 3:
61
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
62
+ else:
63
+ gray = image
64
+
65
+ # 3.3 تغيير الحجم إلى 28x28
66
+ resized = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)
67
+
68
+ # 3.4 **معالجة قوية للألوان**
69
+ # MNIST: خلفية سوداء (قيمة 0) وكتابة بيضاء (قيمة 1)
70
+ # نقوم بتحويل الصورة إلى ثنائية (أسود/أبيض) مع عتبة ذكية
71
+
72
+ # حساب العتبة التلقائية باستخدام Otsu
73
+ _, binary = cv2.threshold(resized, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
74
+
75
+ # حساب متوسط البيكسلات البيضاء
76
+ white_pixels = np.sum(binary == 255)
77
+ black_pixels = np.sum(binary == 0)
78
+
79
+ # إذا كان عدد البيكسلات البيضاء > السوداء، هذا يعني أن الخلفية بيضاء
80
+ # نقلب الألوان لجعل الخلفية سوداء
81
+ if white_pixels > black_pixels:
82
+ binary = 255 - binary
83
+
84
+ # تطبيع إلى [0,1]
85
+ normalized = binary.astype('float32') / 255.0
86
+
87
+ # إعادة التشكيل
88
+ reshaped = normalized.reshape(1, 28, 28, 1)
89
+
90
+ return normalized, reshaped
91
+
92
+ # ============================================
93
+ # 4. دالة التنبؤ (مع عرض الصورة المعالجة)
94
  # ============================================
95
  @spaces.GPU
96
  def predict_sketch(image):
97
  try:
98
+ # 4.1 معالجة الصورة
99
+ normalized, reshaped = process_image_for_mnist(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
+ # 4.2 التنبؤ
 
 
 
 
 
 
 
102
  pred = model.predict(reshaped, verbose=0)[0]
103
  predicted_class = int(np.argmax(pred))
104
  confidence = float(np.max(pred))
105
 
106
+ # 4.3 تحويل الصورة المعالجة إلى صورة لعرضها
107
+ fig, ax = plt.subplots(figsize=(2, 2))
108
+ ax.imshow(normalized, cmap='gray')
109
+ ax.set_title(f'الصورة المعالجة\n(يتوقع: {predicted_class})')
110
+ ax.axis('off')
111
+
112
+ # حفظ الصورة في ذاكرة مؤقتة
113
+ buf = BytesIO()
114
+ plt.savefig(buf, format='png', bbox_inches='tight')
115
+ buf.seek(0)
116
+ processed_image = base64.b64encode(buf.getvalue()).decode('utf-8')
117
+ plt.close()
118
+
119
+ # 4.4 بناء معلومات التصحيح
120
  top3_idx = np.argsort(pred)[-3:][::-1]
121
  top3_str = "\n".join([f" {i+1}. {idx}: {pred[idx]:.2%}" for i, idx in enumerate(top3_idx)])
122
+
123
  debug_info = f"""
124
  📊 معلومات المعالجة:
 
 
125
  - الفئة المتوقعة: {predicted_class}
126
  - نسبة الثقة: {confidence:.2%}
127
  - أعلى 3 احتمالات:
128
  {top3_str}
129
+ - توزيع الاحتمالات الكامل:
130
+ {', '.join([f'{i}: {pred[i]:.2%}' for i in range(10)])}
131
  """
132
+
133
+ # 4.5 إرجاع النتائج
134
+ return {
135
+ 'prediction': {str(i): float(pred[i]) for i in range(10)},
136
+ 'processed_image': f'data:image/png;base64,{processed_image}',
137
+ 'debug_info': debug_info
138
+ }
139
 
140
  except Exception as e:
141
+ return {
142
+ 'prediction': {"خطأ": 1.0},
143
+ 'processed_image': '',
144
+ 'debug_info': f"❌ خطأ: {str(e)}"
145
+ }
146
 
147
  # ============================================
148
+ # 5. دالة المثال العشوائي
149
  # ============================================
150
  def random_example():
151
  idx = np.random.randint(0, len(test_images))
 
156
  return img_rgb, f"الرقم الحقيقي: {test_labels[idx]}"
157
 
158
  # ============================================
159
+ # 6. دالة تحديث الواجهة بعد التنبؤ
160
+ # ============================================
161
+ def update_outputs(image):
162
+ result = predict_sketch(image)
163
+ return (
164
+ result['prediction'],
165
+ result['processed_image'],
166
+ result['debug_info']
167
+ )
168
+
169
+ # ============================================
170
+ # 7. بناء واجهة Gradio
171
  # ============================================
172
  with gr.Blocks(title="MNIST Recognizer") as demo:
173
  gr.Markdown("# 🧠 التعرف على الأرقام المكتوبة بخط اليد")
174
  gr.Markdown("ارسم رقماً (0-9) في المربع، أو اضغط على زر **مثال عشوائي**.")
175
+
176
  with gr.Row():
177
  with gr.Column(scale=1):
 
178
  sketch = gr.Sketchpad(label="✏️ ارسم هنا")
179
  with gr.Row():
180
  submit_btn = gr.Button("🔮 توقع", variant="primary")
181
  random_btn = gr.Button("🎲 مثال عشوائي", variant="secondary")
182
+ info = gr.Textbox(label="📌 معلومات التصحيح", interactive=False, lines=12)
183
+
184
  with gr.Column(scale=1):
185
  output = gr.Label(num_top_classes=3, label="📊 الاحتمالات")
186
+ processed_img = gr.Image(label="🖼️ الصورة بعد المعالجة (ما يراه النموذج)", height=150)
187
+
188
+ # ربط الأزرار
189
+ submit_btn.click(
190
+ fn=update_outputs,
191
+ inputs=sketch,
192
+ outputs=[output, processed_img, info]
193
+ )
194
+ random_btn.click(
195
+ fn=random_example,
196
+ inputs=[],
197
+ outputs=[sketch, info]
198
+ )
199
 
200
  # ============================================
201
+ # 8. تشغيل التطبيق
202
  # ============================================
203
  demo.launch()