5.16. نوى الالتفاف المخصصة¶
كان لكل من مرشحات الجوار التي تناولناها حتى الآن إحصائية مدمجة يطبقها المرشح على النافذة في كل موضع -- المتوسط، والمتوسط الموزون غاوسياً، والوسيط. تُعد morph() المرشح الوحيد الذي يسمح للتطبيق بتوفير الإحصائية بنفسه، على هيئة نواة: مصفوفة صغيرة من الأوزان تصف كيف ينبغي للمرشح أن يدمج بكسلات الجوار في قيمة خرج واحدة.
تعتمد الآلية على عملية الالتفاف الكلاسيكية. في كل موضع خرج، يُضرب كل بكسل من الجوار بالوزن المقابل له في النواة، ثم تُجمع حواصل الضرب، وتُقاس النتيجة وتُزاح اختيارياً، وتُكتب القيمة في بكسل الخرج. تنتج النوى المختلفة نتائج مختلفة من المدخل نفسه. فالنواة ذات الأوزان الموجبة المتساوية جميعها تعيد إنتاج مرشح mean()؛ والنواة ذات الشكل الجرسي تعيد إنتاج gaussian(). أما الأنماط الأخرى فتنتج استجابات الحواف، والنقش البارز، والتدرجات، والشحذ، وضباب الحركة، وقائمة طويلة من التأثيرات الأخرى -- كل ما أراد فعله معالجة الصور الكلاسيكية في تمريرة خطية واحدة.
5.16.1. دالة morph¶
تبدو التوقيعة مماثلة لمرشحات الجوار الأخرى مع وسيط إضافي واحد:
img.morph(size, kernel, mul=1.0, add=0.0)
يمثل size نصف القطر بالطريقة نفسها المتبعة في كل مكان آخر، لذا يجب أن تكون النواة بالضبط (2 * size + 1) صفاً في (2 * size + 1) عموداً. والنواة نفسها هي قائمة Python مسطحة بهذا العدد من الأرقام، بترتيب الصفوف أولاً -- فالمدخلات (2 * size + 1) الأولى هي الصف العلوي، والمدخلات (2 * size + 1) التالية هي الصف الثاني، وهكذا، نزولاً إلى الصف السفلي. يقيس mul مجموع حواصل الضرب قبل كتابته في بكسل الخرج، ويضيف add ثابتاً. وتترك القيمتان الافتراضيتان mul=1.0 و add=0.0 خرج الالتفاف دون تغيير.
ثمة تفصيل يستحق التوضيح: تقسم الدالة تلقائياً مجموع حواصل الضرب على مجموع مدخلات النواة قبل كتابة الخرج. ويعني هذا التقسيم التلقائي أن نواة المتوسطة التي يبلغ مجموع مدخلاتها تسعة -- نواة ضباب صندوقي بحجم 3 في 3 مثلاً -- تخرج بمقياس واحد على تسعة دون أي جهد إضافي، وأن نواة التقريب الغاوسي التي يبلغ مجموعها ستة عشر تخرج بمقياس واحد على ستة عشر، وكلاهما دون أن يضطر التطبيق إلى حساب القسمة بنفسه. ولا يضبط التطبيق mul إلا حين يريد مقياساً إضافياً فوق التطبيع التلقائي -- أو، وهو الأكثر شيوعاً، حين يكون مجموع النواة صفراً (نواة استجابة حافة) ويصبح التقسيم التلقائي قسمةً على لا شيء. ويعامل إطار العمل المجموع على أنه واحد في تلك الحالة، فيصبح mul المقبض الوحيد لإبقاء مجموع حواصل الضرب غير المقاس ضمن النطاق.
يعمل أيضاً الزوج threshold=True / offset=N من قسم العتبة التكيفية على morph()، لذا يستطيع إطار النواة المخصصة نفسه إنتاج عتبة ثنائية يُحسب حدها الفاصل بإحصائية مخصصة.
5.16.2. تخطيط النواة¶
النواة بحجم 3 في 3 (size=1) هي قائمة مسطحة من تسعة أرقام مرتبة من اليسار إلى اليمين، ومن الأعلى إلى الأسفل. ويُقرأ هذا العرف بشكل طبيعي إذا قُسمت القائمة على ثلاثة أسطر في Python:
sobel_x = [-1, 0, 1,
-2, 0, 2,
-1, 0, 1]
هذا هو مشغّل التدرج Sobel-x -- أول نواة قياسية سيرغب فيها أي تطبيق وواحدة من المفيد استعراضها من البداية إلى النهاية. والنمط بسيط: أوزان سالبة في العمود الأيسر، وأوزان موجبة في العمود الأيمن، مع عمود مركزي صفري. وأوزان الصفوف -1, -2, -1 (أو 1, 2, 1 على اليمين) أعلى في الوسط منها عند الزوايا، مما يمنح الصف المركزي تأثيراً على النتيجة أكبر من صفوف الزوايا.
حين تجتاح النواة حافة عمودية -- عموداً من البكسلات ينتقل من داكن على اليسار إلى ساطع على اليمين -- تلتقط الأوزان السالبة الجانب الداكن وتلتقط الأوزان الموجبة الجانب الساطع. يكون مجموع حواصل الضرب عدداً موجباً كبيراً، يكتبه المرشح كبكسل خرج ساطع. أما الرقعة الأفقية ذات السطوع المنتظم فتنتج صفراً، لأن كل وزن موجب يقابله وزن سالب من المقدار نفسه على بكسل بالقيمة نفسها.
تشغيل النواة:
img.morph(1, sobel_x, mul=0.25)
يبلغ مجموع نواة Sobel صفراً -- إذ يقابل كل وزن سالب على الجانب الأيسر وزن موجب مساوٍ على الجانب الأيمن -- لذا لا يقسم التقسيم التلقائي على أي شيء، ويكون mul المقياس الوحيد على مجموع حواصل الضرب. وتبقي القيمة mul=0.25 الاستجابة ضمن النطاق: أكبر مجموع مطلق يمكن أن ينتجه Sobel-x من رقعة 3 في 3 هو نحو 4 * 255 = 1020 (ثمانية بكسلات ساطعة موزونة حتى 2)، وقسمة ذلك على أربعة تجعل الحالات القصوى عند 255، حيث تقتطعها الصيغة بنظافة.
تكتشف نواة Sobel-y المقابلة الحواف الأفقية عبر تدوير نمط الأوزان نفسه بمقدار 90 درجة:
sobel_y = [-1, -2, -1,
0, 0, 0,
1, 2, 1]
أما التطبيقات التي تريد اكتشاف أي حافة، بغض النظر عن الاتجاه، فتشغّل عادةً كلا مرشحي Sobel وتدمج الاستجابتين.
5.16.3. إزاحة الخرج¶
يمثل add النصف الآخر من قصة القياس. فاستجابة النواة ذات المجموع الصفري موقّعة -- موجبة على جانب من الحافة، وسالبة على الآخر -- والنصف السالب يُقتطع إلى صفر عند كتابته في بكسل غير موقّع. وتزيح add=128 الاستجابة لتتمركز عند الرمادي المتوسط، فتنجو الاستجابات السالبة كقيم أدنى من 128 وتقع الموجبة فوقها: تصبح استجابة الحافة أو النقش البارز مرئية في كلا الاتجاهين، على حساب نصف النطاق في كل منهما.
إن تحديد المزيج المتوقع من mul و add لنواة ما جزء من تصميم النواة؛ ويسرد فهرس النوى القياسية الإعدادات الصحيحة لكل نواة شائعة.
5.16.4. النوى الأكبر¶
وُصف كل ما في هذه الصفحة بنوى 3 في 3 (size=1)، لأن ذلك هو الحجم الذي يستخدمه الفهرس القياسي ولأن تخطيط الصفوف أولاً سهل الكتابة يدوياً بهذا الحجم. غير أن لا شيء في الآلية يقصر النواة على 3 في 3. فالقيمة size=2 تشغّل نواة 5 في 5، بخمسة وعشرين مدخلاً في القائمة المسطحة؛ و size=3 تشغّل نواة 7 في 7 بتسعة وأربعين؛ وهكذا، حتى أي نصف قطر يكون التطبيق مستعداً لدفع كلفته. ويتعامل إطار العمل مع تخطيط القائمة المسطحة أو الصفوف المتداخلة بأي حجم فردي.
إن داعي اللجوء إلى نواة أكبر هو الداعي نفسه للجوء إلى جوار أكبر في أي من المرشحات المدمجة: مزيد من المتوسطة، وكشف ميزات أوسع، وحساسية أقل لضوضاء البكسل الواحد. وتنمو الكلفة بمربع نصف القطر -- فنواة 5 في 5 تؤدي نحو 2.8 ضعف العمل لكل بكسل مقارنةً بنواة 3 في 3، ونواة 7 في 7 نحو 5.4 أضعاف -- ويأتي هذا المضاعف مباشرةً من معدل الإطارات.
النمط العملي هو البقاء عند size=1 للفهرس القياسي واللجوء إلى الأحجام الأكبر فقط حين تحتاج الخوارزمية إلى الجوار الأكبر. نادراً ما تستفيد كاشفات الحواف بعد حجم 3 في 3؛ بينما تستفيد مرشحات التنعيم أحياناً؛ ويعتمد الحجم الصحيح على مقياس الميزات التي يحاول التطبيق إبرازها أو كبتها.
5.16.5. متى تلجأ إلى morph¶
للتنعيم اليومي، تكون mean() و gaussian() و bilateral() أسرع وأنظف. ولكشف الحواف، صُممت laplacian() و find_edges() خصيصاً لهذا الغرض. أما داعي اللجوء إلى morph() مباشرةً فهو حين يحتاج التطبيق إلى التفاف محدد لا تتيحه المرشحات المدمجة -- مرشح Sobel اتجاهي، أو قالب حافة مخصص، أو نواة مضبوطة على ملمس معين سيبحث عنه بقية خط المعالجة، أو أي من النوى المفيدة في الفهرس القياسي الذي بنته معالجة الصور الكلاسيكية على مدى العقود. تتوفر المرونة الكاملة للنوى الاعتباطية؛ والثمن أن التطبيق مسؤول عن اختيار قيم النواة التي تنتج النتيجة التي يريدها.