ML Kit'e bir resim ilettiğinizde, resimdeki her nesnenin konumuyla birlikte resimde en fazla beş nesne algılar. Video akışlarındaki nesneler algılanırken her nesnenin, nesneyi kareden kareye izlemek için kullanabileceğiniz benzersiz bir kimliği olur.
Algılanan nesneleri sınıflandırmak için özel bir görüntü sınıflandırma modeli kullanabilirsiniz. Model uyumluluğu gereksinimleri, önceden eğitilmiş modellerin nerede bulunacağı ve kendi modellerinizi nasıl eğiteceğinizle ilgili yönergeler için ML Kit ile özel modeller başlıklı makaleyi inceleyin.
Özel bir modeli entegre etmenin iki yolu vardır. Modeli uygulamanızın öğe klasörüne yerleştirerek paketleyebilir veya Cloud Storage'dan dinamik olarak indirebilirsiniz. Aşağıdaki tabloda iki seçenek karşılaştırılmaktadır.
| Paketlenmiş Model | Barındırılan Model |
|---|---|
| Model, uygulamanızın APK'sının bir parçasıdır ve bu da uygulamanızın boyutunu artırır. | Model, APK'nızın bir parçası değildir. Cloud Storage'a yüklenerek barındırılır. Firebase için Cloud Storage'ı kullanmanızı öneririz. |
| Model, Android cihaz çevrimdışı olsa bile hemen kullanılabilir. | Uygulamanız, modeli isteğe bağlı olarak indirmek için kod içermelidir. |
| Firebase projesi gerekmez | Firebase projesi gerektirir (Cloud Storage for Firebase kullanılıyorsa). |
| Modeli güncellemek için uygulamanızı yeniden yayınlamanız gerekir. | Uygulamanızı yeniden yayınlamadan model güncellemelerini gönderme |
| Yerleşik A/B testi yok | Firebase Remote Config ile A/B testi |
Deneyin
- Paketlenmiş modelin kullanımına ilişkin örnek için Vision hızlı başlangıç uygulamasına, barındırılan modelin kullanımına ilişkin örnek için AutoML hızlı başlangıç uygulamasına bakın.
- Bu API'nin uçtan uca uygulanması için Materyal Tasarım tanıtım uygulamasını inceleyin.
Başlamadan önce
1. Proje düzeyindekibuild.gradle.kts dosyanızda, Google'ın Maven deposunu hem buildscript hem de allprojects bölümlerine eklediğinizden emin olun.
ML Kit Android kitaplıklarının bağımlılıklarını modülünüzün uygulama düzeyindeki Gradle dosyasına (genellikle
app/build.gradle.kts) ekleyin:Bir modeli uygulamanızla paketlemek için:
dependencies { // ... // Object detection & tracking feature with custom bundled model implementation("com.google.mlkit:object-detection-custom:17.0.2") }Cloud Storage for Firebase'den bir model indirmek istiyorsanız henüz yapmadıysanız Firebase'i Android projenize eklediğinizden emin olun. Bu işlem, modeli paketlediğinizde gerekli değildir.
1. Modeli yükleme
Modeli yerel olarak paketlenmiş bir kaynaktan veya uzaktan barındırılan bir kaynaktan yükleyebilirsiniz.
Yerel model kaynağı yapılandırma
Modeli uygulamanızla paketlemek için:
Model dosyasını (genellikle
.tfliteveya.liteile biter) uygulamanızınassets/klasörüne kopyalayın. (Önceapp/klasörünü sağ tıklayıp Yeni > Klasör > Öğeler Klasörü'nü tıklayarak klasörü oluşturmanız gerekebilir.)Model dosyasının yolunu belirterek bir
LocalModelnesnesi oluşturun:Kotlin
val localModel = LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute path to model file) // or .setUri(URI to model file) .build()
Java
LocalModel localModel = new LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute path to model file) // or .setUri(URI to model file) .build();
Uzaktan barındırılan model kaynağını yapılandırma
Uzaktan barındırılan modeli kullanmak için model dosyasını kendi uygulama mantığınızı kullanarak cihazın yerel depolama alanına indirmeniz ve ardından yerel model olarak yüklemeniz gerekir. Bir modeli barındırmak için Firebase için Cloud Storage'ı kullanmanızı öneririz. Uygulama ayrıntıları için Firebase ML'den Cloud Storage'a taşıma kılavuzuna bakın.
2. Nesne algılayıcıyı yapılandırma
Model kaynaklarınızı yapılandırdıktan sonra, CustomObjectDetectorOptions nesnesiyle kullanım alanınız için nesne dedektörünü yapılandırın. Aşağıdaki ayarları değiştirebilirsiniz:
| Nesne Algılayıcı Ayarları | |
|---|---|
| Algılama modu |
STREAM_MODE (varsayılan) | SINGLE_IMAGE_MODE
|
| Birden fazla nesneyi algılama ve izleme |
false (varsayılan) | true
En fazla beş nesnenin mi yoksa yalnızca en belirgin nesnenin mi (varsayılan) algılanıp izleneceği. |
| Nesneleri sınıflandırma |
false (varsayılan) | true
Algılanan nesnelerin, sağlanan özel sınıflandırıcı modeli kullanılarak sınıflandırılıp sınıflandırılmayacağı. Özel sınıflandırma modelinizi kullanmak için bu değeri |
| Sınıflandırma güven eşiği |
Algılanan etiketlerin minimum güven puanı. Ayarlanmazsa modelin meta verileri tarafından belirtilen sınıflandırıcı eşiği kullanılır. Modelde herhangi bir meta veri yoksa veya meta verilerde bir sınıflandırıcı eşiği belirtilmemişse varsayılan eşik olarak 0, 0 kullanılır. |
| Nesne başına maksimum etiket sayısı |
Algılayıcının döndüreceği nesne başına maksimum etiket sayısı. Ayarlanmazsa varsayılan değer olan 10 kullanılır. |
Nesne algılama ve izleme API'si, aşağıdaki iki temel kullanım alanı için optimize edilmiştir:
- Kamera vizöründeki en belirgin nesnenin canlı olarak algılanması ve izlenmesi.
- Statik bir görüntüdeki birden fazla nesnenin algılanması.
Bu kullanım alanları için API'yi yerel olarak paketlenmiş bir modelle yapılandırmak üzere:
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);
Uzaktan barındırılan bir modeliniz varsa çalıştırmadan önce indirildiğinden emin olmanız gerekir.
Bu işlemi yalnızca algılayıcıyı çalıştırmadan önce onaylamanız gerekse de hem uzaktan barındırılan hem de yerel olarak paketlenmiş bir modeliniz varsa görüntü algılayıcıyı oluştururken bu kontrolü yapmanız mantıklı olabilir: Uzak model indirilmişse bu modelden, aksi takdirde yerel modelden bir algılayıcı oluşturun.
Kotlin
val modelFile = File(context.cacheDir, "my_remote_model.tflite") val model = if (modelFile.exists()) { // Use the downloaded model if available LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build() } else { // Fall back to the bundled model LocalModel.Builder().setAssetFilePath("model.tflite").build() } val customObjectDetectorOptions = CustomObjectDetectorOptions.Builder(model) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build() val objectDetector = ObjectDetection.getClient(customObjectDetectorOptions)
Java
File modelFile = new File(context.getCacheDir(), "my_remote_model.tflite"); LocalModel model; if (modelFile.exists()) { // Use the downloaded model if available model = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build(); } else { // Fall back to the bundled model model = new LocalModel.Builder().setAssetFilePath("model.tflite").build(); } CustomObjectDetectorOptions customObjectDetectorOptions = new CustomObjectDetectorOptions.Builder(model) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build(); ObjectDetector objectDetector = ObjectDetection.getClient(customObjectDetectorOptions);
Yalnızca uzaktan barındırılan bir modeliniz varsa modelin indirildiğini onaylayana kadar modelle ilgili işlevleri devre dışı bırakmanız (ör. kullanıcı arayüzünüzün bir bölümünü grileştirmeniz veya gizlemeniz) gerekir.
Kotlin
val localFile = File(context.cacheDir, "my_remote_model.tflite") if (localFile.exists()) { // Model is already cached, initialize immediately initializeDetector(localFile) } else { // Model is not yet available, show loading UI and start download showLoadingUI() val storage = Firebase.storage val modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite") modelRef.getFile(localFile) .addOnSuccessListener { // Download complete, initialize the detector hideLoadingUI() initializeDetector(localFile) } .addOnFailureListener { // Handle download error showErrorUI() } } private fun initializeDetector(modelFile: File) { val localModel = LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build() val customObjectDetectorOptions = CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .build() val objectDetector = ObjectDetection.getClient(customObjectDetectorOptions) // Enable ML-related UI features here enableMLFeatures(objectDetector) }
Java
File localFile = new File(context.getCacheDir(), "my_remote_model.tflite"); if (localFile.exists()) { // Model is already cached, initialize immediately initializeDetector(localFile); } else { // Model is not yet available, show loading UI and start download showLoadingUI(); FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite"); modelRef.getFile(localFile) .addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { // Download complete, initialize the detector hideLoadingUI(); initializeDetector(localFile); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { // Handle download error showErrorUI(); } }); } private void initializeDetector(File modelFile) { LocalModel localModel = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build(); CustomObjectDetectorOptions customObjectDetectorOptions = new CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .build(); ObjectDetector objectDetector = ObjectDetection.getClient(customObjectDetectorOptions); // Enable ML-related UI features here enableMLFeatures(objectDetector); }
3. Giriş görüntüsünü hazırlama
ResminizdenInputImage nesnesi oluşturun.
Nesne dedektörü doğrudan bir Bitmap, NV21 ByteBuffer veya YUV_420_888 media.Image'den çalışır. Bu kaynaklardan birine doğrudan erişiminiz varsa bu kaynaklardan InputImage oluşturmanız önerilir. Diğer kaynaklardan bir InputImage oluşturursanız dönüşümü sizin için dahili olarak gerçekleştiririz ve bu işlem daha az verimli olabilir.
Farklı kaynaklardan InputImage nesneler oluşturabilirsiniz. Her biri aşağıda açıklanmıştır.
media.Image kullanma
media.Image nesnesinden InputImage nesnesi oluşturmak için (ör. bir cihazın kamerasından resim yakaladığınızda) media.Image nesnesini ve resmin dönüşünü InputImage.fromMediaImage()'e iletin.
CameraX kitaplığını kullanıyorsanız OnImageCapturedListener ve
ImageAnalysis.Analyzer sınıfları, sizin için döndürme değerini hesaplar.
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 // ... } } }
Resmin dönüş derecesini veren bir kamera kitaplığı kullanmıyorsanız bunu cihazın dönüş derecesinden ve cihazdaki kamera sensörünün yönünden hesaplayabilirsiniz:
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; }
Ardından, media.Image nesnesini ve dönüş derecesi değerini InputImage.fromMediaImage()'e iletin:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Dosya URI'si kullanma
Dosya URI'sinden InputImage nesnesi oluşturmak için uygulama bağlamını ve dosya URI'sini InputImage.fromFilePath()'ye iletin. Bu, kullanıcıdan galeri uygulamasından bir resim seçmesini istemek için ACTION_GET_CONTENT amacını kullandığınızda yararlıdır.
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 veya ByteArray kullanma
ByteBuffer veya ByteArray öğesinden InputImage
nesnesi oluşturmak için öncelikle media.Image girişi için daha önce açıklandığı gibi görüntü döndürme derecesini hesaplayın.
Ardından, arabellek veya diziyle birlikte resmin yüksekliği, genişliği, renk kodlama biçimi ve döndürme derecesiyle InputImage nesnesini oluşturun:
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 kullanma
Bitmap nesnesinden InputImage
nesnesi oluşturmak için aşağıdaki bildirimi yapın:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Resim, döndürme dereceleriyle birlikte bir Bitmap nesnesiyle gösterilir.
4. Nesne algılayıcıyı çalıştırma
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. Etiketli nesneler hakkında bilgi alma
process() çağrısı başarılı olursa DetectedObject listesi başarı dinleyicisine iletilir.
Her DetectedObject aşağıdaki özellikleri içerir:
| Sınırlayıcı kutu | Nesnenin resimdeki konumunu belirten bir Rect. |
||||||
| İzleme Kimliği | Nesneyi resimler arasında tanımlayan bir tam sayı. Null in SINGLE_IMAGE_MODE. | ||||||
| Etiketler |
|
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(); } }
Mükemmel bir kullanıcı deneyimi sağlama
En iyi kullanıcı deneyimi için uygulamanızda aşağıdaki yönergeleri uygulayın:
- Nesne algılamanın başarılı olması, nesnenin görsel karmaşıklığına bağlıdır. Az sayıda görsel özelliğe sahip nesnelerin algılanabilmesi için görüntünün daha büyük bir bölümünü kaplaması gerekebilir. Kullanıcılara, algılamak istediğiniz nesne türleriyle iyi çalışan girişler yakalama konusunda rehberlik etmeniz gerekir.
- Sınıflandırma kullandığınızda, desteklenen kategorilere net bir şekilde girmeyen nesneleri algılamak istiyorsanız bilinmeyen nesneler için özel işlem uygulayın.
Ayrıca, ML Kit Materyal Tasarım vitrin uygulamasını ve Materyal Tasarım makine öğrenimi destekli özellikler için kalıplar koleksiyonunu da inceleyin.
Performansı artırma
Nesne tespit etmeyi gerçek zamanlı bir uygulamada kullanmak istiyorsanız en iyi kare hızlarını elde etmek için aşağıdaki yönergeleri uygulayın:Gerçek zamanlı bir uygulamada akış modunu kullanırken çoğu cihaz yeterli kare hızını sağlayamayacağından birden fazla nesne tespit etme kullanmayın.
Cameraveyacamera2API'sini kullanıyorsanız algılayıcıya yapılan çağrıları kısıtlayın. Dedektör çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın. Örnek için hızlı başlangıç örnek uygulamasındakiVisionProcessorBasesınıfına bakın.CameraXAPI'sini kullanıyorsanız geri basınç stratejisinin varsayılan değerine ayarlandığından emin olunImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. Bu, analiz için tek seferde yalnızca bir resmin gönderilmesini sağlar. Analizör meşgulken daha fazla resim üretilirse bunlar otomatik olarak bırakılır ve teslimat için sıraya alınmaz. Analiz edilen görüntü ImageProxy.close() çağrılarak kapatıldığında, en son görüntü teslim edilir.- Grafikleri giriş resmine yerleştirmek için algılayıcının çıkışını kullanıyorsanız önce ML Kit'ten sonucu alın, ardından resmi tek bir adımda oluşturup yerleştirin. Bu işlem, her giriş karesi için yalnızca bir kez görüntüleme yüzeyinde oluşturulur. Bir örnek için hızlı başlangıç örnek uygulamasındaki
CameraSourcePreviewveGraphicOverlaysınıflarına bakın. - Camera2 API'yi kullanıyorsanız görüntüleri
ImageFormat.YUV_420_888biçiminde çekin. Eski Camera API'yi kullanıyorsanız görüntüleriImageFormat.NV21biçiminde çekin.