Android'de ML Kit ile resimlerdeki metinleri tanıyın

Koleksiyonlar ile düzeninizi koruyun İçeriği tercihlerinize göre kaydedin ve kategorilere ayırın.

Resimler veya videolardaki bir sokak işareti metni gibi metinleri tanımak için ML Kit'i kullanabilirsiniz. Bu özelliğin temel özellikleri şunlardır:

Metin Tanıma API'sı
AçıklamaResimlerde veya videolarda Latin alfabesini tanıyın.
Kitaplık adıcom.google.android.gms:play-services-mlkit-text-recognition
UygulamaKitaplık, Google Play Hizmetleri aracılığıyla dinamik olarak indirilir.
Uygulama boyutu etkisi260KB
İlk kullanıma hazırlama süresiİlk kullanımda kitaplığın indirilmesini beklemeniz gerekebilir.
PerformansÇoğu cihazda gerçek zamanlı.

Metin tanıma API'si, indirilmesi gereken bir paketsiz kitaplık kullanır. Bu yüklemeyi, uygulama yüklendiğinde veya ilk kez başlatıldığında ya da Google Play Hizmetleri ModuleInstallClient API üzerinden yapabilirsiniz. Çoğu durumda diğer Android uygulamaları da bu adımı zaten uygulamış olabilir. Bu durumda API hemen kullanılabilir.

Başlamadan önce

  1. Proje düzeyindeki build.gradle dosyanıza Google\u39; Maven deposunu hem buildscript hem de allprojects bölümlerinize eklediğinizden emin olun.
  2. ML Kit Android kitaplıklarının bağımlılıklarını modülünüze genellikle app/build.gradle olan uygulama düzeyindeki Gradle dosyanıza ekleyin:
    dependencies {
      // ...
    
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition:18.0.2'
    }
    
  3. İsteğe bağlı ancak önerilir: Uygulamanızı, Play Store'dan yüklendikten sonra makine öğrenimi modelini cihaza otomatik olarak indirecek şekilde yapılandırabilirsiniz. Bunu yapmak için uygulamanızın AndroidManifest.xml dosyasına aşağıdaki beyanı ekleyin:

    <application ...>
      ...
      <meta-data
          android:name="com.google.mlkit.vision.DEPENDENCIES"
          android:value="ocr" />
      <!-- To use multiple models: android:value="ocr,model2,model3" -->
    </application>
    
    Yükleme sırasında model indirme özelliğini etkinleştirmezseniz cihaz üzerinde algılayıcıyı ilk kez çalıştırdığınızda model indirilir. İndirme işlemi tamamlanmadan önce yaptığınız istekler sonuç vermez.

1. TextRecognizer örneği oluşturma

Bir TextRecognizer örneği oluşturun:

Kotlin

val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)

Java

TextRecognizer recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS);

2. Giriş resmini hazırlama

Bir görüntüdeki metni tanımak için Bitmap, media.Image, ByteBuffer, bayt dizisi veya cihazdaki bir dosyadan InputImage nesnesi oluşturun. Ardından, InputImage nesnesini TextRecognizer adlı processImage yöntemine geçirin.

Farklı kaynaklardan bir InputImage nesnesi oluşturabilirsiniz. Bu nesnelerin her biri aşağıda açıklanmıştır.

media.Image kullanma

Örneğin, bir cihazın kamerasından resim yakaladığınızda media.Image nesnesinden InputImage nesnesi oluşturmak için media.Image nesnesini ve resmin dönüşünü InputImage.fromMediaImage() uygulamasına geçirin.

CameraX kitaplığını kullanırsanız OnImageCapturedListener ve ImageAnalysis.Analyzer sınıfları döndürme değerini sizin için 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 fotoğraf makinesi kitaplığı kullanmıyorsanız cihazın dönüş derecesini ve cihazdaki kamera sensörünün yönünü hesaplayarak bunu 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öndürme derecesi değerini InputImage.fromMediaImage() öğesine 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() öğesine iletin. Bu, kullanıcıdan galeri uygulamasından bir resim seçmesini istemek için ACTION_GET_CONTENT amacı kullandığınızda faydalı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 bir InputImage nesnesi oluşturmak için önce resim döndürme derecesini media.Image girişi için daha önce açıklandığı gibi hesaplayın. Ardından, arabelleği veya dizisi olan ve resim yüksekliği, genişliği, renk kodlama biçimi ve döndürme derecesiyle birlikte 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 bir InputImage nesnesi oluşturmak için aşağıdaki beyanı oluşturun:

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

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

Resim, dönüş dereceleriyle birlikte bir Bitmap nesnesiyle temsil edilir.

3. Resmi işleyin

Resmi process yöntemine geçirin:

Kotlin

val result = recognizer.process(image)
        .addOnSuccessListener { visionText ->
            // Task completed successfully
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

Java

Task<Text> result =
        recognizer.process(image)
                .addOnSuccessListener(new OnSuccessListener<Text>() {
                    @Override
                    public void onSuccess(Text visionText) {
                        // Task completed successfully
                        // ...
                    }
                })
                .addOnFailureListener(
                        new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                // Task failed with an exception
                                // ...
                            }
                        });

4. Tanınan metin bloklarından metin çıkarma

Metin tanıma işlemi başarılı olursa başarılı dinleyiciye bir Text nesnesi iletilir. Text nesnesi, resimde tanınan metnin tamamını, sıfır veya daha fazla TextBlock nesnesini içerir.

Her bir TextBlock, sıfır veya daha fazla Line nesne içeren dikdörtgen metin blokunu temsil eder. Her bir Line nesnesi, sıfır veya daha fazla Element nesnesi içeren bir metin satırını temsil eder. Her bir Element nesnesi, bir veya daha fazla Symbol nesnesi içeren, kelime benzeri bir varlığı temsil eder. Her Symbol nesnesi bir karakteri, basamağı veya kelime benzeri varlığı temsil eder.

Her bir TextBlock, Line, Element ve Symbol nesnesi için bölgede tanınan metni, bölgenin sınır koordinatlarını ve rotasyon bilgileri, güven puanı gibi diğer birçok özelliği görebilirsiniz.

Örnek:

Kotlin

val resultText = result.text
for (block in result.textBlocks) {
    val blockText = block.text
    val blockCornerPoints = block.cornerPoints
    val blockFrame = block.boundingBox
    for (line in block.lines) {
        val lineText = line.text
        val lineCornerPoints = line.cornerPoints
        val lineFrame = line.boundingBox
        for (element in line.elements) {
            val elementText = element.text
            val elementCornerPoints = element.cornerPoints
            val elementFrame = element.boundingBox
        }
    }
}

Java

String resultText = result.getText();
for (Text.TextBlock block : result.getTextBlocks()) {
    String blockText = block.getText();
    Point[] blockCornerPoints = block.getCornerPoints();
    Rect blockFrame = block.getBoundingBox();
    for (Text.Line line : block.getLines()) {
        String lineText = line.getText();
        Point[] lineCornerPoints = line.getCornerPoints();
        Rect lineFrame = line.getBoundingBox();
        for (Text.Element element : line.getElements()) {
            String elementText = element.getText();
            Point[] elementCornerPoints = element.getCornerPoints();
            Rect elementFrame = element.getBoundingBox();
            for (Text.Symbol symbol : element.getSymbols()) {
                String symbolText = symbol.getText();
                Point[] symbolCornerPoints = symbol.getCornerPoints();
                Rect symbolFrame = symbol.getBoundingBox();
            }
        }
    }
}

Giriş resmi yönergeleri

  • ML Kit'in metni doğru şekilde tanıması için giriş görüntüleri, yeterli piksel verileriyle temsil edilen metin içermelidir. Her bir karakterin en az 16x16 piksel olması önerilir. Karakterlerin genellikle 24x24 pikselden büyük olmasında doğruluk avantajı yoktur.

    Bu nedenle, 640x480 boyutlu bir resim, resmin tam genişliğinde kaplayan bir kartvizit taramak için iyi bir şekilde çalışabilir. Harf boyutunda kağıt üzerine basılı bir dokümanı taramak için 720x1280 piksel boyutunda bir resim gerekebilir.

  • Düşük resim odağı, metin tanıma doğruluğunu etkileyebilir. Kabul edilebilir sonuçlar elde edemiyorsanız kullanıcıdan resmi yeniden yakalamasını isteyin.

  • Gerçek zamanlı bir uygulamadaki metni tanıyorsanız giriş resimlerinin genel boyutlarını dikkate almanız gerekir. Küçük resimler daha hızlı işlenebilir. Gecikmeyi azaltmak için metnin resmin mümkün olduğunca fazla yer kaplamasına dikkat edin ve resimleri daha düşük çözünürlüklerde yakalayın (yukarıda belirtilen doğruluk gereksinimlerini göz önünde bulundurarak). Daha fazla bilgi için Performansı artırmaya yönelik ipuçları bölümünü inceleyin.

Performansı artırmaya yönelik ipuçları

  • Camera veya camera2 API'yi kullanıyorsanız algılayıcıya yapılan çağrıları daraltabilirsiniz. Algılayıcı çalışırken yeni bir video çerçevesi varsa çerçeveyi bırakın. Örnek için hızlı başlangıç örnek uygulamasındaki VisionProcessorBase sınıfını inceleyin.
  • CameraX API'yi kullanıyorsanız, basınç stratejisinin varsayılan değerine (ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) ayarlandığından emin olun. Bu, aynı anda yalnızca bir resmin analiz için yayınlanacağını garanti eder. Analiz aracı meşgul olduğunda daha fazla görüntü üretilirse otomatik olarak çıkarılır ve teslimat için sıraya alınmaz. Analiz edilen resim, ImageProxy.close() öğesi çağrılarak kapatıldıktan sonra, sonraki en son görüntü yayınlanır.
  • Algılayıcının çıkışını giriş resmine bindirmek için kullanırsanız önce ML Kit'ten sonucu alın, ardından resmi ve yer paylaşımını tek adımda oluşturun. Bu işlem, her bir giriş çerçevesi için görüntüleme yüzeyini yalnızca bir kez oluşturur. Örnek için hızlı başlangıç örnek uygulamasındaki CameraSourcePreview ve GraphicOverlay sınıflarını inceleyin.
  • Camera2 API'yi kullanıyorsanız görüntüleri ImageFormat.YUV_420_888 biçiminde çekin. Eski Kamera API'sini kullanıyorsanız görüntüleri ImageFormat.NV21 biçiminde çekin.
  • Daha düşük çözünürlükte görüntüler yakalamayı düşünebilirsiniz. Ancak bu API'nin görüntü boyutu şartlarını da göz önünde bulundurun.