انتقل إلى المحتوى الرئيسي

الوحدة 7 — تقطيع النُسَخ مع Mask R-CNN

الوحدة السابقة أعطتنا قناعًا يقول «هذه بكسلات سيّارة»، لكنّها لم تُفرّق بين سيّارتين ملتصقتين. لعدّ السيّارات في تقاطع طرق مزدحم، هذا التمييز جوهري: سيّارتان متوقّفتان جنبًا إلى جنب لهما قناع دلالي واحد، بينما نحتاج إلى معرفة أنّهما جسمان متمايزان. هنا يدخل تقطيع النُسَخ، الذي يُنتج قناعًا مستقلًّا لكلّ جسم عدّي.

المبدأ: كشف + قناع لكلّ جسم

تقطيع النُسَخ يجمع بين الكشف والتقطيع الدلالي: أوّلًا نكشف الأجسام (صناديق + أصناف + ثقة)، ثم لكلّ جسم مكتشف نُنتج قناعًا مستقلًّا يحدّد بكسلاته بدقّة داخل صندوقه. النتيجة قائمة من الأجسام، لكلّ منها صنف، وثقة، وصندوق، وقناع.

هذه مقاربة مختلفة جوهريًا عن التقطيع الدلالي. في التقطيع الدلالي، القرار يُتَّخذ لكلّ بكسل مستقلّ عن كون هذا البكسل ينتمي إلى «الجسم رقم 1» أو «الجسم رقم 2»؛ في تقطيع النُسَخ، القرار يبدأ من الجسم كوحدة ثم يُفصَّل إلى بكسلاته.

Mask R-CNN: القناع فوق Faster R-CNN

Mask R-CNN (He et al.، 2017) هو المرجع الرائد. الفكرة بسيطة ومقنعة: نأخذ Faster R-CNN من الوحدة 2، ونُضيف رأس ثالث بالتوازي مع رأسي التصنيف والانحدار: رأس قناع ينتج قناعًا صغيرًا (مثلًا 28×28) لكلّ اقتراح.

المعمارية:

الصورة → المُشفِّر (ResNet + FPN)

شبكة اقتراح المناطق (RPN)

RoI Align ← الجزء الحاسم
↓ ↓ ↓
رأس تصنيف رأس صندوق رأس قناع

RoI Align الذي رأيناه في الوحدة 2 يصبح هنا أساسيًا. RoI Pooling القديم كان يقرّب الإحداثيات، مُدخِلًا انحيازًا بمقدار نصف بكسل — مقبول للتصنيف، كارثي للقناع. الاستيفاء الثنائي الخطّي لـRoI Align يحفظ المحاذاة الدقيقة، ما يُتيح إنتاج أقنعة تحاذي الحدود الفعلية للجسم.

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

الفرق العملي مع التقطيع الدلالي

جدول يُلخّص التقاطعات والاختلافات:

المعيارالتقطيع الدلاليتقطيع النُسَخ
مُخرَج لكلّ سيّارةبكسلات مشتركة مع البقيةقناع منفصل
مُخرَج لبكسل «طريق»صنف «طريق»لا شيء
ملتصقان يتلامسانقناع واحدقناعان
الأمثل عندماعناصر الخلفية مهمّةالعدّ ومتابعة الأجسام
البنية النموذجيةU-Net، DeepLabMask R-CNN، YOLOv8-seg

لعدّ السيّارات على تقاطع طرق، تقطيع النُسَخ هو الاختيار الصحيح. للتحقّق من أنّ السيّارة داخل ممرّها، نجمع بين الاثنين: تقطيع دلالي للممرّ + تقطيع نُسَخ للسيّارات، ثم نحسب التقاطع.

التقطيع الشامل بلمحة

التقطيع الشامل (panoptic) يُوحّد التقطيعين. كلّ بكسل يحصل على:

  • صنف: من مجموعة الأصناف كاملة (بما فيها الخلفية)
  • معرّف نُسخة: للأصناف العدّية فقط (سيّارة، مُشاة، دراجة)

بكسلات «طريق» تشترك في صنف بلا نُسخة (فالطريق ليس عدّيًا). بكسلات «سيّارة» تحصل على صنف ونُسخة (سيّارة رقم 1، رقم 2، ...). هذا يُقارب أخيرًا ما يفعله الإنسان: يرى مشهدًا مُنظَّمًا لا خليطًا من الأقنعة.

النماذج الحديثة مثل Mask2Former وOneFormer تُعالج التصنيف والكشف وتقطيع النُسَخ والتقطيع الشامل بمعمارية موحّدة، وهذه هي وجهة البحث اليوم.

Segment Anything (SAM): ثورة التوسيم

الوحدة 1 ذكرت أنّ توسيم قناع يستغرق قرابة 90 ثانية. Segment Anything (Meta، 2023) قلب هذه المعادلة. SAM نموذج تقطيع «قابل للتوجيه» (promptable): تُشير إلى نقطة داخل جسم، أو ترسم صندوقًا حوله، أو حتى تُدخل نصًّا يصفه، وSAM يُنتج قناعه فورًا بدقّة مذهلة.

القيمة العملية: مُوسِّم يمكنه إنتاج قناع دقيق بنقرة واحدة بدلًا من رسم عشرات النقاط يدويًا. الكلفة تنخفض بأمر مقدار، وأصبح ممكنًا وسم مجموعات بيانات كبيرة بميزانيات متواضعة.

from segment_anything import sam_model_registry, SamPredictor
import numpy as np
import cv2

# تحميل SAM المدرب مسبقا
sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h.pth")
predictor = SamPredictor(sam)

image = cv2.imread("carrefour.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
predictor.set_image(image)

# نقاط توجيه: نقرة على السيارة المستهدفة
input_point = np.array([[540, 320]]) # (x, y)
input_label = np.array([1]) # 1 = ايجابي، 0 = سلبي

masks, scores, _ = predictor.predict(
point_coords=input_point,
point_labels=input_label,
multimask_output=True, # يعيد ثلاثة اقنعة مرشحة
)

# نختار القناع الاعلى ثقة
best_mask = masks[np.argmax(scores)]
print(f"عدد بكسلات القناع: {best_mask.sum()}")

SAM ليس بديلًا عن Mask R-CNN المُدرَّب على مجموعتك الخاصّة، لأنّه لا يعرف أسماء أصنافك الميدانية. لكنّه أداة توسيم ممتازة: تستخدمه لبناء مجموعة تدريب بسرعة، ثم تُدرّب Mask R-CNN أو YOLOv8-seg عليها.

استخدام Mask R-CNN بـtorchvision

import torch
import torchvision
from torchvision.transforms import functional as F
from PIL import Image

weights = torchvision.models.detection.MaskRCNN_ResNet50_FPN_Weights.DEFAULT
model = torchvision.models.detection.maskrcnn_resnet50_fpn(weights=weights)
model.eval()

image = Image.open("carrefour.jpg").convert("RGB")
tensor = F.to_tensor(image)

with torch.no_grad():
outputs = model([tensor])

preds = outputs[0]
# اسطر Mask R-CNN المضافة على مخرج Faster R-CNN
masks = preds["masks"] # (N, 1, H, W) اقنعة كثيفة
boxes = preds["boxes"] # (N, 4)
labels = preds["labels"]
scores = preds["scores"]

# قناع ثنائي بعتبة 0.5
binary_masks = (masks > 0.5).squeeze(1)
print(f"عدد الاجسام المقطعة: {len(binary_masks)}")
اختر Mask R-CNN عندما القناع مهمّ، وYOLOv8-seg عندما السرعة مهمّة

Mask R-CNN يُقدّم أقنعة أدقّ لكنّه بطيء (10 إلى 15 FPS على GPU). YOLOv8-seg يُقدّم أقنعة أخشن قليلًا لكن بسرعة 40 إلى 60 FPS. لعدّ السيّارات على فيديو مباشر، YOLOv8-seg يكفي غالبًا. للتحليل غير الفوري لسلامة الممرّ حيث يهمّ شكل السيّارة بدقّة، Mask R-CNN أنسب.

في الخلاصة

  • تقطيع النُسَخ يُنتج قناعًا مستقلًّا لكلّ جسم عدّي، على خلاف التقطيع الدلالي الذي يُشارك بكسلات الصنف نفسه.
  • Mask R-CNN يُضيف رأس قناع إلى Faster R-CNN، مع RoI Align الذي يحفظ المحاذاة الدقيقة اللازمة للأقنعة.
  • التقطيع الشامل يُوحّد الاثنين: صنف لكلّ بكسل + معرّف نُسخة للأجسام العدّية.
  • Segment Anything (SAM) يقلّب اقتصاديات التوسيم: أقنعة عالية الجودة بنقرة واحدة، ما يُتيح بناء مجموعات كبيرة بميزانيات صغيرة.

الوحدة التالية: من الصورة إلى الفيديو، وكيف نُتابع كلّ جسم مكتشف عبر الإطارات بمعرّف ثابت.