ניתוח תמונות

ניתוח תמונות מספק לאפליקציה תמונה שנגישה למעבד (CPU), ובעזרתה לבצע עיבוד תמונות, ראייה ממוחשבת או מסקנות מלמידת מכונה. מממשת analyze() שתרוץ בכל מסגרת.

כדי ללמוד איך לשלב את ערכת ML Kit של Google עם אפליקציית CameraX, למידע נוסף, ראו ML Kit Analyzer.

מצבי הפעלה

כשצינור הניתוח של האפליקציה לא יכול לעמוד בפריים של CameraX ניתן להגדיר את CameraX לשחרור פריימים באחת מהדרכים הבאות:

  • ללא חסימה (ברירת מחדל): במצב הזה, העורך תמיד שומר במטמון את תמונה עדכנית בתוך מאגר תמונות (דומה לתור עם עומק של תמונה) בזמן שהאפליקציה מנתחת את התמונה הקודמת. אם CameraX מקבל תמונה חדשה לפני שהעיבוד של האפליקציה מסתיים, התמונה החדשה נשמרת באותו מאגר נתונים זמני ומחליפה את התמונה הקודמת. חשוב לשים לב: ל-ImageAnalysis.Builder.setImageQueueDepth() אין השפעה על בתרחיש הזה, והתוכן של מאגר הנתונים הזמני תמיד מחליף. אפשר להפעיל את המצב הזה של מניעת חסימה על ידי התקשרות setBackpressureStrategy() עם STRATEGY_KEEP_ONLY_LATEST. למידע נוסף על המשמעויות של המנהלים, ניתן לעיין בחומר העזר תיעוד עבור STRATEGY_KEEP_ONLY_LATEST

  • חסימה: במצב הזה, הכלי הפנימי יכול להוסיף מספר תמונות לתור התמונות הפנימי, ויתחיל לשחרר פריימים רק כשהתור מלא. החסימה מתרחשת בכל ההיקף של מכשיר המצלמה: אם למכשיר המצלמה יש כמה תרחישים לדוגמה, חסום בזמן ש- CameraX מעבד את התמונות האלה. עבור לדוגמה, כשגם התצוגה המקדימה וגם ניתוח התמונות קשורים למכשיר מצלמה, אז גם התצוגה המקדימה תיחסם בזמן ש- CameraX מעבד את התמונות. כדי להפעיל את מצב החסימה, צריך להעביר STRATEGY_BLOCK_PRODUCER אל setBackpressureStrategy() אפשר גם להגדיר את העומק של תור התמונות באמצעות ImageAnalysis.Builder.setImageQueueDepth().

עם זמן אחזור קצר וכלי לניתוח ביצועים גבוהים, שבו הזמן הכולל ניתוח תמונה קצר ממשך הפריים של CameraX (16 אלפיות שנייה עבור 60fps, לדוגמה), כל אחד ממצבי ההפעלה מספק ביצועים חלקה באופן כללי חוויה אישית. מצב חסימה עדיין יכול להיות שימושי בתרחישים מסוימים, כמו בהתמודדות עם רעידות מערכת קצרות מאוד.

עם זמן אחזור ארוך ועם מנתח ביצועים גבוהים, מצב חסימה עם נדרש תור ארוך יותר כדי לפצות על זמן האחזור. עם זאת, שימו לב האפליקציה עדיין יכולה לעבד את כל המסגרות.

המכשיר הזה עובד עם זמן אחזור ארוך ועם זמן אחזור ארוך (המנתח לא יכול לעבד את כל הנתונים פריימים), מצב ללא חסימה עלול להיות בחירה מתאימה יותר, כי צריך להסיר את הפריימים כדי לנתח את הנתיב, תרחישים לדוגמה אחרים שעוסקים בו-זמנית עדיין יכולים לראות את כל הפריימים.

הטמעה

כדי להשתמש בניתוח תמונות באפליקציה, מבצעים את השלבים הבאים:

מיד לאחר החיבור, CameraX תשלח תמונות למנתח הרשום. בסיום הניתוח, התקשרו ImageAnalysis.clearAnalyzer() או לבטל את הקישור של התרחיש לדוגמה ImageAnalysis כדי להפסיק את הניתוח.

תרחיש לדוגמה לשימוש ב-ImageAnalysis

ImageAnalysis מתחבר את כלי הניתוח (צרכן תמונות) ל- CameraX, שהיא מפיקה תמונות. האפליקציות יכולות להשתמש ImageAnalysis.Builder כדי ליצור אובייקט ImageAnalysis. באמצעות ImageAnalysis.Builder, האפליקציה יכולה להגדיר את הדברים הבאים:

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

תרחיש לדוגמה של Build ImageAnalysis את ההגדרות של מרחב צבעים ואת המקומות שבהם ניתן לאחזר את הבייטים של הפיקסלים.

בתוך כלי הניתוח, האפליקציה צריכה לבצע את הפעולות הבאות:

  1. לנתח מסגרת נתונה במהירות האפשרית, עדיף במסגרת מגבלת זמן לקצב פריימים (לדוגמה, פחות מ-32 אלפיות השנייה במקרה של 30FPS). אם האפליקציה לא יכולה לנתח פריים מהר מספיק, כדאי לשקול אחת מ- מנגנונים נתמכים להסרת פריימים.
  2. צריך לשחרר את ImageProxy ל- CameraX באמצעות התקשרות ImageProxy.close(). חשוב לשים לב שאין לקרוא לפונקציה wrapped 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:

Kotlin

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)

Java

ImageAnalysis imageAnalysis =
    new ImageAnalysis.Builder()
        // enable the following line if RGBA output is needed.
        //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
        .setTargetResolution(new Size(1280, 720))
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
        .build();

imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
    @Override
    public void analyze(@NonNull ImageProxy imageProxy) {
        int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees();
            // insert your code here.
            ...
            // after done, release the ImageProxy object
            imageProxy.close();
        }
    });

cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, imageAnalysis, preview);

מקורות מידע נוספים

למידע נוסף על CameraX, ניתן לעיין במשאבים הנוספים הבאים.

Codelab

  • תחילת העבודה עם CameraX
  • דוגמת קוד

  • אפליקציות לדוגמה של CameraX