Android पर, AutoML से ट्रेनिंग वाले मॉडल की मदद से, इमेज को लेबल करना

ऑटोएमएल विज़न एज का उपयोग करके अपने स्वयं के मॉडल को प्रशिक्षित करने के बाद, आप छवियों को लेबल करने के लिए इसे अपने ऐप में उपयोग कर सकते हैं।

ऑटोएमएल विज़न एज से प्रशिक्षित मॉडल को एकीकृत करने के दो तरीके हैं: आप मॉडल को अपने ऐप के एसेट फ़ोल्डर के अंदर रखकर बंडल कर सकते हैं, या आप इसे फ़ायरबेस से गतिशील रूप से डाउनलोड कर सकते हैं।

मॉडल बंडलिंग विकल्प
आपके ऐप में बंडल किया गया
  • मॉडल आपके ऐप के एपीके का हिस्सा है
  • एंड्रॉइड डिवाइस ऑफ़लाइन होने पर भी मॉडल तुरंत उपलब्ध होता है
  • फ़ायरबेस प्रोजेक्ट की कोई आवश्यकता नहीं
फायरबेस के साथ होस्ट किया गया
  • मॉडल को फायरबेस मशीन लर्निंग पर अपलोड करके होस्ट करें
  • एपीके आकार कम कर देता है
  • मॉडल मांग पर डाउनलोड किया जाता है
  • अपने ऐप को दोबारा प्रकाशित किए बिना मॉडल अपडेट पुश करें
  • फायरबेस रिमोट कॉन्फ़िगरेशन के साथ आसान ए/बी परीक्षण
  • फ़ायरबेस प्रोजेक्ट की आवश्यकता है

शुरू करने से पहले

  1. एमएल किट एंड्रॉइड लाइब्रेरी के लिए निर्भरता को अपने मॉड्यूल की ऐप-स्तरीय ग्रेडल फ़ाइल में जोड़ें, जो आमतौर पर app/build.gradle है:

    किसी मॉडल को अपने ऐप के साथ बंडल करने के लिए:

    dependencies {
      // ...
      // Image labeling feature with bundled automl model
      implementation 'com.google.mlkit:image-labeling-custom:16.3.1'
    }
    

    फ़ायरबेस से किसी मॉडल को गतिशील रूप से डाउनलोड करने के लिए, linkFirebase निर्भरता जोड़ें:

    dependencies {
      // ...
      // Image labeling feature with automl model downloaded
      // from firebase
      implementation 'com.google.mlkit:image-labeling-custom:16.3.1'
      implementation 'com.google.mlkit:linkfirebase:16.1.0'
    }
    
  2. यदि आप कोई मॉडल डाउनलोड करना चाहते हैं , तो सुनिश्चित करें कि आपने अपने एंड्रॉइड प्रोजेक्ट में फायरबेस जोड़ा है , यदि आपने पहले से ऐसा नहीं किया है। जब आप मॉडल को बंडल करते हैं तो इसकी आवश्यकता नहीं होती है।

1. मॉडल लोड करें

स्थानीय मॉडल स्रोत कॉन्फ़िगर करें

मॉडल को अपने ऐप के साथ बंडल करने के लिए:

  1. फायरबेस कंसोल से डाउनलोड किए गए ज़िप संग्रह से मॉडल और उसके मेटाडेटा को निकालें। हम अनुशंसा करते हैं कि आप फ़ाइलों को वैसे ही उपयोग करें जैसे आपने उन्हें डाउनलोड किया था, बिना किसी संशोधन (फ़ाइल नाम सहित) के।

  2. अपने ऐप पैकेज में अपना मॉडल और उसकी मेटाडेटा फ़ाइलें शामिल करें:

    1. यदि आपके प्रोजेक्ट में एसेट फ़ोल्डर नहीं है, तो app/ फ़ोल्डर पर राइट-क्लिक करके एक बनाएं, फिर न्यू > फ़ोल्डर > एसेट फ़ोल्डर पर क्लिक करें।
    2. मॉडल फ़ाइलें रखने के लिए संपत्ति फ़ोल्डर के अंतर्गत एक उप-फ़ोल्डर बनाएं।
    3. फ़ाइलों model.tflite , dict.txt , और manifest.json को उप-फ़ोल्डर में कॉपी करें (तीनों फ़ाइलें एक ही फ़ोल्डर में होनी चाहिए)।
  3. यह सुनिश्चित करने के लिए कि ऐप बनाते समय ग्रैडल मॉडल फ़ाइल को संपीड़ित नहीं करता है, अपने ऐप की build.gradle फ़ाइल में निम्नलिखित जोड़ें:

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

    मॉडल फ़ाइल को ऐप पैकेज में शामिल किया जाएगा और कच्ची संपत्ति के रूप में एमएल किट के लिए उपलब्ध होगा।

  4. मॉडल मेनिफेस्ट फ़ाइल का पथ निर्दिष्ट करते हुए LocalModel ऑब्जेक्ट बनाएं:

    जावा

    AutoMLImageLabelerLocalModel localModel =
        new AutoMLImageLabelerLocalModel.Builder()
            .setAssetFilePath("manifest.json")
            // or .setAbsoluteFilePath(absolute file path to manifest file)
            .build();
    

    Kotlin

    val localModel = LocalModel.Builder()
        .setAssetManifestFilePath("manifest.json")
        // or .setAbsoluteManifestFilePath(absolute file path to manifest file)
        .build()
    

फ़ायरबेस-होस्टेड मॉडल स्रोत कॉन्फ़िगर करें

दूरस्थ रूप से होस्ट किए गए मॉडल का उपयोग करने के लिए, एक CustomRemoteModel ऑब्जेक्ट बनाएं, जिसमें मॉडल को प्रकाशित करते समय आपने जो नाम निर्दिष्ट किया था उसे निर्दिष्ट करें:

जावा

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

Kotlin

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

फिर, उन शर्तों को निर्दिष्ट करते हुए मॉडल डाउनलोड कार्य शुरू करें जिनके तहत आप डाउनलोड करने की अनुमति देना चाहते हैं। यदि मॉडल डिवाइस पर नहीं है, या यदि मॉडल का एक नया संस्करण उपलब्ध है, तो कार्य फायरबेस से मॉडल को एसिंक्रोनस रूप से डाउनलोड करेगा:

जावा

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

Kotlin

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

कई ऐप्स अपने इनिशियलाइज़ेशन कोड में डाउनलोड कार्य शुरू करते हैं, लेकिन आप मॉडल का उपयोग करने से पहले किसी भी समय ऐसा कर सकते हैं।

अपने मॉडल से एक छवि लेबलर बनाएं

अपने मॉडल स्रोतों को कॉन्फ़िगर करने के बाद, उनमें से एक से एक ImageLabeler ऑब्जेक्ट बनाएं।

यदि आपके पास केवल स्थानीय रूप से बंडल किया गया मॉडल है, तो बस अपने CustomImageLabelerOptions ऑब्जेक्ट से एक लेबलर बनाएं और उस आत्मविश्वास स्कोर सीमा को कॉन्फ़िगर करें जिसकी आप आवश्यकता चाहते हैं ( अपने मॉडल का मूल्यांकन करें देखें):

जावा

CustomImageLabelerOptions customImageLabelerOptions = new CustomImageLabelerOptions.Builder(localModel)
    .setConfidenceThreshold(0.0f)  // Evaluate your model in the Cloud console
                                   // to determine an appropriate value.
    .build();
ImageLabeler labeler = ImageLabeling.getClient(customImageLabelerOptions);

Kotlin

val customImageLabelerOptions = CustomImageLabelerOptions.Builder(localModel)
    .setConfidenceThreshold(0.0f)  // Evaluate your model in the Cloud console
                                   // to determine an appropriate value.
    .build()
val labeler = ImageLabeling.getClient(customImageLabelerOptions)

यदि आपके पास दूरस्थ रूप से होस्ट किया गया मॉडल है, तो आपको इसे चलाने से पहले यह जांचना होगा कि इसे डाउनलोड किया गया है। आप मॉडल प्रबंधक की isModelDownloaded() विधि का उपयोग करके मॉडल डाउनलोड कार्य की स्थिति की जांच कर सकते हैं।

हालाँकि आपको केवल लेबलर चलाने से पहले इसकी पुष्टि करनी होगी, यदि आपके पास दूरस्थ रूप से होस्ट किया गया मॉडल और स्थानीय रूप से बंडल किया गया मॉडल दोनों हैं, तो छवि लेबलर को इंस्टेंट करते समय यह जांच करना उचित हो सकता है: रिमोट मॉडल से एक लेबलर बनाएं यदि इसे डाउनलोड कर लिया गया है, अन्यथा स्थानीय मॉडल से।

जावा

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener<Boolean>() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
                CustomImageLabelerOptions.Builder optionsBuilder;
                if (isDownloaded) {
                    optionsBuilder = new CustomImageLabelerOptions.Builder(remoteModel);
                } else {
                    optionsBuilder = new CustomImageLabelerOptions.Builder(localModel);
                }
                CustomImageLabelerOptions options = optionsBuilder
                        .setConfidenceThreshold(0.0f)  // Evaluate your model in the Cloud console
                                                       // to determine an appropriate threshold.
                        .build();

                ImageLabeler labeler = ImageLabeling.getClient(options);
            }
        });

Kotlin

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded ->
        val optionsBuilder =
            if (isDownloaded) {
                CustomImageLabelerOptions.Builder(remoteModel)
            } else {
                CustomImageLabelerOptions.Builder(localModel)
            }
        // Evaluate your model in the Cloud console to determine an appropriate threshold.
        val options = optionsBuilder.setConfidenceThreshold(0.0f).build()
        val labeler = ImageLabeling.getClient(options)
}

यदि आपके पास केवल दूरस्थ रूप से होस्ट किया गया मॉडल है, तो आपको मॉडल-संबंधित कार्यक्षमता को अक्षम करना चाहिए - उदाहरण के लिए, अपने यूआई के हिस्से को ग्रे-आउट या छुपाएं - जब तक कि आप पुष्टि न करें कि मॉडल डाउनलोड हो गया है। आप श्रोता को मॉडल प्रबंधक की download() विधि से जोड़कर ऐसा कर सकते हैं:

जावा

RemoteModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @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.
            }
        });

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.
    }

2. इनपुट छवि तैयार करें

फिर, प्रत्येक छवि के लिए जिसे आप लेबल करना चाहते हैं, अपनी छवि से एक InputImage ऑब्जेक्ट बनाएं। जब आप Bitmap उपयोग करते हैं या, यदि आप कैमरा 2 एपीआई, YUV_420_888 media.Image का उपयोग करते हैं, तो छवि लेबलर सबसे तेज़ चलता है, जिसे संभव होने पर अनुशंसित किया जाता है।

आप विभिन्न स्रोतों से एक InputImage बना सकते हैं, प्रत्येक को नीचे समझाया गया है।

media.Image उपयोग करना

media.Image ऑब्जेक्ट से एक InputImage ऑब्जेक्ट बनाने के लिए, जैसे कि जब आप किसी डिवाइस के कैमरे से एक छवि कैप्चर करते हैं, तो media.Image ऑब्जेक्ट और छवि के रोटेशन को InputImage.fromMediaImage() पर पास करें।

यदि आप कैमराएक्स लाइब्रेरी का उपयोग करते हैं, OnImageCapturedListener और ImageAnalysis.Analyzer कक्षाएं आपके लिए रोटेशन मान की गणना करती हैं।

Kotlin+KTX

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) {
        if (imageProxy == null || imageProxy.getImage() == null) {
            return;
        }
        Image mediaImage = imageProxy.getImage();
        InputImage image =
                InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees);
        // Pass image to an ML Kit Vision API
        // ...
    }
}

यदि आप ऐसी कैमरा लाइब्रेरी का उपयोग नहीं करते हैं जो आपको छवि की रोटेशन डिग्री देती है, तो आप डिवाइस की रोटेशन डिग्री और डिवाइस में कैमरा सेंसर के ओरिएंटेशन से इसकी गणना कर सकते हैं:

Kotlin+KTX

private val ORIENTATIONS = SparseIntArray()

init {
    ORIENTATIONS.append(Surface.ROTATION_0, 90)
    ORIENTATIONS.append(Surface.ROTATION_90, 0)
    ORIENTATIONS.append(Surface.ROTATION_180, 270)
    ORIENTATIONS.append(Surface.ROTATION_270, 180)
}
/**
 * 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, context: Context): 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)

    // On most devices, the sensor orientation is 90 degrees, but for some
    // devices it is 270 degrees. For devices with a sensor orientation of
    // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
    val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager
    val sensorOrientation = cameraManager
        .getCameraCharacteristics(cameraId)
        .get(CameraCharacteristics.SENSOR_ORIENTATION)!!
    rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360

    // Return the corresponding FirebaseVisionImageMetadata rotation value.
    val result: Int
    when (rotationCompensation) {
        0 -> result = FirebaseVisionImageMetadata.ROTATION_0
        90 -> result = FirebaseVisionImageMetadata.ROTATION_90
        180 -> result = FirebaseVisionImageMetadata.ROTATION_180
        270 -> result = FirebaseVisionImageMetadata.ROTATION_270
        else -> {
            result = FirebaseVisionImageMetadata.ROTATION_0
            Log.e(TAG, "Bad rotation value: $rotationCompensation")
        }
    }
    return result
}

Java

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

/**
 * 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, Context context)
        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);

    // On most devices, the sensor orientation is 90 degrees, but for some
    // devices it is 270 degrees. For devices with a sensor orientation of
    // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
    CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
    int sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION);
    rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;

    // Return the corresponding FirebaseVisionImageMetadata rotation value.
    int result;
    switch (rotationCompensation) {
        case 0:
            result = FirebaseVisionImageMetadata.ROTATION_0;
            break;
        case 90:
            result = FirebaseVisionImageMetadata.ROTATION_90;
            break;
        case 180:
            result = FirebaseVisionImageMetadata.ROTATION_180;
            break;
        case 270:
            result = FirebaseVisionImageMetadata.ROTATION_270;
            break;
        default:
            result = FirebaseVisionImageMetadata.ROTATION_0;
            Log.e(TAG, "Bad rotation value: " + rotationCompensation);
    }
    return result;
}

फिर, media.Image ऑब्जेक्ट और रोटेशन डिग्री मान को InputImage.fromMediaImage() पर पास करें:

Kotlin+KTX

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

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

फ़ाइल URI का उपयोग करना

फ़ाइल URI से InputImage ऑब्जेक्ट बनाने के लिए, ऐप संदर्भ पास करें और URI को InputImage.fromFilePath() पर फ़ाइल करें। यह तब उपयोगी होता है जब आप उपयोगकर्ता को उनके गैलरी ऐप से एक छवि चुनने के लिए संकेत देने के लिए ACTION_GET_CONTENT इरादे का उपयोग करते हैं।

Kotlin+KTX

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 उपयोग करना

ByteBuffer या ByteArray से एक InputImage ऑब्जेक्ट बनाने के लिए, पहले media.Image इनपुट के लिए पहले वर्णित छवि रोटेशन डिग्री की गणना करें। फिर, छवि की ऊंचाई, चौड़ाई, रंग एन्कोडिंग प्रारूप और रोटेशन डिग्री के साथ बफर या सरणी के साथ InputImage ऑब्जेक्ट बनाएं:

Kotlin+KTX

val image = InputImage.fromByteBuffer(
        byteBuffer,
        /* 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
);

Bitmap उपयोग करना

Bitmap ऑब्जेक्ट से InputImage ऑब्जेक्ट बनाने के लिए, निम्नलिखित घोषणा करें:

Kotlin+KTX

val image = InputImage.fromBitmap(bitmap, 0)

Java

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

छवि को रोटेशन डिग्री के साथ एक Bitmap ऑब्जेक्ट द्वारा दर्शाया गया है।

3. छवि लेबलर चलाएँ

किसी छवि में ऑब्जेक्ट को लेबल करने के लिए, image ऑब्जेक्ट को ImageLabeler की process() विधि में पास करें।

जावा

labeler.process(image)
        .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() {
            @Override
            public void onSuccess(List<ImageLabel> labels) {
                // Task completed successfully
                // ...
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Task failed with an exception
                // ...
            }
        });

Kotlin

labeler.process(image)
        .addOnSuccessListener { labels ->
            // Task completed successfully
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

4. लेबल वाली वस्तुओं के बारे में जानकारी प्राप्त करें

यदि छवि लेबलिंग ऑपरेशन सफल होता है, तो ImageLabel ऑब्जेक्ट की एक सूची सफल श्रोता को भेज दी जाती है। प्रत्येक ImageLabel ऑब्जेक्ट उस चीज़ का प्रतिनिधित्व करता है जिसे छवि में लेबल किया गया था। आप प्रत्येक लेबल का टेक्स्ट विवरण, मैच का आत्मविश्वास स्कोर और मैच का सूचकांक प्राप्त कर सकते हैं। उदाहरण के लिए:

जावा

for (ImageLabel label : labels) {
    String text = label.getText();
    float confidence = label.getConfidence();
    int index = label.getIndex();
}

Kotlin

for (label in labels) {
    val text = label.text
    val confidence = label.confidence
    val index = label.index
}

वास्तविक समय के प्रदर्शन को बेहतर बनाने के लिए युक्तियाँ

यदि आप वास्तविक समय एप्लिकेशन में छवियों को लेबल करना चाहते हैं, तो सर्वोत्तम फ्रैमरेट्स प्राप्त करने के लिए इन दिशानिर्देशों का पालन करें:

  • छवि लेबलर को थ्रॉटल कॉल। यदि छवि लेबलर चलने के दौरान कोई नया वीडियो फ़्रेम उपलब्ध हो जाता है, तो फ़्रेम को छोड़ दें। उदाहरण के लिए क्विकस्टार्ट सैंपल ऐप में VisionProcessorBase क्लास देखें।
  • यदि आप इनपुट छवि पर ग्राफ़िक्स को ओवरले करने के लिए छवि लेबलर के आउटपुट का उपयोग कर रहे हैं, तो पहले परिणाम प्राप्त करें, फिर छवि को प्रस्तुत करें और एक ही चरण में ओवरले करें। ऐसा करने से, आप प्रत्येक इनपुट फ़्रेम के लिए केवल एक बार डिस्प्ले सतह पर रेंडर करते हैं। उदाहरण के लिए क्विकस्टार्ट नमूना ऐप में CameraSourcePreview और GraphicOverlay कक्षाएं देखें।
  • यदि आप कैमरा2 एपीआई का उपयोग करते हैं, तो ImageFormat.YUV_420_888 प्रारूप में छवियां कैप्चर करें।

    यदि आप पुराने कैमरा एपीआई का उपयोग करते हैं, तो ImageFormat.NV21 प्रारूप में छवियां कैप्चर करें।