คำแนะนำในการแยกประเภทรูปภาพสำหรับ Android

งานตัวแยกประเภทรูปภาพ MediaPipe ให้คุณแยกประเภทรูปภาพได้ คุณใช้งานนี้เพื่อระบุว่ารูปภาพแสดงถึงอะไรในชุดหมวดหมู่ที่กำหนดไว้ในเวลาฝึกได้ คำแนะนำเหล่านี้จะแสดงวิธีใช้ตัวแยกประเภทรูปภาพ กับแอป Android ตัวอย่างโค้ดที่อธิบายไว้ในวิธีการเหล่านี้มีอยู่ใน GitHub

คุณสามารถดูการใช้งานจริงของงานนี้ได้โดยดูการสาธิตเว็บ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับความสามารถ โมเดล และตัวเลือกการกำหนดค่าของงานนี้ โปรดดูที่ภาพรวม

ตัวอย่างโค้ด

โค้ดตัวอย่าง MediaPipe Tasks คือการใช้งานแบบง่ายๆ ของแอปตัวแยกประเภทรูปภาพสำหรับ Android ตัวอย่างนี้ใช้กล้องบนอุปกรณ์ Android ที่จับต้องได้เพื่อจำแนกวัตถุอย่างต่อเนื่อง และยังใช้รูปภาพและวิดีโอจากแกลเลอรีของอุปกรณ์เพื่อจำแนกวัตถุแบบคงที่ได้อีกด้วย

คุณสามารถใช้แอปเป็นจุดเริ่มต้นสำหรับแอป Android ของคุณเอง หรือใช้อ้างอิงเมื่อแก้ไขแอปที่มีอยู่ โค้ดตัวอย่างตัวแยกประเภทรูปภาพจะโฮสต์อยู่ใน GitHub

ดาวน์โหลดโค้ด

วิธีการต่อไปนี้แสดงวิธีสร้างสำเนาโค้ดตัวอย่างในเครื่องโดยใช้เครื่องมือบรรทัดคำสั่ง git

วิธีดาวน์โหลดโค้ดตัวอย่าง

  1. โคลนที่เก็บ Git โดยใช้คำสั่งต่อไปนี้
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. เลือกกำหนดค่าอินสแตนซ์ git เพื่อใช้การชำระเงินแบบกระจัดกระจายเพื่อให้มีเพียงไฟล์สำหรับแอปตัวอย่างตัวแยกประเภทรูปภาพเท่านั้น ดังนี้
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_classification/android
    

หลังจากสร้างโค้ดตัวอย่างในเวอร์ชันในเครื่องแล้ว คุณสามารถนำเข้าโปรเจ็กต์ลงใน Android Studio และเรียกใช้แอป ดูวิธีการได้ในคู่มือการตั้งค่าสำหรับ Android

องค์ประกอบหลัก

ไฟล์ต่อไปนี้มีโค้ดที่สำคัญสำหรับแอปพลิเคชันตัวอย่างการจัดประเภทรูปภาพนี้

  • ImageClassifierHelper.kt - เริ่มต้นตัวแยกประเภทรูปภาพและจัดการโมเดลและมอบสิทธิ์การเลือก
  • MainActivity.kt - ใช้แอปพลิเคชัน รวมถึงการเรียกใช้ ImageClassificationHelper และ ClassificationResultsAdapter
  • ClassificationResultsAdapter.kt - จัดการและจัดรูปแบบผลลัพธ์

ตั้งค่า

ส่วนนี้จะอธิบายขั้นตอนสำคัญในการตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์และโปรเจ็กต์โค้ดเพื่อใช้ตัวแยกประเภทรูปภาพ สำหรับข้อมูลทั่วไปเกี่ยวกับการตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์เพื่อใช้งาน MediaPipe รวมถึงข้อกำหนดเวอร์ชันของแพลตฟอร์ม โปรดดูคู่มือการตั้งค่าสำหรับ Android

การอ้างอิง

ตัวแยกประเภทรูปภาพใช้ไลบรารี com.google.mediapipe:tasks-vision เพิ่มการอ้างอิงนี้ลงในไฟล์ build.gradle ของโปรเจ็กต์การพัฒนาแอป Android นำเข้าทรัพยากร Dependency ที่จำเป็นด้วยโค้ดต่อไปนี้

dependencies {
    ...
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
}

รุ่น

งานตัวแยกประเภทรูปภาพ MediaPipe ต้องใช้โมเดลที่ผ่านการฝึกและเข้ากันได้กับงานนี้ ดูข้อมูลเพิ่มเติมเกี่ยวกับโมเดลที่ผ่านการฝึกที่ใช้ได้สําหรับตัวแยกประเภทรูปภาพได้ที่ภาพรวมงานส่วนโมเดล

เลือกและดาวน์โหลดโมเดล จากนั้นเก็บโมเดลไว้ในไดเรกทอรีโปรเจ็กต์ของคุณ:

<dev-project-root>/src/main/assets

ใช้เมธอด BaseOptions.Builder.setModelAssetPath() เพื่อระบุเส้นทางที่โมเดลใช้ วิธีการนี้จะอ้างถึงในตัวอย่างโค้ดในส่วนถัดไป

ในโค้ดตัวอย่างตัวแยกประเภทรูปภาพ โมเดลจะได้รับการกำหนดในไฟล์ ImageClassifierHelper.kt

สร้างงาน

คุณใช้ฟังก์ชัน createFromOptions เพื่อสร้างงานได้ ฟังก์ชัน createFromOptions จะยอมรับตัวเลือกการกำหนดค่าต่างๆ เช่น โหมดการวิ่ง ภาษาของชื่อที่แสดง จำนวนผลลัพธ์สูงสุด เกณฑ์ความเชื่อมั่น และรายการอนุญาตหมวดหมู่หรือรายการปฏิเสธ ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกการกำหนดค่าได้ที่ภาพรวมการกำหนดค่า

งานตัวแยกประเภทรูปภาพรองรับอินพุต 3 ประเภท ได้แก่ ภาพนิ่ง ไฟล์วิดีโอ และสตรีมวิดีโอสด คุณต้องระบุโหมดการทำงานให้สอดคล้องกับประเภทข้อมูลอินพุตของคุณเมื่อสร้างงาน เลือกแท็บที่สอดคล้องกับประเภทข้อมูลอินพุตเพื่อดูวิธีสร้างงานและเรียกใช้การอนุมาน

รูปภาพ

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.IMAGE)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

วิดีโอ

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.VIDEO)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

สตรีมแบบสด

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setMaxResults(5)
    .setResultListener((result, inputImage) -> {
         // Process the classification result here.
    })
    .setErrorListener((result, inputImage) -> {
         // Process the classification errors here.
    })
    .build()
imageClassifier = ImageClassifier.createFromOptions(context, options)
    

การใช้โค้ดตัวอย่างของตัวแยกประเภทรูปภาพช่วยให้ผู้ใช้สลับระหว่างโหมดการประมวลผลได้ วิธีนี้ทำให้โค้ดการสร้างงานซับซ้อนขึ้นและอาจไม่เหมาะกับกรณีการใช้งานของคุณ คุณดูโค้ดนี้ได้ในฟังก์ชัน setupImageClassifier() ของไฟล์ ImageClassifierHelper.kt

ตัวเลือกการกำหนดค่า

งานนี้มีตัวเลือกการกำหนดค่าต่อไปนี้สำหรับแอป Android

ชื่อตัวเลือก คำอธิบาย ช่วงค่า ค่าเริ่มต้น
runningMode ตั้งค่าโหมดการทำงานสำหรับงาน มี 3 โหมด ได้แก่

รูปภาพ: โหมดสำหรับอินพุตรูปภาพเดียว

วิดีโอ: โหมดสำหรับเฟรมที่ถอดรหัสของวิดีโอ

LIVE_STREAM: โหมดสำหรับสตรีมแบบสดของข้อมูลอินพุต เช่น จากกล้อง ในโหมดนี้ ต้องเรียกใช้ resultsListener เพื่อตั้งค่า Listener ให้รับผลลัพธ์ แบบไม่พร้อมกัน
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
displayNamesLocale ตั้งค่าภาษาของป้ายกำกับที่จะใช้กับชื่อที่แสดงซึ่งระบุไว้ในข้อมูลเมตาของโมเดลงาน หากมี ค่าเริ่มต้นคือ en สำหรับภาษาอังกฤษ คุณเพิ่มป้ายกำกับที่แปลแล้วไปยังข้อมูลเมตาของโมเดลที่กำหนดเองได้โดยใช้ TensorFlow Lite Metadata Writer API รหัสภาษา en
maxResults ตั้งค่าจำนวนผลลัพธ์การจัดประเภทที่มีคะแนนสูงสุด (ไม่บังคับ) ที่จะแสดง หาก < 0 ระบบจะแสดงผลลัพธ์ที่ใช้ได้ทั้งหมด จำนวนบวกใดก็ได้ -1
scoreThreshold ตั้งค่าเกณฑ์คะแนนการคาดการณ์ที่ลบล้างเกณฑ์ที่ระบุไว้ในข้อมูลเมตาของโมเดล (หากมี) ผลลัพธ์ที่ต่ำกว่าค่านี้ถูกปฏิเสธ ทศนิยมใดก็ได้ ไม่ได้ตั้งค่า
categoryAllowlist ตั้งค่ารายการชื่อหมวดหมู่ที่อนุญาตซึ่งไม่บังคับ หากไม่ว่างเปล่า ระบบจะกรองผลลัพธ์การจัดประเภทที่มีชื่อหมวดหมู่ที่ไม่ได้อยู่ในชุดนี้ออก ระบบจะไม่สนใจชื่อหมวดหมู่ที่ซ้ำกันหรือไม่รู้จัก ตัวเลือกนี้จะใช้กับ categoryDenylist ไม่ได้ และการใช้ทั้ง 2 อย่างจะทำให้เกิดข้อผิดพลาด สตริงใดก็ได้ ไม่ได้ตั้งค่า
categoryDenylist ตั้งค่ารายการตัวเลือกชื่อหมวดหมู่ที่ไม่ได้รับอนุญาต หากไม่ว่างเปล่า ผลการจัดประเภทที่มีชื่อหมวดหมู่อยู่ในชุดนี้จะถูกกรองออก ระบบจะไม่สนใจชื่อหมวดหมู่ที่ซ้ำกันหรือไม่รู้จัก ตัวเลือกนี้ใช้ร่วมกับ categoryAllowlist ไม่ได้ และการใช้ทั้ง 2 อย่างจะทําให้เกิดข้อผิดพลาด สตริงใดก็ได้ ไม่ได้ตั้งค่า
resultListener ตั้งค่า Listener ผลลัพธ์เพื่อรับผลลัพธ์การจัดประเภทแบบไม่พร้อมกันเมื่อตัวแยกประเภทรูปภาพอยู่ในโหมดสตรีมแบบสด ใช้ได้เมื่อตั้งค่าโหมดวิ่งเป็น LIVE_STREAM เท่านั้น ไม่มีข้อมูล ไม่ได้ตั้งค่า
errorListener ตั้งค่า Listener ข้อผิดพลาดที่ไม่บังคับ ไม่มีข้อมูล ไม่ได้ตั้งค่า

เตรียมข้อมูล

ตัวแยกประเภทรูปภาพใช้งานได้กับรูปภาพ ไฟล์วิดีโอ และวิดีโอสตรีมแบบสด งานนี้จะจัดการกับการประมวลผลอินพุตข้อมูลล่วงหน้า รวมถึงการปรับขนาด การหมุน และการปรับค่าให้เป็นมาตรฐาน

คุณต้องแปลงรูปภาพหรือเฟรมอินพุตเป็นออบเจ็กต์ com.google.mediapipe.framework.image.MPImage ก่อนที่จะส่งไปยังตัวแยกประเภทรูปภาพ

รูปภาพ

import com.google.mediapipe.framework.image.BitmapImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Load an image on the user’s device as a Bitmap object using BitmapFactory.

// Convert an Android’s Bitmap object to a MediaPipe’s Image object.
Image mpImage = new BitmapImageBuilder(bitmap).build();
    

วิดีโอ

import com.google.mediapipe.framework.image.BitmapImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Load a video file on the user's device using MediaMetadataRetriever

// From the video’s metadata, load the METADATA_KEY_DURATION and
// METADATA_KEY_VIDEO_FRAME_COUNT value. You’ll need them
// to calculate the timestamp of each frame later.

// Loop through the video and load each frame as a Bitmap object.

// Convert the Android’s Bitmap object to a MediaPipe’s Image object.
Image mpImage = new BitmapImageBuilder(frame).build();
    

สตรีมแบบสด

import com.google.mediapipe.framework.image.MediaImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Create a CameraX’s ImageAnalysis to continuously receive frames 
// from the device’s camera. Configure it to output frames in RGBA_8888
// format to match with what is required by the model.

// For each Android’s ImageProxy object received from the ImageAnalysis, 
// extract the encapsulated Android’s Image object and convert it to 
// a MediaPipe’s Image object.
android.media.Image mediaImage = imageProxy.getImage()
Image mpImage = new MediaImageBuilder(mediaImage).build();
    

ในโค้ดตัวอย่างของตัวแยกประเภทรูปภาพ การจัดเตรียมข้อมูลจะจัดการในไฟล์ ImageClassifierHelper.kt

เรียกใช้งาน

คุณสามารถเรียกใช้ฟังก์ชัน classify ที่สอดคล้องกับโหมดการวิ่งเพื่อทริกเกอร์การอนุมานได้ Image Classifier API จะแสดงผลหมวดหมู่ที่เป็นไปได้สำหรับวัตถุภายในรูปภาพหรือเฟรมอินพุต

รูปภาพ

ImageClassifierResult classifierResult = imageClassifier.classify(image);
    

วิดีโอ

// Calculate the timestamp in milliseconds of the current frame.
long frame_timestamp_ms = 1000 * video_duration * frame_index / frame_count;

// Run inference on the frame.
ImageClassifierResult classifierResult =
    imageClassifier.classifyForVideo(image, frameTimestampMs);
    

สตรีมแบบสด


// Run inference on the frame. The classifications results will be available 
// via the `resultListener` provided in the `ImageClassifierOptions` when 
// the image classifier was created.
imageClassifier.classifyAsync(image, frameTimestampMs);
    

โปรดทราบดังต่อไปนี้

  • เมื่อทำงานในโหมดวิดีโอหรือโหมดสตรีมแบบสด คุณต้องระบุการประทับเวลาของเฟรมอินพุตในงานเครื่องมือแยกประเภทรูปภาพด้วย
  • เมื่อทำงานในโหมดรูปภาพหรือวิดีโอ งานตัวแยกประเภทรูปภาพจะบล็อกชุดข้อความปัจจุบันจนกว่าการประมวลผลรูปภาพหรือเฟรมอินพุตจะเสร็จสิ้น หากต้องการหลีกเลี่ยงการบล็อกอินเทอร์เฟซผู้ใช้ ให้เรียกใช้การประมวลผลในเทรดเบื้องหลัง
  • เมื่อทำงานในโหมดสตรีมแบบสด งานตัวแยกประเภทรูปภาพจะไม่บล็อกชุดข้อความปัจจุบัน แต่จะกลับมาทำงานทันที โดยจะเรียกใช้เครื่องมือฟังผลลัพธ์พร้อมผลลัพธ์การตรวจจับทุกครั้งที่ประมวลผลเฟรมอินพุตเสร็จแล้ว หากมีการเรียกฟังก์ชัน classifyAsync เมื่องานตัวแยกประเภทรูปภาพไม่ว่างในการประมวลผลเฟรมอื่น งานจะไม่สนใจเฟรมอินพุตใหม่

ในโค้ดตัวอย่างของตัวแยกประเภทรูปภาพ ฟังก์ชัน classify ได้รับการกำหนดไว้ในไฟล์ ImageClassifierHelper.kt

จัดการและแสดงผลลัพธ์

เมื่อใช้การอนุมาน งานตัวแยกประเภทรูปภาพจะแสดงผลออบเจ็กต์ ImageClassifierResult ซึ่งมีรายการหมวดหมู่ที่เป็นไปได้สำหรับวัตถุภายในรูปภาพหรือเฟรมอินพุต

ตัวอย่างต่อไปนี้แสดงตัวอย่างข้อมูลเอาต์พุตจากงานนี้

ImageClassifierResult:
 Classifications #0 (single classification head):
  head index: 0
  category #0:
   category name: "/m/01bwb9"
   display name: "Passer domesticus"
   score: 0.91406
   index: 671
  category #1:
   category name: "/m/01bwbt"
   display name: "Passer montanus"
   score: 0.00391
   index: 670

ผลลัพธ์นี้มาจากการเรียกใช้ Bird Classifier ใน:

ในโค้ดตัวอย่างตัวแยกประเภทรูปภาพ คลาส ClassificationResultsAdapter ในไฟล์ ClassificationResultsAdapter.kt จะจัดการผลลัพธ์ดังต่อไปนี้

fun updateResults(imageClassifierResult: ImageClassifierResult? = null) {
    categories = MutableList(adapterSize) { null }
    if (imageClassifierResult != null) {
        val sortedCategories = imageClassifierResult.classificationResult()
            .classifications()[0].categories().sortedBy { it.index() }
        val min = kotlin.math.min(sortedCategories.size, categories.size)
        for (i in 0 until min) {
            categories[i] = sortedCategories[i]
        }
    }
}