תרחיש השימוש של ניתוח תמונות מספק לאפליקציה תמונה שנגישה למעבד (CPU), שבה אפשר לבצע עיבוד תמונה, ראייה ממוחשבת או הסקת מסקנות של למידת מכונה. האפליקציה מטמיעה שיטת analyze() שפועלת בכל פריים.
כדי ללמוד איך לשלב את ערכת ה-ML של Google עם אפליקציית CameraX, עיינו במאמר ML Kit Analyzer.
מצבי הפעלה
אם צינור עיבוד הנתונים של האפליקציה לא עומד בדרישות של CameraX לגבי קצב הפריימים, אפשר להגדיר את CameraX כך שישמיט פריימים באחת מהדרכים הבאות:
non-blocking (ברירת המחדל): במצב הזה, ה-executor תמיד שומר במטמון את התמונה האחרונה במאגר תמונות (בדומה לתור בעומק של אחד) בזמן שהאפליקציה מנתחת את התמונה הקודמת. אם CameraX מקבלת תמונה חדשה לפני שהאפליקציה מסיימת את העיבוד, התמונה החדשה נשמרת באותו מאגר נתונים ומחליפה את התמונה הקודמת. שימו לב שאין השפעה ל-
ImageAnalysis.Builder.setImageQueueDepth()בתרחיש הזה, ותוכן המאגר תמיד נמחק ומוחלף. כדי להפעיל את המצב הזה, קוראים ל-setBackpressureStrategy()עםSTRATEGY_KEEP_ONLY_LATEST. למידע נוסף על ההשלכות של מנהל הביצוע, עיינו במסמכי התיעוד שלSTRATEGY_KEEP_ONLY_LATEST.blocking: במצב הזה, ה-executor הפנימי יכול להוסיף כמה תמונות לתור התמונות הפנימי, ומתחיל להשמיט פריימים רק כשהתור מלא. החסימה מתרחשת בכל היקף המצלמה: אם למצלמה יש כמה תרחישי שימוש קשורים, כל תרחישי השימוש האלה ייחסמו בזמן ש-CameraX מעבד את התמונות האלה. לדוגמה, כשגם התצוגה המקדימה וגם ניתוח התמונה קשורים למכשיר מצלמה, גם התצוגה המקדימה תיחסם בזמן ש-CameraX מעבד את התמונות. כדי להפעיל את מצב החסימה, מעבירים את הערך
STRATEGY_BLOCK_PRODUCERאלsetBackpressureStrategy(). אפשר גם להגדיר את עומק התור של התמונות באמצעות ImageAnalysis.Builder.setImageQueueDepth().
אם משתמשים במנתח עם זמן אחזור קצר וביצועים גבוהים, שבו הזמן הכולל לניתוח תמונה קצר יותר ממשך הזמן של פריים ב-CameraX (לדוגמה, 16 אלפיות השנייה ל-60 פריימים לשנייה), כל אחד ממצבי ההפעלה מספק חוויה חלקה באופן כללי. מצב חסימה עדיין יכול להיות שימושי בתרחישים מסוימים, למשל כשמתמודדים עם תנודות קצרות מאוד במערכת.
במנתח עם זמן אחזור גבוה וביצועים גבוהים, צריך מצב חסימה עם תור ארוך יותר כדי לפצות על זמן האחזור. עם זאת, חשוב לזכור שהאפליקציה עדיין יכולה לעבד את כל הפריימים.
אם זמן האחזור גבוה והניתוח אורך זמן רב (המנתח לא יכול לעבד את כל הפריימים), יכול להיות שמצב ללא חסימה יהיה בחירה מתאימה יותר, כי צריך להשמיט פריימים לנתיב הניתוח, אבל תרחישי שימוש אחרים שקשורים בו-זמנית עדיין יכולים לראות את כל הפריימים.
הטמעה
כדי להשתמש בניתוח תמונות באפליקציה, פועלים לפי השלבים הבאים:
- בניית תרחיש לדוגמה של
ImageAnalysis. - יוצרים
ImageAnalysis.Analyzer. - מגדירים את המנתח ל-
ImageAnalysis. - מקשרים את בעלי מחזור החיים, את בורר המצלמה ואת תרחיש השימוש של
ImageAnalysisלמחזור החיים.
מיד אחרי הקישור, CameraX שולחת תמונות למנתח הרשום.
בסיום הניתוח, צריך להפעיל את ImageAnalysis.clearAnalyzer() או לבטל את הקישור של תרחיש השימוש ImageAnalysis כדי להפסיק את הניתוח.
תרחיש לדוגמה של יצירת ImageAnalysis
ImageAnalysis מחבר את המנתח (צרכן תמונות) ל-CameraX, שהוא יצרן תמונות.
אפליקציות יכולות להשתמש ב-ImageAnalysis.Builder כדי ליצור אובייקט ImageAnalysis. באמצעות ImageAnalysis.Builder,
האפליקציה יכולה להגדיר את הדברים הבאים:
- פרמטרים של פלט התמונה:
- פורמט: CameraX תומך ב-
YUV_420_888וב-RGBA_8888באמצעותsetOutputImageFormat(int). פורמט ברירת המחדל הואYUV_420_888. - Resolution ו-AspectRatio: אפשר להגדיר כל אחד מהפרמטרים האלה, אבל אי אפשר להגדיר את שני הערכים בו-זמנית.
- סיבוב.
- שם היעד: השתמשו בפרמטר הזה למטרות ניפוי באגים.
- פורמט: CameraX תומך ב-
- הגדרות להצגת כמה תמונות בבת אחת:
אפליקציות יכולות להגדיר את הרזולוציה או את יחס הגובה-רוחב, אבל לא את שניהם. רזולוציית הפלט המדויקת תלויה בגודל (או ביחס הגובה-רוחב) שהאפליקציה מבקשת וביכולות החומרה, והיא עשויה להיות שונה מהגודל או מהיחס שהתבקשו. למידע על האלגוריתם להתאמת רזולוציה, קראו את המאמר בנושא setTargetResolution().
אפליקציה יכולה להגדיר את פיקסלי התמונה של הפלט כך שיהיו במרחבי הצבעים YUV (ברירת מחדל) או RGBA. כשמגדירים פורמט פלט RGBA, CameraX ממיר באופן פנימי תמונות ממרחב הצבעים YUV ל-RGBA, ואורז את הביטים של התמונה ב-ByteBuffer של המישור הראשון של ImageProxy (שני המישורים האחרים לא בשימוש) ברצף הבא:
ImageProxy.getPlanes()[0].buffer[0]: alpha
ImageProxy.getPlanes()[0].buffer[1]: red
ImageProxy.getPlanes()[0].buffer[2]: green
ImageProxy.getPlanes()[0].buffer[3]: blue
...
כשמבצעים ניתוח תמונה מורכב והמכשיר לא מצליח לעמוד בקצב הפריימים, אפשר להגדיר את CameraX כך שישמיט פריימים באמצעות האסטרטגיות שמתוארות בקטע מצבי הפעלה בנושא הזה.
יצירת מנתח
אפליקציות יכולות ליצור מנתחים על ידי הטמעת הממשק ImageAnalysis.Analyzer ושינוי analyze(ImageProxy image).
בכל מנתח, האפליקציות מקבלות ImageProxy, שהוא wrapper של Media.Image.
אפשר לשלוח שאילתה לגבי פורמט התמונה באמצעות ImageProxy.getFormat().
הפורמט הוא אחד מהערכים הבאים שהאפליקציה מספקת עם ImageAnalysis.Builder:
ImageFormat.RGBA_8888אם האפליקציה ביקשהOUTPUT_IMAGE_FORMAT_RGBA_8888.ImageFormat.YUV_420_888אם האפליקציה ביקשהOUTPUT_IMAGE_FORMAT_YUV_420_888.
בתרחיש לדוגמה של פיתוח ImageAnalysis מוסבר איך להגדיר את מרחב הצבעים ואיפה אפשר לאחזר את בייטי הפיקסלים.
בתוך מנתח, האפליקציה צריכה לבצע את הפעולות הבאות:
- לנתח פריים נתון מהר ככל האפשר, רצוי במסגרת מגבלת הזמן של קצב הפריימים הנתון (לדוגמה, פחות מ-32 אלפיות השנייה במקרה של 30 פריימים לשנייה). אם האפליקציה לא יכולה לנתח פריים מספיק מהר, כדאי להשתמש באחד ממנגנוני השמטת הפריימים הנתמכים.
- משחררים את
ImageProxyל-CameraX באמצעות קריאה ל-ImageProxy.close(). חשוב לזכור שלא צריך לקרוא לפונקציית הסגירה של Media.Image שעטוף (Media.Image.close()).
אפליקציות יכולות להשתמש ב-Media.Image שעטוף ב-ImageProxy באופן ישיר.
פשוט אל תפעילו את Media.Image.close() על התמונה שעטופה, כי זה יפגע במנגנון שיתוף התמונות ב-CameraX. במקום זאת, השתמשו ב-ImageProxy.close() כדי לשחרר את ה-Media.Image הבסיסי ל-CameraX.
הגדרת המנתח ל-ImageAnalysis
אחרי שיוצרים מנתח, משתמשים ב-ImageAnalysis.setAnalyzer() כדי לרשום אותו ולהתחיל בניתוח. בסיום הניתוח, משתמשים ב-ImageAnalysis.clearAnalyzer() כדי להסיר את המנתח הרשום.
אפשר להגדיר רק מנתח פעיל אחד לניתוח תמונות. אם קוראים ל-ImageAnalysis.setAnalyzer(), הוא מחליף את המנתח הרשום אם הוא כבר קיים. אפליקציות יכולות להגדיר מנתח חדש בכל שלב, לפני או אחרי הקישור של תרחיש השימוש.
קישור של ImageAnalysis למחזור חיים
מומלץ מאוד לקשר את ImageAnalysis למחזור חיים קיים של AndroidX באמצעות הפונקציה ProcessCameraProvider.bindToLifecycle(). שימו לב שהפונקציה bindToLifecycle() מחזירה את מכשיר ה-Camera שנבחר, ואפשר להשתמש בה כדי לשנות הגדרות מתקדמות כמו חשיפה ועוד. במדריך הזה מוסבר איך לשלוט בפלט של המצלמה.
בדוגמה הבאה משולבים כל השלבים הקודמים, ומקשרים את תרחישי השימוש של CameraX ImageAnalysis ו-Preview לבעלים של lifeCycle:
val imageAnalysis = ImageAnalysis.Builder() // enable the following line if RGBA output is needed. // .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { imageProxy -> val rotationDegrees = imageProxy.imageInfo.rotationDegrees // insert your code here. ... // after done, release the ImageProxy object imageProxy.close() }) cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
מקורות מידע נוספים
איפה אפשר למצוא מידע נוסף על CameraX?
Codelab
דוגמת קוד