ตรวจหา ติดตาม และจัดประเภทออบเจ็กต์ด้วยโมเดลการแยกประเภทที่กำหนดเองใน Android

คุณใช้ ML Kit เพื่อตรวจหาและติดตามวัตถุในเฟรมวิดีโอต่อเนื่องได้

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

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

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

โมเดลที่ให้มาด้วย โมเดลที่โฮสต์
รูปแบบนี้เป็นส่วนหนึ่งของ APK ของแอป ซึ่งจะเพิ่มขนาด โมเดลนี้ไม่ได้เป็นส่วนหนึ่งของ APK ของคุณ โดยโฮสต์โดยการอัปโหลดไปยัง Firebase Machine Learning
โมเดลนี้จะพร้อมใช้งานทันทีแม้ว่าอุปกรณ์ Android จะออฟไลน์อยู่ มีการดาวน์โหลดโมเดลตามคำขอ
ไม่จำเป็นต้องใช้โปรเจ็กต์ Firebase ต้องมีโปรเจ็กต์ Firebase
คุณต้องเผยแพร่แอปอีกครั้งเพื่ออัปเดตโมเดล พุชการอัปเดตโมเดลโดยไม่ต้องเผยแพร่แอปอีกครั้ง
ไม่มีการทดสอบ A/B ในตัว การทดสอบ A/B ง่ายๆ ด้วยการกำหนดค่าระยะไกลของ Firebase

ลองเลย

ก่อนเริ่มต้น

  1. ในไฟล์ build.gradle ระดับโปรเจ็กต์ ให้ตรวจสอบว่าได้ใส่ที่เก็บ Maven ของ Google ไว้ทั้งในส่วน buildscript และ allprojects

  2. เพิ่มทรัพยากร Dependency สำหรับไลบรารี Android ของ ML Kit ลงในไฟล์ Gradle ระดับแอปของโมดูล ซึ่งโดยปกติจะอยู่ที่ app/build.gradle

    สำหรับการรวมโมเดลกับแอป ให้ทำดังนี้

    dependencies {
      // ...
      // Object detection & tracking feature with custom bundled model
      implementation 'com.google.mlkit:object-detection-custom:17.0.1'
    }
    

    หากต้องการดาวน์โหลดโมเดลแบบไดนามิกจาก Firebase ให้เพิ่มการอ้างอิง linkFirebase ดังนี้

    dependencies {
      // ...
      // Object detection & tracking feature with model downloaded
      // from firebase
      implementation 'com.google.mlkit:object-detection-custom:17.0.1'
      implementation 'com.google.mlkit:linkfirebase:17.0.0'
    }
    
  3. หากต้องการดาวน์โหลดโมเดล ให้ตรวจสอบว่าคุณเพิ่ม Firebase ลงในโปรเจ็กต์ Android หากยังไม่ได้ทำ ขั้นตอนนี้ไม่จำเป็นเมื่อรวมโมเดลเข้าด้วยกัน

1. โหลดโมเดล

กำหนดค่าต้นทางของโมเดลในเครื่อง

วิธีรวมโมเดลเข้ากับแอป

  1. คัดลอกไฟล์โมเดล (โดยปกติจะลงท้ายด้วย .tflite หรือ .lite) ไปยังโฟลเดอร์ assets/ ของแอป (คุณอาจต้องสร้างโฟลเดอร์ก่อนโดยคลิกขวาที่โฟลเดอร์ app/ แล้วคลิกใหม่ > โฟลเดอร์ > โฟลเดอร์ชิ้นงาน)

  2. จากนั้นเพิ่มข้อมูลต่อไปนี้ลงในไฟล์ build.gradle ของแอปเพื่อให้มั่นใจว่า Gradle จะไม่บีบอัดไฟล์โมเดลเมื่อสร้างแอป

    android {
        // ...
        aaptOptions {
            noCompress "tflite"
            // or noCompress "lite"
        }
    }
    

    ไฟล์โมเดลจะรวมอยู่ในแพ็กเกจแอปและพร้อมให้เล่นใน ML Kit เป็นเนื้อหาดิบ

  3. สร้างออบเจ็กต์ LocalModel โดยระบุเส้นทางไปยังไฟล์โมเดล:

    Kotlin

    val localModel = LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute file path to model file)
            // or .setUri(URI to model file)
            .build()

    Java

    LocalModel localModel =
        new LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute file path to model file)
            // or .setUri(URI to model file)
            .build();

กำหนดค่าแหล่งที่มาของโมเดลที่โฮสต์ใน Firebase

หากต้องการใช้โมเดลที่โฮสต์จากระยะไกล ให้สร้างออบเจ็กต์ CustomRemoteModel ด้วย FirebaseModelSource โดยระบุชื่อที่คุณกำหนดโมเดลเมื่อคุณเผยแพร่:

Kotlin

// Specify the name you assigned in the Firebase console.
val remoteModel =
    CustomRemoteModel
        .Builder(FirebaseModelSource.Builder("your_model_name").build())
        .build()

Java

// Specify the name you assigned in the Firebase console.
CustomRemoteModel remoteModel =
    new CustomRemoteModel
        .Builder(new FirebaseModelSource.Builder("your_model_name").build())
        .build();

จากนั้นเริ่มงานดาวน์โหลดโมเดล โดยระบุเงื่อนไขที่ต้องการอนุญาตให้ดาวน์โหลด หากโมเดลไม่ได้อยู่ในอุปกรณ์ หรือหากมีโมเดลเวอร์ชันใหม่ งานจะดาวน์โหลดโมเดลแบบไม่พร้อมกันจาก Firebase

Kotlin

val downloadConditions = DownloadConditions.Builder()
    .requireWifi()
    .build()
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
    .addOnSuccessListener {
        // Success.
    }

Java

DownloadConditions downloadConditions = new DownloadConditions.Builder()
        .requireWifi()
        .build();
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(@NonNull Task task) {
                // Success.
            }
        });

แอปจำนวนมากเริ่มต้นงานดาวน์โหลดในโค้ดการเริ่มต้น แต่คุณสามารถเริ่มงานได้ทุกเมื่อก่อนที่จะต้องใช้โมเดล

2. กำหนดค่าตัวตรวจจับออบเจ็กต์

หลังจากกำหนดค่าแหล่งที่มาของโมเดล ให้กำหนดค่าตัวตรวจจับออบเจ็กต์สำหรับ Use Case ของคุณด้วยออบเจ็กต์ CustomObjectDetectorOptions คุณสามารถเปลี่ยนการตั้งค่าต่อไปนี้

การตั้งค่าตัวตรวจจับออบเจ็กต์
โหมดการตรวจจับ STREAM_MODE (ค่าเริ่มต้น) | SINGLE_IMAGE_MODE

ใน STREAM_MODE (ค่าเริ่มต้น) ตัวตรวจจับออบเจ็กต์จะทำงานโดยมีเวลาในการตอบสนองต่ำ แต่อาจทำให้เกิดผลลัพธ์ที่ไม่สมบูรณ์ (เช่น กรอบล้อมรอบหรือป้ายกำกับหมวดหมู่ที่ไม่ระบุ) ในการเรียกใช้ตัวตรวจจับ 2-3 รายการแรก นอกจากนี้ใน STREAM_MODE ตัวตรวจจับจะกำหนดรหัสติดตามให้กับออบเจ็กต์ ซึ่งคุณสามารถใช้เพื่อติดตามออบเจ็กต์ในเฟรมได้ ใช้โหมดนี้เมื่อคุณต้องการติดตามออบเจ็กต์ หรือเมื่อเวลาในการตอบสนองต่ำเป็นสิ่งสำคัญ เช่น เมื่อประมวลผลสตรีมวิดีโอแบบเรียลไทม์

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

ตรวจหาและติดตามออบเจ็กต์หลายรายการ false (ค่าเริ่มต้น) | true

ไม่ว่าจะตรวจหาและติดตามออบเจ็กต์สูงสุด 5 รายการ หรือเฉพาะออบเจ็กต์ที่โดดเด่นที่สุด (ค่าเริ่มต้น)

จำแนกประเภทวัตถุ false (ค่าเริ่มต้น) | true

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

เกณฑ์ความเชื่อมั่นในการแยกประเภท

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

จำนวนป้ายกำกับสูงสุดต่อออบเจ็กต์

จำนวนป้ายกำกับสูงสุดต่อออบเจ็กต์ที่ตัวตรวจจับจะส่งคืน หากไม่ได้ตั้งค่า ระบบจะใช้ค่าเริ่มต้น 10

API การตรวจจับและการติดตามวัตถุได้รับการเพิ่มประสิทธิภาพเพื่อการใช้งานหลักๆ 2 กรณีดังนี้

  • การตรวจจับแบบเรียลไทม์และการติดตามวัตถุที่โดดเด่นที่สุดในช่องมองภาพของกล้อง
  • การตรวจจับวัตถุหลายรายการจากภาพนิ่ง

หากต้องการกำหนดค่า API สำหรับ Use Case เหล่านี้ด้วยโมเดลที่มากับเครื่อง ให้ทำดังนี้

Kotlin

// Live detection and tracking
val customObjectDetectorOptions =
        CustomObjectDetectorOptions.Builder(localModel)
        .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .setMaxPerObjectLabelCount(3)
        .build()

// Multiple object detection in static images
val customObjectDetectorOptions =
        CustomObjectDetectorOptions.Builder(localModel)
        .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
        .enableMultipleObjects()
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .setMaxPerObjectLabelCount(3)
        .build()

val objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions)

Java

// Live detection and tracking
CustomObjectDetectorOptions customObjectDetectorOptions =
        new CustomObjectDetectorOptions.Builder(localModel)
                .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();

// Multiple object detection in static images
CustomObjectDetectorOptions customObjectDetectorOptions =
        new CustomObjectDetectorOptions.Builder(localModel)
                .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableMultipleObjects()
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();

ObjectDetector objectDetector =
    ObjectDetection.getClient(customObjectDetectorOptions);

หากคุณมีโมเดลที่โฮสต์จากระยะไกล คุณจะต้องตรวจสอบว่าโมเดลดังกล่าวได้รับการดาวน์โหลดแล้วก่อนที่จะเรียกใช้ คุณจะตรวจสอบสถานะของงานการดาวน์โหลดโมเดลได้โดยใช้เมธอด isModelDownloaded() ของตัวจัดการโมเดล

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

Kotlin

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded ->
    val optionsBuilder =
        if (isDownloaded) {
            CustomObjectDetectorOptions.Builder(remoteModel)
        } else {
            CustomObjectDetectorOptions.Builder(localModel)
        }
    val customObjectDetectorOptions = optionsBuilder
            .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
            .enableClassification()
            .setClassificationConfidenceThreshold(0.5f)
            .setMaxPerObjectLabelCount(3)
            .build()
    val objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions)
}

Java

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener(new OnSuccessListener() {
        @Override
        public void onSuccess(Boolean isDownloaded) {
            CustomObjectDetectorOptions.Builder optionsBuilder;
            if (isDownloaded) {
                optionsBuilder = new CustomObjectDetectorOptions.Builder(remoteModel);
            } else {
                optionsBuilder = new CustomObjectDetectorOptions.Builder(localModel);
            }
            CustomObjectDetectorOptions customObjectDetectorOptions = optionsBuilder
                .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();
            ObjectDetector objectDetector =
                ObjectDetection.getClient(customObjectDetectorOptions);
        }
});

หากคุณมีเฉพาะโมเดลที่โฮสต์จากระยะไกล คุณควรปิดใช้ฟังก์ชันการทำงานเกี่ยวกับโมเดล เช่น ทำให้เป็นสีเทาหรือซ่อนบางส่วนของ UI จนกว่าคุณจะยืนยันว่าได้ดาวน์โหลดโมเดลแล้ว ซึ่งทำได้โดยการแนบ Listener ลงในเมธอด download() ของตัวจัดการโมเดล ดังนี้

Kotlin

RemoteModelManager.getInstance().download(remoteModel, conditions)
    .addOnSuccessListener {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.
    }

Java

RemoteModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(Void v) {
              // Download complete. Depending on your app, you could enable
              // the ML feature, or switch from the local model to the remote
              // model, etc.
            }
        });

3. เตรียมรูปภาพอินพุต

สร้างวัตถุ InputImage จากรูปภาพ ตัวตรวจจับออบเจ็กต์จะทำงานจาก Bitmap, NV21 ByteBuffer หรือ media.Image โดยตรง เราขอแนะนำให้สร้าง InputImage จากแหล่งที่มาเหล่านั้น หากคุณมีสิทธิ์เข้าถึงโดยตรง หากคุณสร้าง InputImage จากแหล่งที่มาอื่นๆ เราจะจัดการ Conversion เป็นการภายในให้ และอาจมีประสิทธิภาพลดลง

คุณสร้างออบเจ็กต์ InputImage จากแหล่งที่มาที่แตกต่างกันได้ โดยแต่ละรายการจะมีคำอธิบายอยู่ด้านล่าง

ใช้ media.Image

หากต้องการสร้างออบเจ็กต์ InputImage จากออบเจ็กต์ media.Image เช่น เมื่อคุณจับภาพจากกล้องของอุปกรณ์ ให้ส่งวัตถุ media.Image และการหมุนรูปภาพไปยัง InputImage.fromMediaImage()

หากใช้ไลบรารี CameraX คลาส OnImageCapturedListener และ ImageAnalysis.Analyzer จะคำนวณค่าการหมุนให้คุณ

Kotlin

private class YourImageAnalyzer : ImageAnalysis.Analyzer {

    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
            // Pass image to an ML Kit Vision API
            // ...
        }
    }
}

Java

private class YourAnalyzer implements ImageAnalysis.Analyzer {

    @Override
    public void analyze(ImageProxy imageProxy) {
        Image mediaImage = imageProxy.getImage();
        if (mediaImage != null) {
          InputImage image =
                InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
          // Pass image to an ML Kit Vision API
          // ...
        }
    }
}

หากไม่ได้ใช้คลังภาพที่มีระดับการหมุนของภาพ คุณจะคํานวณจากองศาการหมุนของอุปกรณ์และการวางแนวของเซ็นเซอร์กล้องในอุปกรณ์ได้ดังนี้

Kotlin

private val ORIENTATIONS = SparseIntArray()

init {
    ORIENTATIONS.append(Surface.ROTATION_0, 0)
    ORIENTATIONS.append(Surface.ROTATION_90, 90)
    ORIENTATIONS.append(Surface.ROTATION_180, 180)
    ORIENTATIONS.append(Surface.ROTATION_270, 270)
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Throws(CameraAccessException::class)
private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    val deviceRotation = activity.windowManager.defaultDisplay.rotation
    var rotationCompensation = ORIENTATIONS.get(deviceRotation)

    // Get the device's sensor orientation.
    val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager
    val sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360
    }
    return rotationCompensation
}

Java

private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 0);
    ORIENTATIONS.append(Surface.ROTATION_90, 90);
    ORIENTATIONS.append(Surface.ROTATION_180, 180);
    ORIENTATIONS.append(Surface.ROTATION_270, 270);
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing)
        throws CameraAccessException {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
    int rotationCompensation = ORIENTATIONS.get(deviceRotation);

    // Get the device's sensor orientation.
    CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE);
    int sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION);

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360;
    }
    return rotationCompensation;
}

จากนั้นส่งออบเจ็กต์ media.Image และค่าองศาการหมุนไปที่ InputImage.fromMediaImage():

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

InputImage image = InputImage.fromMediaImage(mediaImage, rotation);

การใช้ URI ของไฟล์

หากต้องการสร้างออบเจ็กต์ InputImage จาก URI ของไฟล์ ให้ส่งบริบทของแอปและ URI ของไฟล์ไปยัง InputImage.fromFilePath() ซึ่งจะเป็นประโยชน์เมื่อคุณใช้ Intent ACTION_GET_CONTENT เพื่อแจ้งให้ผู้ใช้เลือกรูปภาพจากแอปแกลเลอรี

Kotlin

val image: InputImage
try {
    image = InputImage.fromFilePath(context, uri)
} catch (e: IOException) {
    e.printStackTrace()
}

Java

InputImage image;
try {
    image = InputImage.fromFilePath(context, uri);
} catch (IOException e) {
    e.printStackTrace();
}

หากใช้ ByteBuffer หรือ ByteArray

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

Kotlin

val image = InputImage.fromByteBuffer(
        byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)
// Or:
val image = InputImage.fromByteArray(
        byteArray,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)

Java

InputImage image = InputImage.fromByteBuffer(byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);
// Or:
InputImage image = InputImage.fromByteArray(
        byteArray,
        /* image width */480,
        /* image height */360,
        rotation,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);

ใช้ Bitmap

หากต้องการสร้างออบเจ็กต์ InputImage จากออบเจ็กต์ Bitmap ให้ประกาศต่อไปนี้

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

รูปภาพแสดงด้วยวัตถุ Bitmap ร่วมกับองศาการหมุน

4. เรียกใช้ตัวตรวจจับวัตถุ

Kotlin

objectDetector
    .process(image)
    .addOnFailureListener(e -> {...})
    .addOnSuccessListener(results -> {
        for (detectedObject in results) {
          // ...
        }
    });

Java

objectDetector
    .process(image)
    .addOnFailureListener(e -> {...})
    .addOnSuccessListener(results -> {
        for (DetectedObject detectedObject : results) {
          // ...
        }
    });

5. รับข้อมูลเกี่ยวกับออบเจ็กต์ที่ติดป้ายกำกับ

หากการเรียก process() สำเร็จ ระบบจะส่งรายการ DetectedObject ไปยังผู้ฟังที่ประสบความสำเร็จ

DetectedObject แต่ละรายการจะมีพร็อพเพอร์ตี้ต่อไปนี้

กรอบล้อมรอบ Rect ที่แสดงตำแหน่งของวัตถุในรูปภาพ
รหัสติดตาม จำนวนเต็มที่ระบุวัตถุในรูปภาพต่างๆ Null ใน SINGLE_IMAGE_mode
ป้ายกำกับ
คำอธิบายป้ายกำกับ คำอธิบายข้อความของป้ายกำกับ แสดงผลเฉพาะเมื่อข้อมูลเมตาของโมเดล TensorFlow Lite มีคำอธิบายป้ายกำกับ
ดัชนีป้ายกำกับ ดัชนีของป้ายกำกับจากป้ายกำกับทั้งหมดที่ตัวแยกประเภทรองรับ
ความเชื่อมั่นของป้ายกำกับ ค่าความเชื่อมั่นของการจัดประเภทออบเจ็กต์

Kotlin

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (detectedObject in results) {
    val boundingBox = detectedObject.boundingBox
    val trackingId = detectedObject.trackingId
    for (label in detectedObject.labels) {
      val text = label.text
      val index = label.index
      val confidence = label.confidence
    }
}

Java

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (DetectedObject detectedObject : results) {
  Rect boundingBox = detectedObject.getBoundingBox();
  Integer trackingId = detectedObject.getTrackingId();
  for (Label label : detectedObject.getLabels()) {
    String text = label.getText();
    int index = label.getIndex();
    float confidence = label.getConfidence();
  }
}

ช่วยให้ผู้ใช้ได้รับประสบการณ์ที่ดี

เพื่อให้ผู้ใช้ได้รับประสบการณ์ที่ดีที่สุด โปรดปฏิบัติตามหลักเกณฑ์ต่อไปนี้ในแอป

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

นอกจากนี้ ลองไปที่ แอปแสดงดีไซน์ Material ของ ML Kit และคอลเล็กชัน ดีไซน์ Material สำหรับฟีเจอร์ที่ขับเคลื่อนโดยแมชชีนเลิร์นนิง

Improving performance

หากคุณต้องการใช้การตรวจจับวัตถุในแอปพลิเคชันแบบเรียลไทม์ ให้ทำตามหลักเกณฑ์ต่อไปนี้เพื่อให้ได้อัตราเฟรมที่ดีที่สุด

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

  • หากคุณใช้ Camera หรือ camera2 API ให้ควบคุมการเรียกใช้ตัวตรวจจับ หากเฟรมวิดีโอใหม่พร้อมใช้งานขณะที่ตัวตรวจจับกำลังทำงาน ให้วางเฟรมลง ดูคลาส VisionProcessorBase ในแอปตัวอย่าง Quickstart
  • หากคุณใช้ CameraX API โปรดตรวจสอบว่าได้ตั้งค่ากลยุทธ์ Backpressure ไว้เป็นค่าเริ่มต้น ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST ซึ่งจะช่วยรับประกันว่าระบบจะนำส่งรูปภาพเพียง 1 รูปเพื่อทำการวิเคราะห์ต่อครั้ง หากมีการสร้างรูปภาพเพิ่มเติมเมื่อเครื่องมือวิเคราะห์ไม่ว่าง ระบบจะนำรูปภาพเหล่านั้นออกโดยอัตโนมัติและไม่เข้าคิวสำหรับการนำส่ง เมื่อปิดรูปภาพที่วิเคราะห์แล้วโดยเรียกใช้ ImageProxy.close() ระบบจะส่งรูปภาพล่าสุดถัดไป
  • หากคุณใช้เอาต์พุตจากตัวตรวจจับเพื่อวางซ้อนกราฟิกบนรูปภาพอินพุต ให้รับผลลัพธ์จาก ML Kit ก่อน จากนั้นจึงแสดงผลรูปภาพและการวางซ้อนในขั้นตอนเดียว ซึ่งจะแสดงผลบนพื้นผิวแสดงผลเพียงครั้งเดียวต่อเฟรมอินพุตแต่ละเฟรม ดูคลาส CameraSourcePreview และ GraphicOverlay ในแอปตัวอย่างสำหรับการเริ่มต้นใช้งานอย่างรวดเร็ว
  • หากคุณใช้ Camera2 API ให้จับภาพในรูปแบบ ImageFormat.YUV_420_888 หากคุณใช้ Camera API เวอร์ชันเก่า ให้จับภาพในรูปแบบ ImageFormat.NV21