File size: 6,693 Bytes
59d6247
 
b691765
59d6247
f877e18
f1946ce
 
f877e18
f1946ce
 
59d6247
f877e18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f1946ce
911101b
 
 
 
 
f1946ce
911101b
06d2fa0
8b1d6f5
911101b
8b1d6f5
59d6247
8b1d6f5
 
911101b
 
 
59d6247
 
8b1d6f5
 
 
911101b
59d6247
 
 
8b1d6f5
 
06d2fa0
 
 
8b1d6f5
 
06d2fa0
 
8b1d6f5
59d6247
06d2fa0
8b1d6f5
06d2fa0
 
8b1d6f5
06d2fa0
 
f877e18
 
06d2fa0
f877e18
f1946ce
da1d009
f1946ce
f877e18
f1946ce
911101b
8b1d6f5
911101b
8b1d6f5
06d2fa0
8b1d6f5
59d6247
da1d009
8b1d6f5
f877e18
 
 
 
 
 
 
 
 
 
 
 
 
 
59d6247
 
8b1d6f5
59d6247
8b1d6f5
 
 
 
59d6247
 
 
c052932
59d6247
 
 
 
 
 
 
8b1d6f5
f1946ce
da1d009
f1946ce
8313df0
911101b
 
 
 
 
 
 
 
 
 
 
f1946ce
508baaa
 
06d2fa0
546adda
f1946ce
8b1d6f5
911101b
 
 
da1d009
8b1d6f5
f1946ce
8b1d6f5
da1d009
8b1d6f5
911101b
06d2fa0
8b1d6f5
06d2fa0
 
 
911101b
 
 
 
 
 
8b1d6f5
373292c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import os
import cv2
import numpy as np
import gradio as gr
import spaces

# ============================================
# 1. بناء/تدريب النموذج لحفظه محلياً
# ============================================
model_path = 'mnist_cnn_model.keras'

def ensure_model_exists():
    if not os.path.exists(model_path):
        print("⚠️ جاري تدريب النموذج وحفظه لأول مرة...")
        import tensorflow as tf
        from tensorflow.keras import datasets, layers, models

        model = models.Sequential([
            layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
            layers.MaxPooling2D((2, 2)),
            layers.Conv2D(64, (3, 3), activation='relu'),
            layers.MaxPooling2D((2, 2)),
            layers.Flatten(),
            layers.Dense(128, activation='relu'),
            layers.Dropout(0.5),
            layers.Dense(10, activation='softmax')
        ])
        model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
        
        (x_train, y_train), _ = datasets.mnist.load_data()
        x_train = x_train.reshape((60000, 28, 28, 1)).astype('float32') / 255.0
        model.fit(x_train, y_train, epochs=3, validation_split=0.1, verbose=1)
        model.save(model_path)
        print("✅ تم حفظ النموذج بنجاح.")

ensure_model_exists()

# تحميل بيانات الاختبار للأمثلة العشوائية
import tensorflow as tf
(_, _), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
test_images_norm = test_images.reshape((10000, 28, 28, 1)).astype('float32') / 255.0

# ============================================
# 2. دالة معالجة الصورة الآمنة
# ============================================
def process_image_for_mnist(image_input):
    # التحقق الآمن من الصفر/الغياب بدون تقييم المصفوفة كـ Boolean
    if image_input is None:
        return None, None

    if isinstance(image_input, dict):
        image = image_input.get('composite', None)
        if image is None:
            image = image_input.get('background', None)
        if image is None and len(image_input) > 0:
            image = list(image_input.values())[0]
    else:
        image = image_input

    if image is None or not isinstance(image, np.ndarray) or image.size == 0:
        return None, None

    if len(image.shape) == 3 and image.shape[-1] == 4:
        image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)

    if len(image.shape) == 3:
        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    else:
        gray = image.copy()

    resized = cv2.resize(gray, (28, 28), interpolation=cv2.INTER_AREA)
    _, binary = cv2.threshold(resized, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    if np.sum(binary == 255) > np.sum(binary == 0):
        binary = 255 - binary

    normalized = binary.astype('float32') / 255.0
    reshaped = normalized.reshape(1, 28, 28, 1)

    return normalized, reshaped

loaded_model = None

# ============================================
# 3. دالة التوقع ببيئة ZeroGPU المضمونة
# ============================================
@spaces.GPU
def predict_sketch(image):
    global loaded_model
    try:
        # إصلاح سبب الخطأ: استخدام size أو is None بشكل صريح
        if image is None:
            return {}, None, "⚠️ يرجى الرسم في المربع أو اختيار مثال أولاً!"

        normalized, reshaped = process_image_for_mnist(image)

        if normalized is None:
            return {}, None, "⚠️ تعذر معالجة الصورة المدخلة."

        import tensorflow as tf
        
        gpus = tf.config.list_physical_devices('GPU')
        if gpus:
            try:
                for gpu in gpus:
                    tf.config.experimental.set_memory_growth(gpu, True)
            except RuntimeError:
                pass

        if loaded_model is None:
            loaded_model = tf.keras.models.load_model(model_path)

        preds = loaded_model(reshaped, training=False).numpy()[0]
        predicted_class = int(np.argmax(preds))
        confidence = float(np.max(preds))

        probabilities = {str(i): float(preds[i]) for i in range(10)}

        display_img = (normalized * 255).astype(np.uint8)
        display_img = cv2.resize(display_img, (140, 140), interpolation=cv2.INTER_NEAREST)

        top3_idx = np.argsort(preds)[-3:][::-1]
        top3_str = "\n".join([f"   {i+1}. الرقم {idx}: {preds[idx]:.2%}" for i, idx in enumerate(top3_idx)])

        debug_info = f"""📊 نتائج التوقع :
- الرقم المتوقع: {predicted_class}
- نسبة الثقة: {confidence:.2%}

🔝 أعلى 3 احتمالات:
{top3_str}"""

        return probabilities, display_img, debug_info

    except Exception as e:
        return {}, None, f"❌ خطأ أثناء التوقّع: {str(e)}"

# ============================================
# 4. دالة اختيار مثال عشوائي (تمت إعادتها)
# ============================================
def random_example():
    idx = np.random.randint(0, len(test_images))
    img = test_images[idx]
    img_large = cv2.resize(img, (280, 280), interpolation=cv2.INTER_NEAREST)
    img_rgb = np.stack([img_large] * 3, axis=2)
    return img_rgb, f"🎲 تم اختيار رقم عشوائي (الرقم الحقيقي: {test_labels[idx]})"

# ============================================
# 5. بناء واجهة Gradio
# ============================================
with gr.Blocks(title="MNIST") as demo:
    gr.Markdown("# 🧠 التعرف على الأرقام المكتوبة بخط اليد ")
    
    with gr.Row():
        with gr.Column(scale=1):
            sketch = gr.Sketchpad(label="✏️ منطقة الرسم")
            with gr.Row():
                submit_btn = gr.Button("🔮 توقع", variant="primary")
                random_btn = gr.Button("🎲 مثال عشوائي", variant="secondary")
            info = gr.Textbox(label="📌 معلومات التصحيح", interactive=False, lines=8)
            
        with gr.Column(scale=1):
            output = gr.Label(num_top_classes=3, label="📊 احتمالات التوقع")
            processed_img = gr.Image(label="🖼️ الصورة بعد المعالجة", image_mode="L")

    # ربط الأحداث
    submit_btn.click(
        fn=predict_sketch,
        inputs=sketch,
        outputs=[output, processed_img, info]
    )
    
    random_btn.click(
        fn=random_example,
        inputs=[],
        outputs=[sketch, info]
    )

demo.launch()