تخطَّ إلى المحتوى

🤖 شرح الذكاء الاصطناعي وتعلّم الآلة

تمثيل الصور (Image Representation)

الدرس 44 من 50· ⏱ 6 دقائق قراءة· 🗓 آخر تحديث: ١٩ سبتمبر ٢٠٢٦

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

ما هي الصورة رقميًا؟

الصورة = مصفوفة (أو 3D tensor) من الأرقام. كل رقم يمثّل لون بكسل واحد.

صورة رمادية 28×28:     صورة RGB 28×28:
┌──────────┐           ┌──────────┐
│ 0 ... 255│           │  R       │
│ :        │           │  G       │   كل قناة 28×28
│ :        │           │  B       │   = 3 قنوات
└──────────┘           └──────────┘
2D array                3D tensor (3, 28, 28)

البكسلات والقنوات

Grayscale (رمادي)

import numpy as np
from PIL import Image

img_gray = Image.open("digit_5.png").convert("L")    # "L" = luminance = grayscale
arr = np.array(img_gray)
print(arr.shape)        # (H, W) — بدون قناة
print(arr.dtype)        # uint8
print(arr[0, 0])        # قيمة البكسل (0-255)

RGB (ملوّن)

img_rgb = Image.open("photo.jpg").convert("RGB")
arr = np.array(img_rgb)
print(arr.shape)        # (H, W, 3) — ثلاث قنوات: R, G, B
print(arr[0, 0])        # [R, G, B] عند الزاوية العلوية اليسرى

قنوات أخرى

القنواتالاستخدام
1رمادي (grayscale)
3RGB (صور يومية)
4RGBA (مع شفافية)
3-12+صور طبية/متعددة الأطياف (multispectral)

قيم البكسلات (Pixel ranges)

import numpy as np

# uint8: الأكثر شيوعًا في الصور الخام
arr_uint8 = np.array([[0, 128, 255]], dtype=np.uint8)
print(f"uint8 range: {arr_uint8.min()} to {arr_uint8.max()}")

# float32 بعد التطبيع
arr_float = arr_uint8.astype(np.float32) / 255.0
print(f"float range: {arr_float.min():.3f} to {arr_float.max():.3f}")
# float range: 0.000 to 1.000
الصيغةالمدىالاستخدام
uint80..255صور خام (PIL, JPEG)
float32 [0, 1]0..1بعد ToTensor() في PyTorch
float32 [0, 255]0..255بعض النماذج
float32 [-1, 1]-1..1بعد Normalize(mean=0.5, std=0.5)

HWC vs CHW — ترتيب الأبعاد

هذا مصدر شائع للأخطاء:

HWC (Height, Width, Channels)   ← الترتيب الافتراضي لـ NumPy / PIL
CHW (Channels, Height, Width)  ← الترتيب الافتراضي لـ PyTorch
import numpy as np

arr = np.array([
    [[255, 0, 0], [0, 255, 0]],   # بكسلان: أحمر، أخضر
    [[0, 0, 255], [128, 128, 128]]  # بكسلان: أزرق، رمادي
], dtype=np.uint8)                  # shape: (2, 2, 3) — HWC

print("HWC shape:", arr.shape)      # (2, 2, 3)

# تحويل HWC → CHW
chw = arr.transpose(2, 0, 1)
print("CHW shape:", chw.shape)      # (3, 2, 2)
print("R channel:\n", chw[0])       # [[255, 0], [0, 0]]
print("G channel:\n", chw[1])       # [[0, 255], [0, 0]]
print("B channel:\n", chw[2])       # [[0, 0], [0, 255]]

لماذا يهمّ؟ نموذج PyTorch يتوقّع (N, C, H, W) — الدفعة في البُعد الأوّل. خطأ في الترتيب → خطأ في الـ forward pass.

تحويل PIL ↔ NumPy ↔ PyTorch

import torch
from PIL import Image
import numpy as np
from torchvision import transforms

# PIL → NumPy
img = Image.open("photo.jpg").convert("RGB")
arr_np = np.array(img)                              # (H, W, 3), uint8
print(arr_np.shape, arr_np.dtype)

# NumPy → PIL
img2 = Image.fromarray(arr_np)

# NumPy → PyTorch (يحتاج تطبيع)
arr_chw = arr_np.transpose(2, 0, 1)                 # HWC -> CHW
tensor = torch.from_numpy(arr_chw).float() / 255.0   # uint8 -> float32 [0, 1]
print(tensor.shape, tensor.dtype)                    # (3, H, W), float32

# PIL → PyTorch مباشرة
to_tensor = transforms.ToTensor()
tensor2 = to_tensor(img)                            # (3, H, W), float32 [0, 1]
print(tensor2.shape, tensor2.dtype)
print("Equal:", torch.allclose(tensor, tensor2))

التطبيع (Normalization)

Standardization (Z-score)

import torch

# ImageNet statistics — شائعة في النماذج المدربة مسبقًا
mean = torch.tensor([0.485, 0.456, 0.406])
std  = torch.tensor([0.229, 0.224, 0.225])

# صيغة: (x - mean) / std
img_normalized = (tensor - mean.view(3, 1, 1)) / std.view(3, 1, 1)

Rescaling إلى [-1, 1]

normalize_11 = transforms.Normalize(mean=(0.5, 0.5, 0.5),
                                    std=(0.5, 0.5, 0.5))
img_neg11 = normalize_11(tensor)                    # النطاق: [-1, 1]

Rescaling إلى [0, 1] (فقط)

# ToTensor يفعل هذا تلقائيًا
img_01 = transforms.ToTensor()(img)                 # النطاق: [0, 1]

تغيير الحجم (Resizing)

from torchvision import transforms

resize_224 = transforms.Resize((224, 224))
img_resized = resize_224(tensor)                    # أيّ صورة تصبح 224×224

مشكلة الـ Aspect Ratio:

صورة أصلية:  800 × 600         →  Resize(224, 224)  →  مشوّهة
              (نسبة 4:3)

الحل:
              Resize(256)        يحافظ على النسبة
              CenterCrop(224)    قصّ 224×224 من المركز
pipeline = transforms.Compose([
    transforms.Resize(256),                  # الجانب الأقصر = 256
    transforms.CenterCrop(224),              # قصّ 224×224 من المركز
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225]),
])

Color Spaces (مقدمة)

الصيغةالاستخدام
RGBصور يومية، شاشات
BGROpenCV (تنبيه: ترتيب معكوس عن PIL)
Grayscaleصور قديمة، بعض مهام OCR
HSV (Hue, Saturation, Value)مهام تتطلب فهم اللون
YCbCrفيديو، ضغط

التحويل:

img_gray = img.convert("L")                    # RGB → Grayscale
img_hsv  = img.convert("HSV")

للنماذج الحديثة: ابقَ في RGB. التحويلات الأخرى نادرة الاستخدام.

كيف تصير الصورة batch

صورة واحدة (PIL):     shape (H, W, 3) uint8
                ↓ ToTensor()
tensor واحد:         shape (3, H, W) float32
                ↓ unsqueeze(0)
batch (عيّنة واحدة): shape (1, 3, H, W) float32
                ↓ DataLoader (N عيّنات)
batch كامل:         shape (N, 3, H, W) float32
import torch
from torchvision import transforms
from PIL import Image

img = Image.open("photo.jpg").convert("RGB")
to_tensor = transforms.ToTensor()

x = to_tensor(img)                              # (3, H, W)
x_batch = x.unsqueeze(0)                        # (1, 3, H, W) — batch=1

# في DataLoader يضاف البُعد تلقائيًا

تذكير: شكل batch PyTorch دائمًا (batch_size, channels, height, width). راجع PyTorch: Tensors و Dataset.

Augmentation — مقدمة مفاهيمية

Augmentation = تحويلات عشوائية تُطبَّق على صور التدريب لتعظيم التعلّم:

  • Random crop, flip, rotation, color jitter.

قاعدة أساسيّة (نتناولها بالتفصيل في 92 — خط أنابيب عملي):

التدريب: augmentation عشوائي
التقييم: deterministic (بدون عشوائية)

مثال مبسّط:

from torchvision import transforms

train_transform = transforms.Compose([
    transforms.RandomHorizontalFlip(p=0.5),       # 50% انعكاس
    transforms.RandomCrop(32, padding=4),         # قصّ عشوائي
    transforms.ToTensor(),
    transforms.Normalize(mean, std),
])

eval_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean, std),
])

لا تُطبّق augmentation عشوائي في validation/test.

أخطاء شائعة

  • خلط HWC و CHW: شكل خطأ في PyTorch.
  • نسيان التطبيع: النموذج المدرب مسبقًا يتوقّع إحصاءات محدّدة (مثل ImageNet mean/std).
  • "Resize بدون CenterCrop": يُشوّه الـ aspect ratio.
  • "تطبيق augmentation على test set": يُفسد التقييم.
  • "uint8 في forward pass": قد يعمل لكن غيّره إلى float. (البعض يستخدم model.half() على GPU).
  • "تحويل RGB إلى BGR بدون قصد": خطأ شائع عند الانتقال بين PIL/OpenCV.

ملخّص القرارات عند تحضير صورة

1. تحميل:         PIL.open() / cv2.imread()
2. إلى RGB:       .convert("RGB")  (إن كانت رمادية أو RGBA)
3. تغيير الحجم:   Resize(256) + CenterCrop(224) — يحافظ على النسبة
4. إلى Tensor:    transforms.ToTensor()  — uint8 [0..255] → float32 [0..1]، HWC → CHW
5. تطبيع:         Normalize(mean, std)   — يتطابق مع إحصاءات التدريب
6. batch:         unsqueeze(0) أو DataLoader

الخطوات التالية

🔍 أدوات مفيدة: torchvision.transforms، albumentations لتحويلات متقدّمة.

شرح تمثيل الصور (Image Representation) — الذكاء الاصطناعي وتعلّم الآلة بالعربي
تمثيل الصور (Image Representation)الذكاء الاصطناعي وتعلّم الآلة بالعربي · The Code Fix

📚 لمزيد من التعمّق في الذكاء الاصطناعي وتعلّم الآلة، راجِع توثيق scikit-learn الرسمي.

هل كان هذا الدرس مفيدًا؟