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(), שמעוותת את המקור דרך המטריצה תוך כדי ציור שלו על יעד – התוצאה נוחתת בחוצץ (buffer) שהיישום שולט בו, והעיוות מורכב עם כל השאר בקריאה: שינוי קנה המידה, מיזוג האלפא, המיסוך.
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() מחילה את העיוות על התמונה עצמה.