EngReem85 commited on
Commit
8b1d6f5
·
verified ·
1 Parent(s): d1ee1f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -102
app.py CHANGED
@@ -5,15 +5,11 @@ import cv2
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
-
17
  if os.path.exists(model_path):
18
  model = tf.keras.models.load_model(model_path)
19
  print("✅ تم تحميل النموذج المحفوظ.")
@@ -34,7 +30,7 @@ else:
34
  metrics=['accuracy'])
35
 
36
  (x_train, y_train), _ = datasets.mnist.load_data()
37
- x_train = x_train.reshape((60000, 28, 28, 1)).astype('float32') / 255
38
  model.fit(x_train, y_train, epochs=5, validation_split=0.1, verbose=1)
39
  model.save(model_path)
40
  print("✅ تم بناء النموذج وتدريبه وحفظه.")
@@ -43,106 +39,95 @@ else:
43
  # 2. تحميل بيانات الاختبار
44
  # ============================================
45
  (_, _), (test_images, test_labels) = datasets.mnist.load_data()
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. دالة المثال العشوائي
@@ -150,56 +135,43 @@ def predict_sketch(image):
150
  def random_example():
151
  idx = np.random.randint(0, len(test_images))
152
  img = test_images[idx].reshape(28, 28)
153
- # تكبير الصورة لـ Sketchpad
154
- img_large = cv2.resize(img, (280, 280))
155
  img_rgb = np.stack([img_large] * 3, axis=2)
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
- # اختبار النموذج على 100 صورة من MNIST
200
- test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=0)
201
- print(f"✅ دقة النموذج على MNIST: {test_acc:.2%}")
202
  # ============================================
203
- # 8. تشغيل التطبيق
204
  # ============================================
205
  demo.launch()
 
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
  if os.path.exists(model_path):
14
  model = tf.keras.models.load_model(model_path)
15
  print("✅ تم تحميل النموذج المحفوظ.")
 
30
  metrics=['accuracy'])
31
 
32
  (x_train, y_train), _ = datasets.mnist.load_data()
33
+ x_train = x_train.reshape((60000, 28, 28, 1)).astype('float32') / 255.0
34
  model.fit(x_train, y_train, epochs=5, validation_split=0.1, verbose=1)
35
  model.save(model_path)
36
  print("✅ تم بناء النموذج وتدريبه وحفظه.")
 
39
  # 2. تحميل بيانات الاختبار
40
  # ============================================
41
  (_, _), (test_images, test_labels) = datasets.mnist.load_data()
42
+ test_images = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255.0
43
 
44
  # ============================================
45
+ # 3. دالة معالجة الصورة
46
  # ============================================
47
+ def process_image_for_mnist(image_input):
48
  """
49
+ تعالج الصورة المدخلة من Sketchpad لتناسب تنسيق MNIST
50
  """
51
+ if image_input is None:
52
+ raise ValueError("لم يتم إدخال أي صورة!")
53
+
54
+ # التعامل مع مدخلات Sketchpad (تكون Dictionary في الإصدارات الحديثة)
55
+ if isinstance(image_input, dict):
56
+ image = image_input.get('composite', None)
57
+ if image is None:
58
+ image = image_input.get('background', list(image_input.values())[0])
59
+ else:
60
+ image = image_input
61
+
62
+ # تحويل الصورة إلى RGB إذا كانت تحتوي على قناة Alpha (RGBA)
63
+ if image.shape[-1] == 4:
64
+ image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
65
+
66
+ # تحويل إلى تدرج رمادي Gray Scale
67
  if len(image.shape) == 3:
68
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
69
  else:
70
+ gray = image.copy()
71
+
72
+ # تغيير الحجم إلى 28x28
73
  resized = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)
74
+
75
+ # عتبة أوتوماتيكية لجعل الصورة ثنائية (أسود وأبيض)
 
 
 
 
76
  _, binary = cv2.threshold(resized, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
77
+
78
+ # جعل الخلفية سوداء (0) والرسم أبيض (255) كما في MNIST
79
  white_pixels = np.sum(binary == 255)
80
  black_pixels = np.sum(binary == 0)
 
 
 
81
  if white_pixels > black_pixels:
82
  binary = 255 - binary
83
+
84
+ # تطبيع القيم بين 0 و 1
85
  normalized = binary.astype('float32') / 255.0
 
 
86
  reshaped = normalized.reshape(1, 28, 28, 1)
87
+
88
  return normalized, reshaped
89
 
90
  # ============================================
91
+ # 4. دالة التنبؤ
92
  # ============================================
93
  @spaces.GPU
94
  def predict_sketch(image):
95
  try:
96
+ if image is None:
97
+ return {}, None, "⚠️ يرجى الرسم أولاً!"
98
+
99
+ # معالجة الصورة
100
  normalized, reshaped = process_image_for_mnist(image)
101
+
102
+ # التنبؤ بواسطة النموذج
103
  pred = model.predict(reshaped, verbose=0)[0]
104
  predicted_class = int(np.argmax(pred))
105
  confidence = float(np.max(pred))
106
+
107
+ # تحضير نص التصحيح Debug Info
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  top3_idx = np.argsort(pred)[-3:][::-1]
109
+ top3_str = "\n".join([f" {i+1}. الرقم {idx}: {pred[idx]:.2%}" for i, idx in enumerate(top3_idx)])
110
 
111
+ debug_info = f"""📊 معلومات التوقع:
 
112
  - الفئة المتوقعة: {predicted_class}
113
  - نسبة الثقة: {confidence:.2%}
114
+
115
+ 🔝 أعلى 3 احتمالات:
116
  {top3_str}
117
+
118
+ 🔢 جميع الاحتمالات:
119
+ {', '.join([f'{i}: {pred[i]:.1%}' for i in range(10)])}"""
120
+
121
+ # تحويل صورة المعالجة لمصفوفة 3D لعرضها في Gradio بشكل مريح
122
+ display_img = (normalized * 255).astype(np.uint8)
123
+ display_img = cv2.resize(display_img, (140, 140), interpolation=cv2.INTER_NEAREST)
124
+
125
+ # إرجاع المخرجات الثلاثة مباشرة بالتفصيل
126
+ predictions_dict = {str(i): float(pred[i]) for i in range(10)}
127
+ return predictions_dict, display_img, debug_info
128
+
129
  except Exception as e:
130
+ return {}, None, f"❌ حدث خطأ أثناء التوقع: {str(e)}"
 
 
 
 
131
 
132
  # ============================================
133
  # 5. دالة المثال العشوائي
 
135
  def random_example():
136
  idx = np.random.randint(0, len(test_images))
137
  img = test_images[idx].reshape(28, 28)
138
+ img_large = cv2.resize(img, (280, 280), interpolation=cv2.INTER_NEAREST)
 
139
  img_rgb = np.stack([img_large] * 3, axis=2)
140
+ return img_rgb, f"🎲 تم اختيار رقم عشوائي (الرقم الحقيقي: {test_labels[idx]})"
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  # ============================================
143
+ # 6. بناء واجهة Gradio
144
  # ============================================
145
+ with gr.Blocks(title="MNIST Digit Recognizer") as demo:
146
+ gr.Markdown("# 🧠 التعرف على الأرقام المكتوبة بخط اليد (MNIST)")
147
+ gr.Markdown("ارسم رقماً (من 0 إلى 9) في المربع الأيسر، ثم اضغط على زر **توقع**.")
148
 
149
  with gr.Row():
150
  with gr.Column(scale=1):
151
+ sketch = gr.Sketchpad(label="✏️ منطقة الرسم")
152
  with gr.Row():
153
  submit_btn = gr.Button("🔮 توقع", variant="primary")
154
  random_btn = gr.Button("🎲 مثال عشوائي", variant="secondary")
155
+ info = gr.Textbox(label="📌 معلومات التصحيح (Debug Info)", interactive=False, lines=10)
156
+
157
  with gr.Column(scale=1):
158
+ output = gr.Label(num_top_classes=3, label="📊 احتمالات التوقع")
159
+ processed_img = gr.Image(label="🖼️ الصورة بعد المعالجة (ما يراه النموذج)", image_mode="L")
160
+
161
+ # ربط الأحداث والأزرار
162
  submit_btn.click(
163
+ fn=predict_sketch,
164
  inputs=sketch,
165
  outputs=[output, processed_img, info]
166
  )
167
+
168
  random_btn.click(
169
  fn=random_example,
170
  inputs=[],
171
  outputs=[sketch, info]
172
  )
173
+
 
 
174
  # ============================================
175
+ # 7. تشغيل التطبيق
176
  # ============================================
177
  demo.launch()