זיהוי אובייקטים עם אנדרואיד

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

הדגמה מונפשת לזיהוי אובייקטים

סקירה כללית של זיהוי אובייקטים

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

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

מודלים ומערך נתונים

מדריך זה משתמש במודלים שהוכשרו באמצעות מערך הנתונים של COCO . COCO הוא מערך נתונים לזיהוי אובייקטים בקנה מידה גדול המכיל 330K תמונות, 1.5 מיליון מופעי אובייקטים ו-80 קטגוריות אובייקטים.

יש לך אפשרות להשתמש באחד מהדגמים הבאים שהוכשרו מראש:

  • EfficientDet-Lite0 [מומלץ] - מודל זיהוי אובייקטים קל משקל עם מחלץ תכונות BiFPN, מנבא קופסה משותפת ואובדן מוקד. ה-mAP (ממוצע ממוצע דיוק) עבור מערך האימות של COCO 2017 הוא 25.69%.

  • EfficientDet-Lite1 - מודל EfficientDet לזיהוי אובייקטים בגודל בינוני. ה-mAP עבור מערך האימות של COCO 2017 הוא 30.55%.

  • EfficientDet-Lite2 - מודל זיהוי אובייקטים של EfficientDet גדול יותר. ה-mAP עבור מערך האימות של COCO 2017 הוא 33.97%.

  • MobileNetV1-SSD - דגם קל במיוחד המותאם לעבודה עם TensorFlow Lite לזיהוי אובייקטים. ה-mAP עבור מערך האימות של COCO 2017 הוא 21%.

עבור הדרכה זו, מודל EfficientDet-Lite0 יוצר איזון טוב בין גודל ודיוק.

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

דוגמה להגדרה והרצה

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

דרישות מערכת

  • Android Studio גרסה 2021.1.1 (Bumblebee) ומעלה.
  • Android SDK גרסה 31 ומעלה
  • מכשיר אנדרואיד עם גרסת מערכת הפעלה מינימלית של SDK 24 (אנדרואיד 7.0 - נוגט) עם מצב מפתח מופעל.

קבל את הקוד לדוגמה

צור עותק מקומי של הקוד לדוגמה. תשתמש בקוד זה כדי ליצור פרויקט ב-Android Studio ולהפעיל את היישום לדוגמה.

כדי לשכפל ולהגדיר את הקוד לדוגמה:

  1. שכפל את מאגר git
    git clone https://github.com/tensorflow/examples.git
    
  2. לחלופין, הגדר את מופע ה-git שלך לשימוש בקופה דלילה, כך שיש לך רק את הקבצים עבור האפליקציה לדוגמה לזיהוי אובייקטים:
    cd examples
    git sparse-checkout init --cone
    git sparse-checkout set lite/examples/object_detection/android
    

ייבוא ​​והפעל את הפרויקט

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

כדי לייבא ולבנות את פרויקט הקוד לדוגמה:

  1. הפעל את Android Studio .
  2. מ-Android Studio, בחר קובץ > חדש > ייבוא ​​פרויקט .
  3. נווט אל ספריית הקוד לדוגמה המכילה את הקובץ build.gradle ( .../examples/lite/examples/object_detection/android/build.gradle ) ובחר את הספרייה הזו.
  4. אם Android Studio מבקש סינכרון Gradle, בחר אישור.
  5. ודא שמכשיר האנדרואיד שלך מחובר למחשב שלך ומצב מפתח מופעל. לחץ על חץ Run הירוק.

אם תבחר את הספרייה הנכונה, Android Studio יוצר פרויקט חדש ובונה אותו. תהליך זה עשוי להימשך מספר דקות, בהתאם למהירות המחשב שלך ואם השתמשת ב-Android Studio עבור פרויקטים אחרים. כאשר הבנייה מסתיימת, ה-Android Studio מציג הודעת BUILD SUCCESSFUL בחלונית סטטוס Build Output .

אופציונלי: כדי לתקן שגיאות בנייה על ידי עדכון גרסת הפלאגין של Android:

  1. פתח את הקובץ build.gradle בספריית הפרויקט.
  2. שנה את גרסת כלי אנדרואיד באופן הבא:

    // from: classpath
    'com.android.tools.build:gradle:4.2.2'
    // to: classpath
    'com.android.tools.build:gradle:4.1.2'
    
  3. סנכרן את הפרויקט על ידי בחירה: קובץ > סנכרן פרויקט עם קבצי Gradle .

להפעלת הפרויקט:

  1. מ-Android Studio, הפעל את הפרויקט על ידי בחירה בהפעלה > הפעלה... .
  2. בחר מכשיר אנדרואיד מחובר עם מצלמה כדי לבדוק את האפליקציה.

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

הוסף תלות בפרויקט

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

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

ההוראות הבאות מסבירות כיצד להוסיף את התלות הנדרשת בפרויקט ובמודול לפרויקט אפליקציית Android משלך.

כדי להוסיף תלות במודול:

  1. במודול המשתמש ב-TensorFlow Lite, עדכן את קובץ build.gradle של המודול כך שיכלול את התלות הבאה. בקוד לדוגמה, קובץ זה נמצא כאן: ...examples/lite/examples/object_detection/android/app/build.gradle ( הפניה לקוד )

    dependencies {
      ...
      implementation 'org.tensorflow:tensorflow-lite-task-vision:0.4.0'
      // Import the GPU delegate plugin Library for GPU inference
      implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'
      implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'
    }
    

    הפרויקט חייב לכלול את ספריית המשימות Vision ( tensorflow-lite-task-vision ). ספריית יחידת העיבוד הגרפית (GPU) ( tensorflow-lite-gpu-delegate-plugin ) מספקת את התשתית להפעלת האפליקציה ב-GPU, ו-Delegate ( tensorflow-lite-gpu ) מספקת את רשימת התאימות.

  2. ב-Android Studio, סנכרן את התלות בפרויקט על ידי בחירה: קובץ > סנכרן פרויקט עם קבצי Gradle .

אתחול מודל ML

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

מודל TensorFlow Lite כולל קובץ .tflite המכיל את קוד הדגם ולעיתים קרובות כולל קובץ תוויות המכיל את שמות המחלקות החזויות על ידי המודל. במקרה של זיהוי אובייקטים, מחלקות הן אובייקטים כמו אדם, כלב, חתול או מכונית.

דוגמה זו מורידה מספר מודלים המצוינים ב- download_models.gradle , והמחלקה ObjectDetectorHelper מספקת בורר עבור המודלים:

val modelName =
  when (currentModel) {
    MODEL_MOBILENETV1 -> "mobilenetv1.tflite"
    MODEL_EFFICIENTDETV0 -> "efficientdet-lite0.tflite"
    MODEL_EFFICIENTDETV1 -> "efficientdet-lite1.tflite"
    MODEL_EFFICIENTDETV2 -> "efficientdet-lite2.tflite"
    else -> "mobilenetv1.tflite"
  }

כדי לאתחל את הדגם באפליקציה שלך:

  1. הוסף קובץ מודל .tflite לספריית src/main/assets של פרויקט הפיתוח שלך, כגון: EfficientDet-Lite0 .
  2. הגדר משתנה סטטי עבור שם הקובץ של הדגם שלך. באפליקציה לדוגמה, אתה מגדיר את המשתנה modelName ל- MODEL_EFFICIENTDETV0 כדי להשתמש במודל הזיהוי EfficientDet-Lite0.
  3. הגדר את האפשרויות עבור המודל, כגון סף החיזוי, גודל ערכת התוצאות, ובאופן אופציונלי, נציגי האצת חומרה:

    val optionsBuilder =
      ObjectDetector.ObjectDetectorOptions.builder()
        .setScoreThreshold(threshold)
        .setMaxResults(maxResults)
    
  4. השתמש בהגדרות מאובייקט זה כדי לבנות אובייקט TensorFlow Lite ObjectDetector המכיל את המודל:

    objectDetector =
      ObjectDetector.createFromFileAndOptions(
        context, modelName, optionsBuilder.build())
    

ה- setupObjectDetector מגדיר את פרמטרי הדגם הבאים:

  • סף זיהוי
  • מספר מקסימלי של תוצאות זיהוי
  • מספר שרשורי עיבוד לשימוש ( BaseOptions.builder().setNumThreads(numThreads) )
  • דגם בפועל ( modelName )
  • ObjectDetector אובייקט ( objectDetector )

הגדר את מאיץ החומרה

בעת אתחול מודל TensorFlow Lite ביישום שלך, אתה יכול להשתמש בתכונות האצת חומרה כדי להאיץ את חישובי הניבוי של המודל.

נציגי TensorFlow Lite הם מודולי תוכנה שמאיצים את הביצוע של מודלים של למידת מכונה באמצעות חומרת עיבוד מיוחדת במכשיר נייד, כגון יחידות עיבוד גרפיות (GPUs), Tensor Processing Units (TPUs) ומעבדי אותות דיגיטליים (DSPs). מומלץ להשתמש בנציגים להפעלת דגמי TensorFlow Lite, אך לא חובה.

גלאי האובייקטים מאותחל באמצעות ההגדרות הנוכחיות בשרשור המשתמש בו. אתה יכול להשתמש בנציגי CPU ו- NNAPI עם גלאים שנוצרים על השרשור הראשי ומשמשים על שרשור רקע, אבל השרשור שאיתחל את הגלאי חייב להשתמש בנציג ה-GPU.

הנציגים מוגדרים בתוך הפונקציה ObjectDetectionHelper.setupObjectDetector() :

when (currentDelegate) {
    DELEGATE_CPU -> {
        // Default
    }
    DELEGATE_GPU -> {
        if (CompatibilityList().isDelegateSupportedOnThisDevice) {
            baseOptionsBuilder.useGpu()
        } else {
            objectDetectorListener?.onError("GPU is not supported on this device")
        }
    }
    DELEGATE_NNAPI -> {
        baseOptionsBuilder.useNnapi()
    }
}

למידע נוסף על שימוש בנציגי האצת חומרה עם TensorFlow Lite, ראה TensorFlow Lite Delegates .

הכן נתונים עבור המודל

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

מודל EfficientDet-Lite0 המשמש בדוגמא קוד זו מקבל Tensors המייצגים תמונות בגודל של 320 x 320, עם שלושה ערוצים (אדום, כחול וירוק) לכל פיקסל. כל ערך בטנזור הוא בית בודד בין 0 ל-255. לכן, כדי להריץ תחזיות על תמונות חדשות, האפליקציה שלך חייבת להפוך את נתוני התמונה לאובייקטי נתונים של Tensor בגודל ובצורה אלה. TensorFlow Lite Task Library Vision API מטפל בשינוי הנתונים עבורך.

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

כדי להכין נתונים מתת-מערכת המצלמה לעיבוד על ידי מודל ML:

  1. בנו אובייקט ImageAnalysis כדי לחלץ תמונות בפורמט הנדרש:

    imageAnalyzer =
        ImageAnalysis.Builder()
            .setTargetAspectRatio(AspectRatio.RATIO_4_3)
            .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888)
            .build()
            ...
    
  2. חבר את המנתח למערכת המשנה של המצלמה וצור מאגר סיביות שיכיל את הנתונים שהתקבלו מהמצלמה:

    .also {
      it.setAnalyzer(cameraExecutor) {
        image -> if (!::bitmapBuffer.isInitialized)
        { bitmapBuffer = Bitmap.createBitmap( image.width, image.height,
        Bitmap.Config.ARGB_8888 ) } detectObjects(image)
        }
      }
    
  3. חלץ את נתוני התמונה הספציפיים הדרושים למודל, והעביר את המידע על סיבוב התמונה:

    private fun detectObjects(image: ImageProxy) {
      //Copy out RGB bits to the shared bitmap buffer
      image.use {bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }
        val imageRotation = image.imageInfo.rotationDegrees
        objectDetectorHelper.detect(bitmapBuffer, imageRotation)
      }
    
  4. השלם את כל שינויי הנתונים הסופיים והוסף את נתוני התמונה לאובייקט TensorImage , כפי שמוצג בשיטת ObjectDetectorHelper.detect() של האפליקציה לדוגמה:

    val imageProcessor = ImageProcessor.Builder().add(Rot90Op(-imageRotation / 90)).build()
    // Preprocess the image and convert it into a TensorImage for detection.
    val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))
    

הפעל תחזיות

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

במחלקה fragments/CameraFragment.kt של האפליקציה לדוגמה, האובייקט imageAnalyzer בתוך הפונקציה bindCameraUseCases מעביר באופן אוטומטי נתונים למודל לצורך חיזויים כאשר האפליקציה מחוברת למצלמה.

האפליקציה משתמשת בשיטת cameraProvider.bindToLifecycle() כדי לטפל בבורר המצלמה, חלון התצוגה המקדימה ועיבוד מודל ML. המחלקה ObjectDetectorHelper.kt מטפלת בהעברת נתוני התמונה לתוך המודל. כדי להפעיל את המודל וליצור תחזיות מנתוני תמונה:

  • הפעל את החיזוי על ידי העברת נתוני התמונה לפונקציית החיזוי שלך:

    val results = objectDetector?.detect(tensorImage)
    

האובייקט TensorFlow Lite Interpreter מקבל נתונים אלה, מריץ אותם כנגד המודל ומפיק רשימה של תחזיות. לעיבוד רציף של נתונים על ידי המודל, השתמש בשיטת runForMultipleInputsOutputs() כדי שאובייקטי Interpreter לא ייווצרו ולאחר מכן יוסרו על ידי המערכת עבור כל ריצת חיזוי.

ידית פלט דגם

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

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

interface DetectorListener {
  fun onError(error: String)
  fun onResults(
    results: MutableList<Detection>?,
    inferenceTime: Long,
    imageHeight: Int,
    imageWidth: Int
  )
}

עבור המודל המשמש בדוגמה זו, כל חיזוי כולל מיקום תיבה תוחמת עבור האובייקט, תווית עבור האובייקט וציון חיזוי בין 0 ל-1 בתור Float המייצג את הביטחון של החיזוי, כאשר 1 הוא דירוג הביטחון הגבוה ביותר . באופן כללי, תחזיות עם ציון מתחת ל-50% (0.5) נחשבות לא חד משמעיות. עם זאת, אופן הטיפול בתוצאות חיזוי בעלות ערך נמוך תלוי בך ובצרכים של היישום שלך.

כדי לטפל בתוצאות חיזוי מודל:

  1. השתמש בתבנית מאזינים כדי להעביר תוצאות לקוד האפליקציה או לאובייקטים של ממשק המשתמש שלך. האפליקציה לדוגמה משתמשת בדפוס זה כדי להעביר תוצאות זיהוי מהאובייקט ObjectDetectorHelper לאובייקט CameraFragment :

    objectDetectorListener.onResults(
    // instance of CameraFragment
        results,
        inferenceTime,
        tensorImage.height,
        tensorImage.width)
    
  2. פעל בהתאם לתוצאות, כגון הצגת התחזית למשתמש. הדוגמה מציירת שכבת-על על אובייקט CameraPreview כדי להציג את התוצאה:

    override fun onResults(
      results: MutableList<Detection>?,
      inferenceTime: Long,
      imageHeight: Int,
      imageWidth: Int
    ) {
        activity?.runOnUiThread {
            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =
                String.format("%d ms", inferenceTime)
    
            // Pass necessary information to OverlayView for drawing on the canvas
            fragmentCameraBinding.overlay.setResults(
                results ?: LinkedList<Detection>(),
                imageHeight,
                imageWidth
            )
    
            // Force a redraw
            fragmentCameraBinding.overlay.invalidate()
        }
    }
    

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

הצעדים הבאים