Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -7,7 +7,7 @@ import os
|
|
| 7 |
import spaces
|
| 8 |
|
| 9 |
# ============================================
|
| 10 |
-
# 1. تحميل
|
| 11 |
# ============================================
|
| 12 |
model_path = 'mnist_cnn_model.keras'
|
| 13 |
|
|
@@ -15,14 +15,15 @@ if os.path.exists(model_path):
|
|
| 15 |
model = tf.keras.models.load_model(model_path)
|
| 16 |
print("✅ تم تحميل النموذج المحفوظ.")
|
| 17 |
else:
|
| 18 |
-
print("⚠️
|
| 19 |
model = models.Sequential([
|
| 20 |
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
|
| 21 |
layers.MaxPooling2D((2, 2)),
|
| 22 |
layers.Conv2D(64, (3, 3), activation='relu'),
|
| 23 |
layers.MaxPooling2D((2, 2)),
|
| 24 |
layers.Flatten(),
|
| 25 |
-
layers.Dense(
|
|
|
|
| 26 |
layers.Dense(10, activation='softmax')
|
| 27 |
])
|
| 28 |
model.compile(optimizer='adam',
|
|
@@ -31,57 +32,74 @@ else:
|
|
| 31 |
|
| 32 |
(x_train, y_train), _ = datasets.mnist.load_data()
|
| 33 |
x_train = x_train.reshape((60000, 28, 28, 1)).astype('float32') / 255
|
| 34 |
-
model.fit(x_train, y_train, epochs=
|
| 35 |
model.save(model_path)
|
| 36 |
print("✅ تم بناء النموذج وتدريبه وحفظه.")
|
| 37 |
|
| 38 |
# ============================================
|
| 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
|
| 43 |
|
| 44 |
# ============================================
|
| 45 |
-
# 3. دالة معالجة الصورة والتنبؤ (
|
| 46 |
# ============================================
|
| 47 |
@spaces.GPU
|
| 48 |
def predict_sketch(image):
|
| 49 |
try:
|
| 50 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
if len(image.shape) == 3:
|
| 52 |
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
| 53 |
else:
|
| 54 |
gray = image
|
| 55 |
|
| 56 |
-
#
|
| 57 |
resized = cv2.resize(gray, (28, 28))
|
| 58 |
|
| 59 |
-
# 3. ت
|
| 60 |
-
|
|
|
|
| 61 |
|
| 62 |
-
#
|
| 63 |
-
#
|
| 64 |
-
|
|
|
|
| 65 |
|
| 66 |
-
#
|
| 67 |
-
|
| 68 |
-
pred1 = model.predict(img1, verbose=0)[0]
|
| 69 |
|
| 70 |
-
#
|
| 71 |
-
|
| 72 |
-
|
| 73 |
|
| 74 |
-
#
|
| 75 |
-
|
| 76 |
-
|
|
|
|
| 77 |
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
except Exception as e:
|
| 84 |
-
return {"خطأ": str(e)}
|
| 85 |
|
| 86 |
# ============================================
|
| 87 |
# 4. دالة المثال العشوائي
|
|
@@ -89,7 +107,9 @@ def predict_sketch(image):
|
|
| 89 |
def random_example():
|
| 90 |
idx = np.random.randint(0, len(test_images))
|
| 91 |
img = test_images[idx].reshape(28, 28)
|
| 92 |
-
|
|
|
|
|
|
|
| 93 |
return img_rgb, f"الرقم الحقيقي: {test_labels[idx]}"
|
| 94 |
|
| 95 |
# ============================================
|
|
@@ -101,19 +121,23 @@ with gr.Blocks(title="MNIST Recognizer") as demo:
|
|
| 101 |
|
| 102 |
with gr.Row():
|
| 103 |
with gr.Column(scale=1):
|
| 104 |
-
sketch = gr.Sketchpad(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
with gr.Row():
|
| 106 |
submit_btn = gr.Button("🔮 توقع", variant="primary")
|
| 107 |
random_btn = gr.Button("🎲 مثال عشوائي", variant="secondary")
|
| 108 |
-
info = gr.Textbox(label="📌 معلومات", interactive=False)
|
| 109 |
|
| 110 |
with gr.Column(scale=1):
|
| 111 |
output = gr.Label(num_top_classes=3, label="📊 الاحتمالات")
|
| 112 |
|
| 113 |
-
submit_btn.click(fn=predict_sketch, inputs=sketch, outputs=output)
|
| 114 |
random_btn.click(fn=random_example, inputs=[], outputs=[sketch, info])
|
| 115 |
|
| 116 |
# ============================================
|
| 117 |
-
# 6. تشغيل التطبيق (في النطاق العام
|
| 118 |
# ============================================
|
| 119 |
demo.launch()
|
|
|
|
| 7 |
import spaces
|
| 8 |
|
| 9 |
# ============================================
|
| 10 |
+
# 1. تحميل النموذج (مع تدريب أفضل إن لزم)
|
| 11 |
# ============================================
|
| 12 |
model_path = 'mnist_cnn_model.keras'
|
| 13 |
|
|
|
|
| 15 |
model = tf.keras.models.load_model(model_path)
|
| 16 |
print("✅ تم تحميل النموذج المحفوظ.")
|
| 17 |
else:
|
| 18 |
+
print("⚠️ بناء نموذج جديد مع تدريب أطول...")
|
| 19 |
model = models.Sequential([
|
| 20 |
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
|
| 21 |
layers.MaxPooling2D((2, 2)),
|
| 22 |
layers.Conv2D(64, (3, 3), activation='relu'),
|
| 23 |
layers.MaxPooling2D((2, 2)),
|
| 24 |
layers.Flatten(),
|
| 25 |
+
layers.Dense(128, activation='relu'),
|
| 26 |
+
layers.Dropout(0.5),
|
| 27 |
layers.Dense(10, activation='softmax')
|
| 28 |
])
|
| 29 |
model.compile(optimizer='adam',
|
|
|
|
| 32 |
|
| 33 |
(x_train, y_train), _ = datasets.mnist.load_data()
|
| 34 |
x_train = x_train.reshape((60000, 28, 28, 1)).astype('float32') / 255
|
| 35 |
+
model.fit(x_train, y_train, epochs=5, validation_split=0.1, verbose=1)
|
| 36 |
model.save(model_path)
|
| 37 |
print("✅ تم بناء النموذج وتدريبه وحفظه.")
|
| 38 |
|
| 39 |
# ============================================
|
| 40 |
+
# 2. تحميل بيانات الاختبار
|
| 41 |
# ============================================
|
| 42 |
(_, _), (test_images, test_labels) = datasets.mnist.load_data()
|
| 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 التأكد من شكل الصورة ---
|
| 52 |
+
if isinstance(image, dict):
|
| 53 |
+
image = image['composite']
|
| 54 |
+
|
| 55 |
+
# إذا كانت الصورة (280, 280, 3) أو (280, 280)
|
| 56 |
if len(image.shape) == 3:
|
| 57 |
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
| 58 |
else:
|
| 59 |
gray = image
|
| 60 |
|
| 61 |
+
# --- 3.2 تغيير الحجم إلى 28x28 ---
|
| 62 |
resized = cv2.resize(gray, (28, 28))
|
| 63 |
|
| 64 |
+
# --- 3.3 اكتشاف اتجاه الألوان تلقائياً ---
|
| 65 |
+
# حساب متوسط السطوع
|
| 66 |
+
mean_val = np.mean(resized)
|
| 67 |
|
| 68 |
+
# إذا كان المتوسط > 127 (أي الخلفية فاتحة والكتابة غامقة) → نقلب الألوان
|
| 69 |
+
# لأن MNIST تريد خلفية سوداء (0) وكتابة بيضاء (1)
|
| 70 |
+
if mean_val > 127:
|
| 71 |
+
resized = 255 - resized
|
| 72 |
|
| 73 |
+
# --- 3.4 تطبيق عتبة (Threshold) لتحسين الرسم ---
|
| 74 |
+
_, thresh = cv2.threshold(resized, 128, 255, cv2.THRESH_BINARY)
|
|
|
|
| 75 |
|
| 76 |
+
# --- 3.5 تطبيع إلى [0,1] وإعادة التشكيل ---
|
| 77 |
+
normalized = thresh.astype('float32') / 255.0
|
| 78 |
+
reshaped = normalized.reshape(1, 28, 28, 1)
|
| 79 |
|
| 80 |
+
# --- 3.6 التنبؤ ---
|
| 81 |
+
pred = model.predict(reshaped, verbose=0)[0]
|
| 82 |
+
predicted_class = np.argmax(pred)
|
| 83 |
+
confidence = np.max(pred)
|
| 84 |
|
| 85 |
+
# --- 3.7 معلومات التصحيح (تعرض في مربع المعلومات) ---
|
| 86 |
+
debug_info = f"""
|
| 87 |
+
📊 معلومات المعالجة:
|
| 88 |
+
- متوسط السطوع الأصلي: {mean_val:.1f}
|
| 89 |
+
- تم {'قلب الألوان' if mean_val > 127 else 'الإبقاء على الألوان'}
|
| 90 |
+
- الفئة المتوقعة: {predicted_class}
|
| 91 |
+
- نسبة الثقة: {confidence:.2%}
|
| 92 |
+
- أعلى 3 احتمالات:
|
| 93 |
+
"""
|
| 94 |
+
top3_idx = np.argsort(pred)[-3:][::-1]
|
| 95 |
+
for i, idx in enumerate(top3_idx):
|
| 96 |
+
debug_info += f"\n {i+1}. {idx}: {pred[idx]:.2%}"
|
| 97 |
+
|
| 98 |
+
# إرجاع الاحتمالات والمعلومات
|
| 99 |
+
return {str(i): float(pred[i]) for i in range(10)}, debug_info
|
| 100 |
+
|
| 101 |
except Exception as e:
|
| 102 |
+
return {"خطأ": str(e)}, f"❌ خطأ: {str(e)}"
|
| 103 |
|
| 104 |
# ============================================
|
| 105 |
# 4. دالة المثال العشوائي
|
|
|
|
| 107 |
def random_example():
|
| 108 |
idx = np.random.randint(0, len(test_images))
|
| 109 |
img = test_images[idx].reshape(28, 28)
|
| 110 |
+
# تكبير الصورة لتظهر بشكل واضح في Sketchpad
|
| 111 |
+
img_large = cv2.resize(img, (280, 280))
|
| 112 |
+
img_rgb = np.stack([img_large] * 3, axis=2)
|
| 113 |
return img_rgb, f"الرقم الحقيقي: {test_labels[idx]}"
|
| 114 |
|
| 115 |
# ============================================
|
|
|
|
| 121 |
|
| 122 |
with gr.Row():
|
| 123 |
with gr.Column(scale=1):
|
| 124 |
+
sketch = gr.Sketchpad(
|
| 125 |
+
label="✏️ ارسم هنا",
|
| 126 |
+
brush_radius=12, # تحكم في سمك الفرشاة
|
| 127 |
+
brush_color="#FFFFFF" # لون الفرشاة (أبيض على خلفية سوداء)
|
| 128 |
+
)
|
| 129 |
with gr.Row():
|
| 130 |
submit_btn = gr.Button("🔮 توقع", variant="primary")
|
| 131 |
random_btn = gr.Button("🎲 مثال عشوائي", variant="secondary")
|
| 132 |
+
info = gr.Textbox(label="📌 معلومات التصحيح", interactive=False, lines=8)
|
| 133 |
|
| 134 |
with gr.Column(scale=1):
|
| 135 |
output = gr.Label(num_top_classes=3, label="📊 الاحتمالات")
|
| 136 |
|
| 137 |
+
submit_btn.click(fn=predict_sketch, inputs=sketch, outputs=[output, info])
|
| 138 |
random_btn.click(fn=random_example, inputs=[], outputs=[sketch, info])
|
| 139 |
|
| 140 |
# ============================================
|
| 141 |
+
# 6. تشغيل التطبيق (في النطاق العام)
|
| 142 |
# ============================================
|
| 143 |
demo.launch()
|