الرؤية الحاسوبية | معالجة الصور

المعالجة الأولية للصور

فهم أساسيات الصور من البكسلات إلى RGB، وكيفية تجهيز الصورة قبل تدريب نماذج الذكاء الاصطناعي.

Pixels RGB Preprocessing OpenCV

ما هي الصورة فعلياً؟

الصورة ليست مجرد شكل جميل، بل هي مجموعة من البكسلات الصغيرة جدًا.

كل بكسل يمثل نقطة واحدة في الصورة، وله قيمة تُظهر شدة الضوء أو اللون في هذا الموضع.

إذا كانت الصورة سوداء وبيضاء، فكل بكسل غالباً يأخذ قيمة بين 0 و 255.

التعريف: البكسل هو أصغر وحدة في الصورة، مثل نقطة صغيرة من المربعات الصغيرة التي تتكون منها الصورة.
10
35
200
80
120
90
170
210
255

مثال مبسط لمصفوفة 3x3 من قيم البكسلات

ما هو RGB؟

Red

القناة الحمراء

Green

القناة الخضراء

Blue

القناة الزرقاء

في نظام RGB، كل بكسل يحتوي على ثلاثة قيم: Red، Green، و Blue. كل قيمة تقع عادةً بين 0 و 255. مثال: (255, 0, 0) يعني أحمر كامل، و (0, 0, 255) يعني أزرق كامل، بينما (255, 255, 255) يعني أبيض.

لماذا نحتاج المعالجة الأولية للصور؟

📏

تغيير الحجم

نضبط صورة لمقاييس ثابتة في الشبكات العصبية.

التحويل إلى grayscale

نقل الصورة إلى درجات الرمادي لتبسيط المعالجة.

🧼

إزالة الضوضاء

نقلل التشويش أو الضجيج الناتج عن التصوير.

📊

التطبيع

نحوّل القيم إلى نطاق مناسب مثل 0 إلى 1.

خطوات شائعة في المعالجة الأولية

  • • قراءة الصورة من الملف أو الكاميرا.
  • • تحويل الصورة إلى RGB أو grayscale حسب الحاجة.
  • • تغيير الحجم إلى أبعاد ثابتة مثل 224x224.
  • • تطبيع القيم إلى النطاق [0, 1] أو [-1, 1].
  • • تطبيق فلترة أو إزالة الضوضاء أو تحسين التباين.
  • • تجهيز البيانات لكي تكون جاهزة للـ CNN أو النموذج.

كيف تبدو الصورة في Python؟

from PIL import Image

img = Image.open("cat.jpg").convert("RGB")
print(img.size)
print(img.getpixel((0, 0)))

gray = img.convert("L")
resized = img.resize((224, 224))
print(resized.size)

مثال عملي: استخراج قيمة بكسل

import numpy as np
from PIL import Image

img = Image.open("sample.png").convert("RGB")
arr = np.array(img)
print(arr.shape)       # (height, width, 3)
print(arr[0, 0])      # [R, G, B]
print(arr[0, 0, 0])   # قيمة Red

عندما تكون الصورة في Python، فإنها غالباً تُخزن كـ array ثلاثي الأبعاد.

أبعاد المصفوفة تكون عادةً مثل: (الارتفاع, العرض, 3) عندما تكون الصورة RGB.

الرقم 3 يمثل القنوات: الأحمر، الأخضر، والأزرق.

ملاحظة: الصورة اللون RGB ليست مجرد 2D، بل هي 3D في الحقيقة: ارتفاع × عرض × قنوات الألوان.

أمثلة عملية على المعالجة الأولية

🖼️

إعادة القياس

تغيير حجم الصورة إلى 128x128 أو 224x224 لتناسب النموذج.

🌫️

إزالة الضوضاء

تخفيف التشويش للتحسين في التعرف أو التصنيف.

🧪

التطبيع

ضبط القيم لتصبح مناسبة للتدريب مثل تقسيمها على 255.

كود كامل: قراءة، تحويل، وتطبيع الصورة

import numpy as np
from PIL import Image

img = Image.open("example.jpg").convert("RGB")
img = img.resize((224, 224))
arr = np.array(img) / 255.0

print(arr.shape)
print(arr.min(), arr.max())
print(arr[0, 0])
هذه الخطوات تُعد الصورة جاهزة لأن يستخدمها نموذج التعلم العميق، لأن النموذج يحتاج بيانات موحدة وقريبة من الشكل المطلوب.

كيف يعمل Convolution في الشبكات الالتفافية؟

1) الصورة
0
1
1
0
0
0
1
1
1
0
0
0
1
0
0
0
0
1
1
1
0
0
1
1
0
2) فلتر (Kernel)
1
0
-1
1
0
-1
1
0
-1
3) Feature Map
4
2
1
2
3
1
1
2
4
الفلتر ينزلق فوق الصورة، ويأخذ جزءًا صغيرًا من البكسلات، ثم يضرب كل قيمة في الوزن المناسب، ثم يجمع النتائج. هذا يساعد الشبكة على اكتشاف الحواف أو الخطوط أو الأنماط الصغيرة مثل الزاوية أو الخط المستقيم.

ما معنى Feature Extraction؟

🔴

حافة

الكشف عن حدود الأشكال.

🔵

خطوط

اكتشاف الخطوط المستقيمة والمنحنية.

🟢

أنماط

ملاحظة التفاصيل المهمة داخل الصورة.

الشبكة لا "تقرأ" الصورة كقائمة أرقام فقط، بل تتعلم اكتشاف ميزات مثل الحواف والدوائر والأشكال. كل طبقة تكتشف تفاصيل أكثر تعقيدًا مع تقدم الشبكة.

ما هو Downsampling أو Pooling؟

قبل التصغير
5
9
2
4
8
3
7
1
6
2
8
5
1
4
9
6
بعد Max Pooling (2x2)
9
7
8
9
في Max Pooling، نأخذ أكبر قيمة داخل كل نافذة 2x2. هذا يقلل حجم الصورة ويساعد النموذج على التركيز على أهم المعلومات، مع تقليل الوقت والحسابات.

هل CNN تعمل كأنها عيون ذكية؟

👀
الخطوة 1

تقرأ الصورة

🔍
الخطوة 2

تكتشف الحواف

🧠
الخطوة 3

تجميع الأنماط

الخطوة 4

تتخذ القرار

في البداية تتعلم CNN أن هناك خطوطاً وأشكالاً، ثم تتدرج نحو اكتشاف أشكال أكثر تعقيداً مثل الأرقام أو الحيوانات أو الأغراض. هذا هو سبب نجاح CNN في الصور.

أين يتم تدريب CNN؟ على MNIST من Keras

عينات من مجموعة البيانات MNIST samples
0
1
2
3
4
MNIST Dataset handwritten digits

مجموعة MNIST تحتوي على أرقام مكتوبة بخط اليد من 0 إلى 9، وكل صورة 28×28 بكسل.

توفر Keras مجموعة بيانات جاهزة باسم mnist.

أنت لا تحتاج إلى تنزيلها يدوياً، بل يمكن تحميلها مباشرة باستخدام TensorFlow/Keras.

كل صورة تُخزن على شكل مصفوفة 28×28، وكل مثال له تسمية رقمية من 0 إلى 9.

البيانات: x_train, y_train, x_test, y_test

الكود: تدريب CNN على MNIST من Keras

import tensorflow as tf
from tensorflow.keras import layers, models

# تحميل بيانات MNIST من Keras
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

# إعادة الشكل إلى (عدد الصور, 28, 28, 1)
x_train = x_train.reshape(-1, 28, 28, 1).astype('float32') / 255.0
x_test = x_test.reshape(-1, 28, 28, 1).astype('float32') / 255.0

# تحويل التصنيفات إلى one-hot
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)

# بناء نموذج CNN
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.Dense(10, activation='softmax')
])

model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5, batch_size=64, validation_data=(x_test, y_test))

loss, acc = model.evaluate(x_test, y_test)
print('Test accuracy:', acc)
في هذا الكود، يتم تحميل مجموعة MNIST مباشرة من Keras، ثم المعالجة الأولية، ثم تدريب نموذج CNN لاكتشاف الأرقام من 0 إلى 9.

كيف يقرأ النموذج الرقم؟

1

يعرض الصورة الرقمية المرسومة بيد الإنسان.

2

يلاحظ الحواف والأنماط داخل الصورة.

3

يقلل الحجم ويحفظ أهم المعلومات.

4

يقرر: هذا الرقم هو 7 أو 3 أو 9.

ماذا يحدث داخل CNN بعد اكتشاف الحواف؟

الطبقة الأولى: Conv

تكتشف الحواف والخطوط الأولية.

الطبقة الثانية: Pooling
9
7
8
9

تخفف الحجم وتبقي أهم التفاصيل فقط.

الطبقة النهائية: Dense
0
1
2
3
4

تقرر أخيراً ما هو الرقم أو الكائن.

ما هي Flatten و Dense؟

layers.Flatten()
layers.Dense(128, activation='relu')
layers.Dense(10, activation='softmax')

Flatten: يحول الصورة من شكل مصفوفة إلى صف واحد من الأرقام، لكي يمكنها الدخول إلى طبقة معيارية.

Dense: هي طبقات عصبية عادية تتعلم العلاقات بين الميزات لاكتشاف النتيجة النهائية.

بعد Flatten، لا تصبح الصورة صوراً بل تصبح أرقاماً منظمة يمكن للطبقات الكثيفة أن تتعامل معها.

لماذا نستخدم ReLU؟

ReLU يعني:

إذا كان الرقم سالبًا، يصبح 0.

إذا كان الرقم موجبًا، يبقى كما هو.

ReLU(x) = max(0, x)
def relu(x):
    if x < 0:
        return 0
    return x

print(relu(-3))  # 0
print(relu(5))   # 5
ReLU يساعد الشبكة على اختيار ما هو مهم من البيانات، ويمنع بعض القيم السلبية من التأثير، مما يجعل التعلم أسرع وأكثر فاعلية.

كيف يتم التدريب فعلياً؟

1

تبدأ الشبكة بتخمينات عشوائية.

2

تُقارن النتيجة مع الإجابة الصحيحة.

3

تُصحح الأوزان لتقليل الخطأ.

4

تكرر هذه العملية ملايين المرات.

هذا هو تدريب الشبكة: كل مرة تتعلم قليلاً أكثر عن الصورة لتقليل الخطأ، حتى تصبح قادرة على التمييز بدقة أكبر.

لماذا CNN أفضل من الشبكات العادية على الصور؟

الشبكات العادية تتعامل مع كل بكسل بشكل منفصل، وتسهل عليها أن تنسى العلاقات بين البكسلات القريبة.

CNN تحافظ على المواقع النسبية للبكسلات، لذلك تعرف أن بعض الخطوط تتجمع لتكوّن زاوية، أو أن بعض الأجزاء تشكل رقمًا أو كائنًا.

هذا هو سر CNN: فهي لا تنظر فقط إلى كل نقطة وحدها، بل تنظر إلى أنماط المجموعات الصغيرة داخل الصورة، ثم تتعلم من هذه الأنماط شيئًا أعمق.

ملخص صغير جدًا

• الصورة تتكون من بكسلات.

• CNN يمرر فلترًا صغيرًا فوق الصورة لاكتشاف الحواف والأنماط.

• Pooling يقلل الحجم ويبقي أهم المعلومات.

• Flatten يحول الصور إلى أرقام قابلة للفهم.

• Dense يقرر النتيجة النهائية.

• MNIST من Keras يعطي صور أرقام مكتوبة بخط اليد لتدريب النموذج.

Streamlit: ارفع صورة أو ارسم رقمًا ثم استخدم النموذج المصدّر

import streamlit as st
import numpy as np
from PIL import Image, ImageOps
import tensorflow as tf

st.title("MNIST Digit Predictor")

model = tf.keras.models.load_model("mnist_cnn_model.h5")

uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])

if uploaded_file is not None:
    image = Image.open(uploaded_file).convert("L")
    image = ImageOps.invert(image)
    image = image.resize((28, 28))
    arr = np.array(image).astype("float32") / 255.0
    arr = arr.reshape(1, 28, 28, 1)

    pred = model.predict(arr)
    digit = int(np.argmax(pred))
    confidence = float(np.max(pred))

    st.image(image, caption="Uploaded image", width=180)
    st.success(f"Predicted digit: {digit} | Confidence: {confidence:.2%}")

هذا الكود يسمح للمستخدم برفع صورة رقم مكتوب بخط اليد، أو رسم رقم داخل تطبيق Streamlit، ثم استخدام النموذج المصدّر لمعرفة النتيجة.

تستخدم الصورة كصورة رمادية 28×28، ثم تُعاد تجهيزها حتى تكون مناسبة لإدخالها إلى الشبكة CNN.

الفكرة: صورة المستخدم → تحويل إلى 28x28 → تطبيع → نموذج → التنبؤ بالرقم.
إذا أردت الرسم مباشرة: يمكنك استخدام Streamlit Canvas أو مكتبة مثل streamlit_drawable_canvas.

نسخة Streamlit مع لوحة رسم

import streamlit as st
import numpy as np
from PIL import Image
import tensorflow as tf
from streamlit_drawable_canvas import st_canvas

st.title("Draw a Digit and Predict")

model = tf.keras.models.load_model("mnist_cnn_model.h5")

canvas_result = st_canvas(
    fill_color="black",
    stroke_width=12,
    background_color="black",
    width=280,
    height=280,
    drawing_mode="freedraw",
    key="canvas",
)

if canvas_result.image_data is not None:
    img = Image.fromarray(canvas_result.image_data.astype("uint8")).convert("L")
    img = img.resize((28, 28))
    arr = np.array(img).astype("float32") / 255.0
    arr = arr.reshape(1, 28, 28, 1)

    pred = model.predict(arr)
    digit = int(np.argmax(pred))
    prob = float(np.max(pred))

    st.success(f"Predicted digit: {digit} | Probability: {prob:.2%}")