5.23. تصحيح المنظور¶
تحذير
مصفوفة transform 3 في 3 الاعتباطية مدعومة فقط على OpenMV Cam N6 -- وتُتجاهَل الكلمة المفتاحية بصمت على كل لوحة أخرى. التطبيقات التي تحتاج إلى العمل في أي مكان آخر يجب أن تستخدم الطريقة الجاهزة rotation_corr() (بصيغتها corners=) أو أن تحسب الصورة المصححة مسبقًا خارج اللوحة.
الطريقة الجاهزة rotation_corr() تحزم عائلة معينة من تشويهات المنظور خلف مجموعة صغيرة من المُعاملات، وتعمل على كل لوحة مدعومة. تحتاج بعض التطبيقات إلى تشويه لا يلائم تلك الصيغة: إعادة تعيين إسقاطية اعتباطية من رباعي أضلاع إلى آخر، أو تصحيح معايَر لتركيب معروف سبق حسابه خارج الخط، أو مصفوفة تشويه مُسلّمة جاهزة من خوارزمية سابقة ما. لهذه الحالات، تقبل draw_image() -- إلى جانب copy() و crop() و scale() -- كلمة مفتاحية transform تأخذ مصفوفة 3 في 3 مبنية يدويًا تصف التشويه مباشرة.
5.23.1. التحويلات التآلفية والإسقاطية¶
تُعبَّر عن التشويهات الهندسية في الإحداثيات المتجانسة: موضع البكسل (x, y) مع إلحاق 1، مضروبًا في مصفوفة 3 في 3.
الصيغة التآلفية هي نقطة البداية. صفها السفلي ثابت عند \((0, 0, 1)\):
وعند كتابتها مفصّلة، يكون كل إحداثي خرج تركيبة خطية من إحداثيات الدخل زائد ثابت:
وهو ما يغطي القياس والدوران والقص والإزاحة في أي تركيبة -- وتحت كل هذه، تبقى الخطوط المتوازية متوازية.
الصيغة الإسقاطية (المنظورية) تحرّر الصف السفلي:
وعند كتابتها مفصّلة:
القسمة على \(w' = g x + h y + 1\) هي ما يجعل التحويل إسقاطيًا لا تآلفيًا فحسب. عندما تكون \(g\) و \(h\) كلتاهما صفرًا، تبقى \(w'\) عند واحد ولا تفعل القسمة شيئًا -- الصيغة التآلفية مجددًا. وعندما تكون إحداهما غير صفرية، تتغير \(w'\) مع موضع الدخل وتتعرض البكسلات في مواضع مختلفة لتقصير منظوري بمقادير مختلفة، وهو ما لم يعد يبقي الخطوط المتوازية متوازية -- إنه بالضبط تأثير حجر الزاوية عند النظر إلى مستوٍ مسطح من زاوية مائلة. التحويل الإسقاطي هو أعم تشويه هندسي يحوّل الخطوط المستقيمة إلى خطوط مستقيمة؛ والقياس والقلب والتبديل والتدوير وتصحيح الدوران بالزوايا الأربع كلها حالات خاصة منه.
التحويلات المسماة تنبثق من الصيغة التآلفية مباشرة. تحويل الهوية هو مصفوفة الهوية، و:
بالنسبة لمعظم التحويلات المبنية يدويًا، يبدأ التطبيق بإحدى هذه كأساس ويضرب فيها مزيدًا من المصفوفات لكل عملية إضافية، منتهيًا بمصفوفة 3 في 3 واحدة تصف التشويه المركّب. تُطبَّق المصفوفات من اليمين إلى اليسار: \(M = T R S\) يُشغّل القياس أولًا، ثم الدوران، ثم الإزاحة. التركيب الذي يحتاجه الجميع في النهاية هو الدوران حول مركز الصورة -- مصفوفة دوران مجردة تدوّر الصورة حول أصل البكسل في الزاوية العلوية اليسرى، لذا فالنسخة المركزية تنقل المركز \((c_x, c_y)\) إلى الأصل، وتدوّر، ثم تعيده:
5.23.2. الكلمة المفتاحية transform¶
تدخل المصفوفة عبر الكلمة المفتاحية transform، مُورَّدة كـ ulab.numpy.ndarray بحجم 3 في 3. الطريقة التي ينبغي اللجوء إليها هي draw_image()، التي تشوّه المصدر عبر المصفوفة أثناء رسمه على وجهة -- تستقر النتيجة في مخزن مؤقت يتحكم به التطبيق، ويتركب التشويه مع كل شيء آخر في الاستدعاء: القياس، ومزج ألفا، والإخفاء.
import ulab.numpy as np
M = np.array([[1.2, 0.0, -20.0],
[0.0, 1.2, -15.0],
[0.0, 0.0, 1.0]])
canvas.draw_image(img, transform=M)
يشوّه المثال img على canvas مقيسًا بمقدار 1.2 في كل اتجاه ومُزاحًا يسارًا وللأعلى بمقدار 20 و 15 بكسلًا على التوالي -- تشويه تآلفي مبني مباشرة من مدخلات المصفوفة الموصوفة أعلاه. الكلمة المفتاحية نفسها على copy() و crop() و scale() تطبّق التشويه على الصورة نفسها.