5.23. การแก้ไขมุมมอง

Warning

เมทริกซ์ transform ขนาด 3x3 ตามอำเภอใจนั้น รองรับเฉพาะบน OpenMV Cam N6 เท่านั้น คีย์เวิร์ดนี้จะถูกละเว้นอย่างเงียบๆ บนบอร์ดอื่นทุกตัว แอปพลิเคชันที่ต้องการรันในที่อื่นต้องใช้เมธอด rotation_corr() ที่เตรียมไว้ (ในรูปแบบ corners=) หรือคำนวณภาพที่แก้ไขแล้วนอกบอร์ด

เมธอด rotation_corr() ที่เตรียมไว้จะรวมกลุ่มการบิดเบือนมุมมองเฉพาะประเภทหนึ่งไว้เบื้องหลังชุดพารามิเตอร์ขนาดเล็ก และรันบนทุกบอร์ดที่รองรับ แอปพลิเคชันบางตัวต้องการการบิดเบือนที่ไม่ตรงกับรูปแบบนั้น เช่น การรีแมปแบบฉายภาพตามอำเภอใจจากรูปสี่เหลี่ยมด้านไม่เท่าหนึ่งไปอีกรูปหนึ่ง การแก้ไขที่ได้รับการสอบเทียบสำหรับการติดตั้งที่ทราบซึ่งได้คำนวณออฟไลน์แล้ว หรือเมทริกซ์การบิดเบือนที่อัลกอริทึมอัปสตรีมส่งมาพร้อมใช้ สำหรับกรณีเหล่านั้น draw_image() พร้อมกับ copy(), crop() และ scale() จะรับคีย์เวิร์ด transform ที่รับเมทริกซ์ 3x3 ที่สร้างด้วยตนเองซึ่งอธิบายการบิดเบือนโดยตรง

5.23.1. การแปลงแบบ Affine และ Projective

การบิดเบือนเชิงเรขาคณิตแสดงออกในรูปแบบ พิกัดเนื้อเดียวกัน: ตำแหน่งพิกเซล (x, y) ที่ต่อท้ายด้วย 1 คูณด้วยเมทริกซ์ขนาด 3x3

รูปแบบ affine คือจุดเริ่มต้น แถวล่างของมันถูกกำหนดไว้ที่ \((0, 0, 1)\):

\[\begin{split}\begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}\end{split}\]

เมื่อเขียนออกมา พิกัดเอาต์พุตแต่ละพิกัดคือการรวมเชิงเส้นของพิกัดอินพุตบวกค่าคงที่:

\[x' = a x + b y + c, \qquad y' = d x + e y + f\]

ซึ่งครอบคลุมการปรับขนาด การหมุน การเฉือน และการเลื่อนตำแหน่งในทุกการผสมผสาน และภายใต้ทุกอย่างนั้น เส้นขนานยังคงขนาน

รูปแบบ projective (perspective) จะปลดปล่อยแถวล่าง:

\[\begin{split}\begin{bmatrix} x'' \\ y'' \\ w' \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f \\ g & h & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}, \qquad (x', y') = \left( \frac{x''}{w'}, \; \frac{y''}{w'} \right)\end{split}\]

เมื่อเขียนออกมา:

\[x' = \frac{a x + b y + c}{g x + h y + 1}, \qquad y' = \frac{d x + e y + f}{g x + h y + 1}\]

การหารด้วย \(w' = g x + h y + 1\) คือสิ่งที่ทำให้การแปลงเป็น projective ไม่ใช่เพียง affine เมื่อ \(g\) และ \(h\) ทั้งคู่เป็นศูนย์ \(w'\) จะอยู่ที่หนึ่งและการหารไม่มีผล กลายเป็นรูปแบบ affine อีกครั้ง เมื่อค่าใดค่าหนึ่งไม่ใช่ศูนย์ \(w'\) จะแปรผันตามตำแหน่งอินพุตและพิกเซลที่ตำแหน่งต่างกันจะถูกยืดย่อในปริมาณที่แตกต่างกัน ซึ่งไม่รักษาความขนานของเส้นอีกต่อไป นั่นคือปรากฏการณ์ keystone ของการมองระนาบแบนจากมุมเฉียงนั่นเอง การแปลงแบบ projective คือการบิดเบือนเชิงเรขาคณิตที่ทั่วไปที่สุดที่ยังคงทำให้เส้นตรงไปเป็นเส้นตรง การปรับขนาด การกลับด้าน การสับเปลี่ยน การหมุน และการแก้ไขการหมุนสี่มุมล้วนเป็นกรณีพิเศษของการแปลงนี้

การแปลงที่มีชื่อเรียกจะออกมาจากรูปแบบ affine โดยตรง การแปลงเอกลักษณ์คือเมทริกซ์เอกลักษณ์ และ:

\[\begin{split}\underbrace{\begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix}}_{\text{translate by } (t_x, \; t_y)} \qquad \underbrace{\begin{bmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{bmatrix}}_{\text{scale by } (s_x, \; s_y)} \qquad \underbrace{\begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}}_{\text{rotate by } \theta}\end{split}\]

สำหรับการแปลงที่สร้างด้วยตนเองส่วนใหญ่ แอปพลิเคชันจะเริ่มต้นด้วยหนึ่งในนี้เป็นฐานและคูณเมทริกซ์เพิ่มเติมสำหรับการดำเนินการเพิ่มเติมแต่ละอย่าง ลงท้ายด้วยเมทริกซ์ 3x3 เดียวที่อธิบายการบิดเบือนผสม เมทริกซ์ใช้จากขวาไปซ้าย: \(M = T R S\) จะรันการปรับขนาดก่อน จากนั้นการหมุน แล้วการเลื่อนตำแหน่ง การผสมที่ทุกคนต้องการในที่สุดคือการหมุนรอบศูนย์กลางภาพ เมทริกซ์การหมุนเปล่าๆ จะหมุนภาพรอบจุดกำเนิดพิกเซลที่มุมซ้ายบน ดังนั้นรุ่นที่อยู่ตรงกลางจะย้ายจุดกลาง \((c_x, c_y)\) ไปที่จุดกำเนิด หมุน แล้วย้ายกลับ:

\[\begin{split}M = \underbrace{\begin{bmatrix} 1 & 0 & c_x \\ 0 & 1 & c_y \\ 0 & 0 & 1 \end{bmatrix}}_{\text{move centre back}} \underbrace{\begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}}_{\text{rotate}} \underbrace{\begin{bmatrix} 1 & 0 & -c_x \\ 0 & 1 & -c_y \\ 0 & 0 & 1 \end{bmatrix}}_{\text{move centre to origin}}\end{split}\]

5.23.2. คีย์เวิร์ด transform

เมทริกซ์จะถูกส่งผ่านคีย์เวิร์ด transform โดยให้เป็น ulab.numpy.ndarray ขนาด 3x3 เมธอดที่ควรใช้คือ 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 พิกเซลตามลำดับ ซึ่งเป็นการบิดเบือนแบบ affine ที่สร้างโดยตรงจากรายการเมทริกซ์ที่อธิบายไว้ข้างต้น คีย์เวิร์ดเดียวกันบน copy(), crop() และ scale() จะใช้การบิดเบือนกับภาพเอง