6.18. תמונות ומערכי ndarray¶
המחלקה Image היא המשטח המהיר לעבודה עם פיקסלים בפורמט המקורי של המצלמה: כל מתודה בה פועלת ישירות על חוצץ הפריימים (frame buffer) בפורמט הפיקסלים המקורי של המצלמה. numpy הוא המשטח המספרי הכללי לכל השאר. שתי מתודות מגשרות ביניהם:
image.Image.to_ndarray()– מעתיקה את הפיקסלים של תמונה לתוךndarray.הבנאי של
image.Image– בונה תמונה חדשה מתוךndarray.
יחד הן מאפשרות ליישום לצלם פריים, להעביר אותו ל-numpy לצורך טרנספורמציה מותאמת אישית, ואז להחזיר את התוצאה לתוך תמונה כדי להציגה, לשמור אותה או להזין אותה בחזרה לשאר ספריית התמונות.
6.18.1. מתמונה ל-ndarray¶
to_ndarray() מקצה ndarray חדש ומעתיקה אליו את נתוני הפיקסלים של התמונה (לפי מיפוי ה-dtype שלהלן). זהו לעולם אינו מבט (view) על חוצץ הפריימים (frame buffer) של התמונה – מערך ה-numpy תמיד מחזיק בבעלותו את הבייטים שלו עצמו. החתימה היא to_ndarray(dtype, *, buffer=None), וצורת הפלט תלויה בפורמט התמונה:
GRAYSCALE – מערך דו-ממדי, צורה
(height, width).RGB565 – מערך תלת-ממדי, צורה
(height, width, 3), מישורים בסדר R/G/B.
הארגומנט dtype קובע כיצד ממופה כל ערך פיקסל בן 8 ביט v:
|
אלמנט |
מיפוי לערך פיקסל בן 8 ביט |
|---|---|---|
|
|
|
|
|
|
|
|
|
דוגמה – הצגת פריים בגווני אפור כמטריצת uint8
import csi
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
a = img.to_ndarray('B') # shape (240, 320), dtype=uint8
print(a.shape, a.dtype)
print("mean brightness:", np.mean(a))
מילת המפתח buffer= מאפשרת ליישום לעשות שימוש חוזר ב-bytearray שכבר הוקצה, כך שהמצלמה אינה צריכה להקצות אחד חדש בכל פריים:
buf = bytearray(320 * 240)
while True:
img = csi0.snapshot()
a = img.to_ndarray('B', buffer=buf)
# ... process a ...
6.18.2. מ-ndarray לתמונה¶
בכיוון ההפוך, יש להעביר את ה-ndarray כארגומנט הראשון ל-image.Image. הבנאי מקצה חוצץ (buffer) תמונה חדש ומעתיק אליו את ערכי המערך, מוגבלים ומעוגלים לטווח 0..255
image.Image(arr, *, buffer=None, copy_to_fb=False)
הבנאי מסיק את הגיאומטריה ואת פורמט הפיקסלים מצורת המערך:
צורה
(h, w)– תמונתGRAYSCALE.צורה
(h, w, 3)– תמונתRGB565.
ל-ndarray חייב להיות dtype מסוג float; הבנאי תומך כיום במקרה זה בלבד. הערכים מעוגלים ומוגבלים לטווח 0..255.
buffer= מאפשר ליישום לספק bytearray שכבר הוקצה עבור התמונה המתקבלת. copy_to_fb=True כותב את התוצאה לתוך חוצץ הפריימים (frame buffer) של המצלמה, וזו הבחירה הנכונה כאשר התוצאה אמורה להופיע בתצוגה המקדימה של ה-IDE.
6.18.3. מסע הלוך ושוב¶
import csi
import image
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
a = img.to_ndarray('f') # work in float space
a = 255.0 * (a / 255.0) ** 0.5 # gamma correction
out = image.Image(a, copy_to_fb=True) # back to an image
6.18.4. מתי לגשר¶
גשר זה הוא התשובה הנכונה כאשר היישום זקוק לפעולה מספרית כללית שמתודות image המובנות אינן מספקות – מסננים מותאמים אישית, מיזוגים מותאמים אישית, אי-לינאריות יוצאות דופן – או כאשר יש לשלב נתוני פיקסלים עם נתונים שאינם של תמונה (צירי IMU, דגימות שמע) בחישוב יחיד.
זו אינה התשובה הנכונה לעיבוד פיקסלים בתפוקה גבוהה שהמחלקה Image כבר מכסה. המתודות המובנות פועלות ישירות על חוצץ הפריימים (frame buffer) בפורמט הפיקסלים המקורי של המצלמה והן מהירות בהרבה מהביטוי המקביל ב-numpy. השתמשו בגשר עבור הפעולות שספריית התמונות אינה מספקת כבר.