4.15. חוצצי פריימים (Framebuffers)¶
לאחר שחיישן המצלמה מאותחל, הוא פולט פריימים ברציפות בקצב הפריימים שלו – פריים חדש אחד בכל פרק זמן של פריים, בין אם היישום מוכן לקלוט אותו ובין אם לא. כל פריים צריך מקום ב-RAM שבו ינחת, אחרת הוא יאבד. מאגר חוצצי הפריימים (framebuffer pool) הוא המקום שבו הפריימים הללו שוכנים בין יציאתם מה-DMA לבין עיבודם על ידי קוד המשתמש, ומספר חוצצי הפריימים שהמצלמה שומרת במאגר זה קובע כיצד ה-DMA והיישום חולקים אותם. הבחירה נחשפת דרך framebuffers(), וזמינים ארבעה מצבים, הנבחרים לפי מספר החוצצים.
4.15.1. חוצץ יחיד (count = 1)¶
חוצץ פריימים יחיד ב-RAM. ה-DMA ממלא אותו; היישום קורא ממנו; הקריאה הבאה ל-snapshot() אינה יכולה להתחיל עד שהיישום שחרר את החוצץ, מכיוון שאותו חוצץ עצמו נדרש לשניהם.
המצלמה והיישום פועלים בצעדים נעולים זה לזה. ה-DMA נאלץ להמתין שהיישום יסיים, והיישום נאלץ להמתין שה-DMA יסיים, ומשמעות הדבר היא שקצב הפריימים בר-ההשגה הוא מחצית מקצב הפריימים של החיישן במקרה הטוב – כל פריים שני שהחיישן פולט מגיע בעוד החוצץ תפוס ולכן נאבד.
מצב זה הוא הקטן ביותר ב-RAM והאיטי ביותר מבחינת תפוקה. השתמשו בו רק כאשר ה-RAM צפוף מדי מכדי להקצות חוצץ שני.
4.15.2. חוצץ כפול (count = 2)¶
שני חוצצי פריימים ב-RAM: חוצץ אחורי אחד שה-DMA ממלא, וחוצץ קדמי אחד שהיישום קורא ממנו. כאשר היישום מסיים עם החוצץ הקדמי, שני התפקידים מתחלפים, וה-DMA מתחיל למלא את החוצץ שזה עתה שוחרר בעוד היישום קורא מזה שזה עתה התמלא.
כל עוד היישום מעבד כל פריים בפחות מפרק זמן של פריים מצלמה אחד, היישום רואה את קצב הפריימים המלא של החיישן – הפריים הבא של ה-DMA כבר ממתין בחוצץ האחורי כאשר היישום קורא שוב ל-snapshot(). אולם ברגע שזמן העיבוד חורג מפרק זמן של פריים אחד, הקצב נחתך לחצי: המצלמה תפיק שני פריימים בזמן שלוקח ליישום לעבד אחד, ורק השני מבין השניים יימסר.
מעבר לנקודה זו, הקצב מתדרדר באופן חלק עם זמן העיבוד. בכל פעם שה-DMA מסיים פריים חדש בחוצץ האחורי בעוד היישום עדיין עובד על החוצץ הקדמי, הפריים החדש דורס את הצילום הקודם במקומו במקום להישמט. היישום תמיד מקבל את הפריים העדכני ביותר שהמצלמה הפיקה ב-snapshot() הבא שלו, וקצב היישום בר-ההשגה הופך להופכי של זמן העיבוד שלו.
4.15.3. חוצץ משולש (count = 3)¶
שלושה חוצצי פריימים ב-RAM: שני חוצצים אחוריים שה-DMA מסתובב ביניהם וחוצץ קדמי אחד שעליו היישום עובד כעת. זהו מצב ברירת המחדל שה-OpenMV Cam בוחרת כאשר יש מספיק RAM פנוי, עם נסיגה אוטומטית לחוצץ כפול או יחיד כאשר אין.
החוצץ השלישי מנתק לחלוטין את קצב הפריימים של המצלמה מקצב הפריימים של היישום. ל-DMA תמיד יש חוצץ לכתוב אליו; ליישום תמיד יש חוצץ לקרוא ממנו; בכל snapshot() החוצץ האחורי המוכן העדכני ביותר הופך לחוצץ הקדמי החדש והחוצץ הקדמי הקודם משוחרר לטובת ה-DMA. קצב הפריימים של היישום תואם את הזמן שלוקח לו בפועל לעבד כל פריים – ללא קפיצת ה-1/2 שאליה נופל החוצץ הכפול כאשר זמן העיבוד חורג ולו במעט מפרק זמן של פריים אחד.
4.15.4. FIFO לווידאו (count = 4 או יותר)¶
ארבעה חוצצי פריימים או יותר ב-RAM, מסודרים כטבעת של פריימים הנלכדים ברצף זה אחר זה. כל פריים שהמצלמה מספקת מוכנס לתור ב-FIFO, ו-snapshot() מחזיר את הפריים הישן ביותר בתור ולא את העדכני ביותר. היישום עובר על הפריימים הלכודים בסדר הלכידה, בזמן שיש לו בפועל להקדיש לכל אחד.
מצב זה הוא הבחירה הנכונה כאשר כל פריים חשוב וצפויים עיכובי עיבוד קצרים: כתיבת וידאו לכרטיס SD שמחסנית האחסון שלו עלולה להיתקע לעשרות אלפיות שנייה במהלך מחיקה, הזרמה דרך USB למארח שמפסיק לקרוא לרגע, או חציצה של פרץ קצר של אירוע מהיר לבדיקה בקוד.
שתי מדיניויות מטפלות במקרה שבו ה-FIFO מתמלא לפני שהיישום רוקן אותו.
השמטת פריימים ישנים (ברירת מחדל). כאשר ה-FIFO מתמלא, כל הפריימים בתור פרט לפעיל נזרקים כך ש-
snapshot()הבא מחזיר פריים עדכני ולא מיושן. ה-DMA ממשיך ללכוד לאורך כל הזמן, כך שהיישום תמיד רואה נתונים טריים לאחר עיכוב. זוהי המדיניות הנכונה כאשר המטרה היא לשמור על הזרם הלכוד עדכני – הקלטת וידאו, הזרמה חיה.הפסקת לכידה בעת הצפה. העבירו
fflush=Falseלבנאי שלCSIוה-DMA יפסיק למלא את ה-FIFO כשהוא מלא, ויותיר את הפריימים בתור ללא פגע.snapshot()ממשיך להחזיר פריימים בסדר הלכידה עד שהיישום רוקן אותם, ולאחר מכן ה-DMA מתחדש. זוהי המדיניות הנכונה כאשר המטרה היא לשמר כל פריים של פרץ קצר – לכידת תנועה מהירה לבדיקה פריים-אחר-פריים בקוד לאחר מכן.
ראו csi.CSI.framebuffers() עבור ה-API המלא.
4.15.5. מצב מופעל (Triggered)¶
חלופה למצבים הרצים-תמיד שלעיל היא לכידה מופעלת (triggered), שבה החיישן פולט פריים רק כאשר snapshot() מבקש אחד. המצלמה נמצאת במצב סרק בין תמונות בזק ומתחילה חשיפה חדשה בכל פעם שהיישום פונה.
המחיר הוא תפוקה: לכידה מופעלת אינה יכולה לחפוף לקודמתה, כך שקצב הפריימים המרבי בר-ההשגה הוא מחצית מהקצב הרגיל של החיישן. היתרון הוא תזמון החשיפה. תמונת הבזק שולטת בדיוק מתי החשיפה מתחילה, וזה מה שיישום רוצה כאשר החשיפה צריכה להסתנכרן עם אירוע חיצוני – הבזק סטרובוסקופי, חיישן מיקום על מסוע, פולס על קו GPIO – במקום לנחות איפה שמזדמן בפריים המתגלגל של החיישן הרץ-חופשי בעת שהיישום מוכן לקרוא אותו.
מצב מופעל הוא ספציפי לחיישן. בחיישנים נתמכים הוא מופעל על ידי קריאה ל-csi0.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True) ומבוטל על ידי העברת False.