5.25. إيجاد الكتل

حوّلت عملية وضع العتبة الإطار الملتقط إلى قناع ثنائي: كل بكسل إما يجتاز اختبار العتبة أو لا يجتازه. وهذا يجيب عن سؤال أي الألوان التي يهتم بها التطبيق تظهر في المشهد، لكنه لا يجيب عن سؤال أين -- فالقناع مجرد بحر من الأصفار والآحاد. الخطوة التالية هي كشف الكتل: المرور على القناع، وإيجاد المناطق المتصلة من البكسلات المجتازة، وإعادة كل واحدة منها ككائن له موضع وحجم واتجاه وبقية الخصائص التي يمكن للتطبيق أن يتصرف بناءً عليها.

find_blobs() هي الدالة الأساسية لتلك الخطوة، وهي أكثر نقاط الدخول شيوعًا إلى عالم كائنات النتائج في وحدة image. تتبّع كرة ملونة، أو متابعة خط مرسوم على الأرض، أو عدّ كم بقعة ساطعة يراها مستشعر حراري، أو تقرير ما إذا كان LED أزرق مضاءً أم مطفأً -- يغطي النداء نفسه كل هذه الحالات. تتغير المدخلات (العتبات، والمنطقة المبحوث فيها، والمرشحات المطبّقة على النتيجة)، لكن نمط النداء يبقى ذاته.

5.25.1. النداء الأساسي

تأخذ find_blobs قائمة من العتبات وتعيد قائمة من كائنات نتائج الكتل:

thresholds = [(30, 100, 15, 127, 15, 127)]  # LAB threshold for red
blobs = img.find_blobs(thresholds)

for b in blobs:
    img.draw_rectangle(b.rect, color=(255, 0, 0))
    img.draw_cross(b.cx, b.cy, color=(255, 0, 0))

لكل ثلاثية عتبة الصيغة نفسها التي تتبعها العتبات المُمررة إلى binary() -- ست مدخلات (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) لصورة RGB565 (الحدود بنظام LAB)، ومدخلان (lo, hi) لصورة بتدرج الرمادي. يمكن تزويد ما يصل إلى 32 عتبة في نداء واحد، وهذا ما يجعل find_blobs() بهذه المرونة: يمكن تتبّع منارات حمراء وخضراء وزرقاء في آن واحد، تساهم كل منها بكتلها الخاصة في القائمة المعادة، وتحدد خاصية code لكل كتلة أي عتبة طابقتها.

نداءا draw_rectangle() و draw_cross() أعلاه يوسمان الإطار الملتقط لأجل المعاينة في الـ IDE. تحمل نتيجة الكتلة بالفعل b.rect (مربع الإحاطة كرباعية) و b.cx / b.cy (المركز كأعداد صحيحة)، لذا فإن رسم الكشف عائدًا إلى الإطار هو مجرد نداءين لدالتين.

5.25.2. ما الذي تحتويه النتيجة

كل Blob هي رباعية سمات تجمع معًا كل ما قاسه الكاشف عن المنطقة. تنقسم الخصائص إلى أربع مجموعات.

مجموعة مربع الإحاطة والمركز -- x و y و w و h و rect و cx و cy و cxf و cyf -- تصف موضع الكتلة. rect هي رباعية (x, y, w, h) التي تتوقعها دوال الرسم؛ و cx و cy هما المركز بإحداثيات البكسل الصحيحة؛ و cxf و cyf هما المركز بإحداثيات عشرية دون مستوى البكسل، وهي مفيدة عندما تهتم معايرة سابقة بالمواضع الكسرية.

مجموعة واصفات الشكل -- pixels و area و density و perimeter و roundness و elongation و compactness و rotation -- تصف كيف تبدو الكتلة. pixels هي عدد البكسلات المجتازة؛ و area هي مساحة مربع الإحاطة المحاذي للمحاور (w * h)؛ و density هي نسبة الاثنين، وتقترب من 1.0 لمستطيل مصمت وتنخفض نحو 0.0 لخط مائل رفيع. تقيس كل من roundness و compactness مدى استدارة الكتلة، من منظورين هندسيين مختلفين (roundness من العزوم من الرتبة الثانية، و compactness من نسبة المحيط إلى المساحة)؛ و elongation هي 1.0 - roundness للتسهيل. rotation هي اتجاه المحور الرئيسي بالراديان، وتكون أكثر دقة في الكتل المستطيلة وتصبح مشوّشة في الكتل شبه المستديرة (المحور الملتبس ليس له اتجاه محدد جيدًا).

مجموعة البيانات الوصفية للعتبة والدمج -- code و count -- تحدد أي عتبة طُوبقت وكم عدد الكتل المصدرية التي دُمجت في الكتلة المعادة. code هي خريطة بتات 32 بت ببت واحد مضبوط لكل عتبة مطابِقة (العتبة الواحدة تعطي code == 1؛ والكتلة المدمجة متعددة الألوان قد يكون لها عدة بتات مضبوطة)؛ و count هي 1 ما لم يكن merge=True قد جمع عدة عمليات كشف في واحدة.

مجموعة الزوايا -- corners و min_corners -- تعطي الهندسة المُدارة للكتلة. corners هي رباعية (x, y) للنقاط القصوى المستخرجة من محيط الكتلة، مرتّبة باتجاه عقارب الساعة بدءًا من أعلى اليسار؛ و min_corners هي رباعية الزوايا للمستطيل المُدار ذي المساحة الدنيا الذي يحيط بالكتلة. مستطيل المساحة الدنيا هو الملاءمة المحكمة؛ و rect المحاذي للمحاور هو الملاءمة الفضفاضة المحاذية لشبكة البكسلات. كلاهما مفيد بحسب ما إذا كانت مرحلة لاحقة تحتاج إلى مربع موجّه أم عادي.

A blob detection illustrated against a binary threshold mask. The left panel shows a tilted oval mask of passing pixels. The right panel shows the same mask annotated with the axis-aligned bounding box drawn around it, the centroid marked with a cross in the middle, a dashed minimum-area rotated rectangle hugging the oval at its true angle, and the major-axis line through the centroid pointing along the oval's long direction.

تحمل الكتلة مربع الإحاطة المحاذي للمحاور (rect و x و y و w و h)، والمركز (cx و cy أو ما دون البكسل cxf و cyf)، والمستطيل المُدار ذا المساحة الدنيا (min_corners إضافة إلى rotation)، وخطوط المحور الرئيسي / الثانوي الاختيارية المحسوبة بدوال المساعدة على مستوى الوحدة أدناه.

5.25.4. دمج الكتل المتداخلة

تُجري merge=True معالجة لاحقة لقائمة النتائج لدمج الكتل التي تتداخل مستطيلات إحاطتها. الاستخدام الطبيعي هو كشف هدف ترى الكاميرا لونه كعدة مناطق معتّبة بسبب الانعكاسات اللامعة، أو خطوط الظل، أو الإضاءة غير المتطابقة عبر الكائن: قد تعود كرة حمراء واحدة كثلاث أو أربع كتل حمراء صغيرة، مجتمعةً، ترسم الكرة. مع merge=True، تصبح الكتل الثلاث كتلة واحدة كبيرة، ويغطي rect الاتحاد، ويكون code هو OR بتيًا لرموز الكتل المدموجة (بحيث يحدد الدمج متعدد الألوان أي الألوان ساهمت)، ويُبلّغ count عن عدد الكتل المصدرية التي جُمعت.

ينمّي margin مستطيلات الإحاطة أو يقلّصها قبل اختبار التداخل. مع margin=2، تظل الكتل التي تقترب مستطيلات إحاطتها بمسافة 2 بكسل من بعضها تُدمج؛ ومع margin=-2، تُدمج فقط الكتل التي تتداخل مستطيلات إحاطتها بمقدار 2 بكسل على الأقل. الضبط الطبيعي: هامش موجب للتعامل مع الكتل التي كسّرتها العتبة إلى قطع متجاورة؛ وهامش سالب لإبقاء الكائنات المتميزة المتجمّعة بإحكام منفصلة.

تعمل merge_cb على كل زوج مرشّح قبل حدوث الدمج. تتلقى دالة رد النداء الكتلتين وتعيد True للسماح بالدمج أو False لمنعه. هذه هي الأداة المناسبة للتحقق المتبادل من عمليات الدمج التي تفوتها القاعدة الهندسية -- على سبيل المثال، رفض دمج كتلتين تختلف زاويتا rotation لديهما بأكثر من عتبة معينة، أو رفض دمج كتلة صغيرة في كتلة أكبر منها بكثير إذا كانت الصغيرة مجرد بقعة.

5.25.5. مدرّجات الإسقاط التكرارية

يرفق x_hist_bins_max و y_hist_bins_max مدرّجات إسقاط تكرارية اختيارية بكل كتلة. مدرّج الإسقاط التكراري هو عدد البكسلات المجتازة على امتداد محور واحد: يجمع مدرّج محور X البكسلات المجتازة لكل عمود داخل مربع إحاطة الكتلة، ويجمع مدرّج محور Y لكل صف. تكون قيمة كليهما الافتراضية صفرًا -- فلا تُحسب المدرّجات ما لم تُزوَّد قيمة max غير صفرية، إذ إنها وإلا ستضيف عملًا إلى كل عملية كشف.

عندما تُحسب، توفّر المدرّجات إشارة أحادية البعد رخيصة يمكن للتطبيق إجراء تحليل إضافي عليها: كشف موضع شريط عمودي داخل الكتلة، أو إيجاد نقطة انكسار هدف ثنائي اللون، أو عدّ كم فجوة تظهر على امتداد المحور الطويل. تُملأ كخاصيتي x_hist_bins و y_hist_bins على كل Blob.

5.25.6. دوال مساعدة هندسية إضافية

تعيش حفنة من المقاييس الهندسية الإضافية كدوال على مستوى الوحدة تأخذ كتلة وتعيد القياس المطلوب:

  • تعيد image.get_solidity() صلابة الكتلة -- البكسلات مقسومة على مساحة الغلاف المحدّب. المنطقة المصمتة الممتلئة قريبة من 1.0؛ والكتلة ذات التقعّرات (حدوة حصان، أو يد بأصابع مفرودة) تنخفض إلى ما دون ذلك بكثير.

  • تعيد image.get_convexity() التحدّب -- محيط الغلاف المحدّب مقسومًا على محيط الكتلة. الكتلة المحدّبة تمامًا تساوي 1.0؛ والكتل المسننة أو المشرومة تكون أقل.

  • تعيد image.get_major_axis_line() و image.get_minor_axis_line() كائنات Line على امتداد المحورين الرئيسي والثانوي للكتلة، مشتقّة من المستطيل المُدار ذي المساحة الدنيا.

  • تعيد image.get_enclosing_circle() كائن Circle يحيط بالكتلة -- وهو مفيد عندما تريد مرحلة لاحقة دائرة لرسمها أو للاختبار في مقابلها.

  • تعيد image.get_enclosed_ellipse() الخماسية (cx, cy, rx, ry, rotation) لقطع ناقص محفور في المستطيل ذي المساحة الدنيا للكتلة. تُغذّى القيم مباشرة في draw_ellipse().

5.25.7. التعلّم التلقائي لعتبة

كاشف الكتل ليس أفضل من العتبات التي يُشغّل بها، وعمل إيجاد العتبة الصحيحة للون هدف هو مشكلة بحد ذاتها. هناك نمطان شائعان يقلّلان ذلك العمل.

الأول هو الاختيار التفاعلي في الـ IDE: التقط إطارًا، واسحب مستطيلًا حول مثال للون الهدف، ودع محرر العتبات في الـ IDE يُبلّغ عن حدود LAB التي يراها. تُسقط تلك الحدود في البرنامج النصي كعتبات لـ find_blobs() ويصبح الكاشف جاهزًا.

الثاني هو التعلّم التلقائي البرمجي: روتين معايرة يعمل على الكاميرا يلتقط إطارًا، ويأخذ مدرّجًا تكراريًا لرقعة معلومة حيث يوجد الهدف (get_histogram() مع roi=)، ويقرأ نطاق قيم الرقعة من المدرّج بـ get_percentile(). يضبط المئين الخامس الحد الأدنى لكل قناة والمئين الخامس والتسعون حدّها الأعلى، متجاهلًا البكسلات الشاذة المتفرقة عند الطرفين. على صورة RGB565 يُبلّغ نداء مئين واحد عن قنوات LAB الثلاث دفعةً واحدة، فينتج النداءان الأرقام الستة التي تتوقعها find_blobs():

h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
             lo.a_value, hi.a_value,
             lo.b_value, hi.b_value)