0%
OpenCV مع Python: مقدمة في معالجة الصور

OpenCV مع Python: مقدمة في معالجة الصور

ابدأ رحلتك في معالجة الصور باستخدام OpenCV وPython: فضاءات الألوان، والمرشحات، والحواف، والعتبة، والكشف عن الوجوه، وقراءة بث كاميرا الويب.

I

InSkillCoach

· دقيقة

OpenCV مع Python: مقدمة في معالجة الصور

تُعدّ OpenCV (Open Source Computer Vision Library) المكتبة المرجعية في الرؤية الحاسوبية. وبفضل واجهتها في Python، تتيح قراءة الصور ومقاطع الفيديو وتحويلها وتحليلها في بضعة أسطر فقط: التصفية، والكشف عن الحواف، والتعرّف على الوجوه، ومعالجة بث كاميرا الويب في الزمن الحقيقي. يغطي هذا الدليل العمليات الأساسية التي يعتمد عليها أي مشروع لمعالجة الصور، مع أمثلة كاملة قابلة للتنفيذ.

التثبيت

تُثبَّت OpenCV عبر الحزمة opencv-python التي تتضمن الوحدات الرئيسية:

pip install opencv-python

# Si vous avez besoin des modules supplémentaires (contrib)
pip install opencv-contrib-python

التحقق من التثبيت:

import cv2

print(cv2.__version__)
# 4.10.0 (ou version plus récente)

لاحظ أن الوحدة تُستورَد باسم cv2 مهما كان اسم الحزمة المثبَّتة. تمثّل OpenCV الصور على شكل مصفوفات NumPy، وتُثبَّت هذه المكتبة تلقائيًا بوصفها اعتمادية.

قراءة صورة وعرضها وحفظها

import cv2

# Lecture d'une image depuis le disque
image = cv2.imread("photo.jpg")

# imread ne lève PAS d'exception si le fichier est introuvable : il renvoie None
if image is None:
    raise FileNotFoundError("Impossible de lire photo.jpg")

# Une image est un tableau NumPy : (hauteur, largeur, canaux)
print(image.shape)
# (720, 1280, 3)
print(image.dtype)
# uint8 (valeurs de 0 à 255)

# Affichage dans une fenêtre
cv2.imshow("Ma photo", image)
cv2.waitKey(0)            # attend une touche (0 = indéfiniment)
cv2.destroyAllWindows()   # ferme toutes les fenêtres

# Sauvegarde (le format est déduit de l'extension)
cv2.imwrite("photo_copie.png", image)

التحقق if image is None أمر لا غنى عنه: فنسيان هذا الفحص هو السبب الأول للأخطاء الغامضة لدى المبتدئين.

فضاءات الألوان: فخ BGR

انتبه إلى الفخ الكلاسيكي: تحمّل OpenCV الصور بنظام BGR (أزرق، أخضر، أحمر) وليس RGB. فإذا عرضت صورة من OpenCV باستخدام Matplotlib دون تحويل، ستنعكس الألوان الحمراء والزرقاء.

import cv2

image = cv2.imread("photo.jpg")

# Conversion BGR vers RGB (nécessaire pour Matplotlib, Pillow, etc.)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Conversion en niveaux de gris (un seul canal)
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print(gris.shape)
# (720, 1280) : plus de dimension couleur

# Conversion en HSV (teinte, saturation, valeur)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

يُعدّ فضاء HSV مفيدًا جدًا لعزل لون معيّن، لأن تدرّج اللون (Hue) فيه مستقل عن السطوع:

import numpy as np

# Isoler les zones bleues de l'image
borne_basse = np.array([100, 100, 50])    # teinte, saturation, valeur minimales
borne_haute = np.array([130, 255, 255])
masque = cv2.inRange(hsv, borne_basse, borne_haute)

# Appliquer le masque : seules les zones bleues restent visibles
resultat = cv2.bitwise_and(image, image, mask=masque)

تغيير الحجم والاقتصاص

import cv2

image = cv2.imread("photo.jpg")
hauteur, largeur = image.shape[:2]

# Redimensionnement à des dimensions précises
petite = cv2.resize(image, (640, 360))

# Redimensionnement par facteur (ici : moitié) en conservant les proportions
moitie = cv2.resize(image, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)

# Recadrage : une image est un tableau NumPy, on découpe donc par tranches
# Syntaxe : image[y1:y2, x1:x2]
recadree = image[100:400, 200:600]

cv2.imwrite("recadree.jpg", recadree)

لتصغير صورة، يعطي الاستيفاء cv2.INTER_AREA أفضل نتيجة؛ أما للتكبير فيُفضَّل cv2.INTER_CUBIC أو cv2.INTER_LINEAR.

الرسم على الصورة

تعدّل دوال الرسم الصورة في مكانها مباشرة. والألوان بنظام BGR:

import cv2

image = cv2.imread("photo.jpg")

# Rectangle : coins opposés, couleur BGR, épaisseur (-1 = rempli)
cv2.rectangle(image, (50, 50), (250, 200), (0, 255, 0), 2)

# Cercle : centre, rayon
cv2.circle(image, (400, 300), 60, (255, 0, 0), 3)

# Ligne
cv2.line(image, (0, 0), (640, 360), (0, 0, 255), 2)

# Texte
cv2.putText(image, "Detection", (50, 40),
            cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), 2)

cv2.imwrite("annotee.jpg", image)

التمويه والمرشحات

يُعدّ التمويه الغاوسي اللبنة الأساسية في المعالجة المسبقة: فهو يخفف الضوضاء قبل الكشف عن الحواف أو تطبيق العتبة.

import cv2

image = cv2.imread("photo.jpg")

# Flou gaussien : le noyau (15, 15) doit avoir des dimensions impaires
flou = cv2.GaussianBlur(image, (15, 15), 0)

# Flou médian : très efficace contre le bruit "poivre et sel"
median = cv2.medianBlur(image, 5)

# Filtre bilatéral : lisse les zones plates en préservant les contours
bilateral = cv2.bilateralFilter(image, 9, 75, 75)

كلما كبرت النواة (kernel) زاد التمويه وضوحًا. وتكفي نواة (5, 5) عمومًا كمعالجة مسبقة.

الكشف عن الحواف: Canny وfindContours

تكشف خوارزمية Canny الحواف (أي المناطق التي تتغير فيها الشدة بقوة):

import cv2

image = cv2.imread("photo.jpg")
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Réduire le bruit avant la détection
gris_flou = cv2.GaussianBlur(gris, (5, 5), 0)

# Canny : les deux seuils contrôlent la sensibilité
# En dessous de 50 : rejeté ; au-dessus de 150 : contour sûr
contours_canny = cv2.Canny(gris_flou, 50, 150)
cv2.imwrite("canny.jpg", contours_canny)

تذهب findContours أبعد من ذلك: فهي تستخرج الحواف على شكل قوائم من النقاط، يمكن استخدامها لقياس الأجسام أو عدّها أو إحاطتها بإطار:

# findContours attend une image binaire (résultat de Canny ou d'un seuillage)
contours, _ = cv2.findContours(contours_canny,
                               cv2.RETR_EXTERNAL,      # contours externes seulement
                               cv2.CHAIN_APPROX_SIMPLE)  # compression des points

print(f"Nombre de contours trouvés : {len(contours)}")

# Encadrer chaque contour suffisamment grand
for contour in contours:
    aire = cv2.contourArea(contour)
    if aire > 500:  # ignorer le bruit
        x, y, w, h = cv2.boundingRect(contour)
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imwrite("objets_detectes.jpg", image)

العتبة (Thresholding)

تحوّل العتبة صورة بتدرّجات الرمادي إلى صورة ثنائية (أبيض وأسود)، وهي خطوة تسبق كثيرًا من التحليلات:

import cv2

gris = cv2.imread("document.jpg", cv2.IMREAD_GRAYSCALE)

# Seuillage simple : tout pixel > 127 devient 255, sinon 0
_, binaire = cv2.threshold(gris, 127, 255, cv2.THRESH_BINARY)

# Seuillage d'Otsu : le seuil optimal est calculé automatiquement
_, otsu = cv2.threshold(gris, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# Seuillage adaptatif : seuil calculé localement, idéal pour un
# document photographié avec un éclairage inégal
adaptatif = cv2.adaptiveThreshold(gris, 255,
                                  cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                  cv2.THRESH_BINARY, 11, 2)

الكشف عن الوجوه باستخدام مصنّفات Haar المتتالية

تتضمن OpenCV مصنّفات مدرَّبة مسبقًا للكشف عن الوجوه والعيون والابتسامات. إليك كاشف وجوه كاملًا:

import cv2

# Chargement du classificateur pré-entraîné fourni avec OpenCV
cascade_visages = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

image = cv2.imread("groupe.jpg")
if image is None:
    raise FileNotFoundError("Impossible de lire groupe.jpg")

# La détection travaille sur une image en niveaux de gris
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# detectMultiScale renvoie un rectangle (x, y, largeur, hauteur) par visage
visages = cascade_visages.detectMultiScale(
    gris,
    scaleFactor=1.1,    # réduction de l'image à chaque passe (10 %)
    minNeighbors=5,     # plus haut = moins de faux positifs
    minSize=(40, 40),   # taille minimale d'un visage en pixels
)

print(f"{len(visages)} visage(s) détecté(s)")
# 3 visage(s) détecté(s)

# Encadrer chaque visage
for (x, y, w, h) in visages:
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imwrite("visages_detectes.jpg", image)
cv2.imshow("Visages", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

اضبط قيمة minNeighbors إذا أنتج الكشف عددًا كبيرًا من الإيجابيات الكاذبة (فارفعها) أو فاتته بعض الوجوه (فاخفضها). مصنّفات Haar سريعة لكنها حسّاسة للاتجاه، إذ تكشف أساسًا الوجوه المواجهة للكاميرا.

قراءة بث فيديو أو كاميرا الويب

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

import cv2

cascade_visages = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

# 0 = première webcam ; remplacez par "video.mp4" pour un fichier
capture = cv2.VideoCapture(0)
if not capture.isOpened():
    raise RuntimeError("Impossible d'ouvrir la webcam")

while True:
    # ret vaut False si la lecture échoue (fin de fichier, webcam débranchée)
    ret, frame = capture.read()
    if not ret:
        break

    gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    visages = cascade_visages.detectMultiScale(gris, 1.1, 5, minSize=(40, 40))

    for (x, y, w, h) in visages:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

    cv2.imshow("Webcam - appuyez sur Q pour quitter", frame)

    # waitKey(1) : attendre 1 ms ; quitter si la touche Q est pressée
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# Libérer la webcam et fermer les fenêtres
capture.release()
cv2.destroyAllWindows()

للمضي أبعد: وحدة DNN

تعود مصنّفات Haar المتتالية إلى عام 2001. وللحصول على كشف أدق وأكثر متانة، تدمج OpenCV وحدة للشبكات العصبية العميقة (cv2.dnn) قادرة على تحميل نماذج مدرَّبة مسبقًا بصيغ ONNX وTensorFlow وCaffe:

import cv2

# Exemple : charger un modèle de détection au format ONNX
# net = cv2.dnn.readNetFromONNX("modele_detection.onnx")

الخطوات الطبيعية بعد هذا الدليل: الكشف عن الوجوه عبر DNN (نموذج YuNet المدمج في OpenCV عبر cv2.FaceDetectorYN)، والكشف عن الأجسام باستخدام YOLO، وتقدير الوضعية (pose estimation). ويبقى المبدأ نفسه كما في هذا الدليل: قراءة الصورة، ومعالجتها مسبقًا، وتحليلها، ثم رسم النتيجة.

أفضل الممارسات

  • تحقّق دائمًا من القيمة المُعادة من imread وcapture.read(): فـ OpenCV تُعيد None أو False بدلًا من رفع استثناء.
  • لا تنسَ أبدًا التحويل بين BGR/RGB عند تبادل الصور مع Matplotlib أو Pillow أو نموذج تعلّم عميق.
  • عالج مسبقًا قبل التحليل: التحويل إلى تدرّجات الرمادي ثم التمويه الغاوسي يحسّنان Canny والعتبة في الغالب.
  • حرّر الموارد: استخدم capture.release() وcv2.destroyAllWindows() في نهاية معالجة الفيديو.
  • اعمل على نسخ (image.copy()) عند الرسم، للحفاظ على الصورة الأصلية سليمة.
  • صفِّ حسب المساحة (cv2.contourArea) بعد findContours للتخلص من الضوضاء.

الخاتمة

أصبحت لديك الآن أسس معالجة الصور باستخدام OpenCV: التحميل والحفظ، وفضاءات الألوان (وفخ BGR)، وتغيير الحجم، والمرشحات، والكشف عن الحواف باستخدام Canny وfindContours، والعتبة، والكشف عن الوجوه بمصنّفات Haar المتتالية، ومعالجة بث كاميرا الويب في الزمن الحقيقي. تغطي هذه العمليات الغالبية العظمى من احتياجات أول مشروع في الرؤية الحاسوبية.

للتعمق أكثر، اطّلع على الوثائق الرسمية لـ OpenCV ودروسها في Python، ثم استكشف وحدة DNN للانتقال إلى الكشف القائم على التعلّم العميق.

InSkillCoach

نبذة عن InSkillCoach

خبير في التدريب والتقنيات

مدرّب متخصص في التقنيات المتقدمة والذكاء الاصطناعي، تدعمه شركة GNeurone Inc.

الشهادات:

  • AWS Certified Solutions Architect – Professional
  • شهادات Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.3k
129

التعليقات

التعليقات مدعومة عبر GitHub Discussions

سجّل الدخول باستخدام GitHub للمشاركة في النقاش

تم نسخ الرابط!