Mengenali teks dalam gambar dengan ML Kit di Android

Anda dapat menggunakan ML Kit untuk mengenali teks dalam gambar atau video, seperti teks rambu jalan. Karakteristik utama dari fitur ini adalah:

Fitur Tidak Dipaketkan Paket
Nama perpustakaan com.google.android.gms:play-services-mlkit-text-recognition

com.google.android.gms:play-services-mlkit-text-recognition-chinese

com.google.android.gms:play-services-mlkit-text-recognition-devanagari

com.google.android.gms:play-services-mlkit-text-recognition-japanese

com.google.android.gms:play-services-mlkit-text-recognition-korean

com.google.mlkit:text-recognition

com.google.mlkit:text-recognition-chinese

com.google.mlkit:text-recognition-devanagari

com.google.mlkit:text-recognition-japanese

com.google.mlkit:text-recognition-korean

Penerapan Model didownload secara dinamis melalui Layanan Google Play. Model ditautkan secara statis ke aplikasi Anda pada waktu build.
Ukuran aplikasi Peningkatan ukuran sekitar 260 KB per arsitektur skrip. Peningkatan ukuran sekitar 4 MB per skrip per arsitektur.
Waktu inisialisasi Mungkin harus menunggu model didownload sebelum digunakan pertama kali. Model akan segera tersedia.
Performa Secara real-time di sebagian besar perangkat untuk library skrip Latin, lebih lambat untuk yang lain. Secara real-time di sebagian besar perangkat untuk library skrip Latin, lebih lambat untuk yang lain.

Cobalah

Sebelum memulai

  1. Dalam file build.gradle level project, pastikan Anda menyertakan repositori Maven Google di bagian buildscript dan allprojects.
  2. Tambahkan dependensi untuk library Android ML Kit ke file gradle level aplikasi modul Anda, biasanya app/build.gradle:

    Untuk memaketkan model dengan aplikasi Anda:

    dependencies {
      // To recognize Latin script
      implementation 'com.google.mlkit:text-recognition:16.0.0'
    
      // To recognize Chinese script
      implementation 'com.google.mlkit:text-recognition-chinese:16.0.0'
    
      // To recognize Devanagari script
      implementation 'com.google.mlkit:text-recognition-devanagari:16.0.0'
    
      // To recognize Japanese script
      implementation 'com.google.mlkit:text-recognition-japanese:16.0.0'
    
      // To recognize Korean script
      implementation 'com.google.mlkit:text-recognition-korean:16.0.0'
    }
    

    Untuk menggunakan model di Layanan Google Play:

    dependencies {
      // To recognize Latin script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition:19.0.0'
    
      // To recognize Chinese script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-chinese:16.0.0'
    
      // To recognize Devanagari script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-devanagari:16.0.0'
    
      // To recognize Japanese script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-japanese:16.0.0'
    
      // To recognize Korean script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-korean:16.0.0'
    }
    
  3. Jika memilih untuk menggunakan model ini di Layanan Google Play, Anda dapat mengonfigurasi aplikasi untuk mengunduh model secara otomatis ke perangkat setelah aplikasi Anda diinstal dari Play Store. Untuk melakukannya, tambahkan ke file AndroidManifest.xml aplikasi Anda:

    <application ...>
          ...
          <meta-data
              android:name="com.google.mlkit.vision.DEPENDENCIES"
              android:value="ocr" >
          <!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." -->
    </application>
    

    Anda juga dapat memeriksa ketersediaan model dan meminta download secara eksplisit melalui layanan Google Play ModuleInstallClient API. Jika Anda tidak mengaktifkan model waktu penginstalan mengunduh atau meminta unduhan eksplisit, model diunduh pertama kali saat Anda menjalankan pemindai. Permintaan yang Anda buat sebelum download selesai tidak akan memberikan hasil.

1. Membuat instance TextRecognizer

Membuat instance TextRecognizer, dengan meneruskan opsi yang terkait dengan library yang Anda deklarasikan dependensinya di atas:

Kotlin

// When using Latin script library
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)

// When using Chinese script library
val recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())

// When using Devanagari script library
val recognizer = TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build())

// When using Japanese script library
val recognizer = TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build())

// When using Korean script library
val recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())

Java

// When using Latin script library
TextRecognizer recognizer =
  TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS);

// When using Chinese script library
TextRecognizer recognizer =
  TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build());

// When using Devanagari script library
TextRecognizer recognizer =
  TextRecognition.getClient(new DevanagariTextRecognizerOptions.Builder().build());

// When using Japanese script library
TextRecognizer recognizer =
  TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build());

// When using Korean script library
TextRecognizer recognizer =
  TextRecognition.getClient(new KoreanTextRecognizerOptions.Builder().build());

2. Menyiapkan gambar input

Untuk mengenali teks dalam gambar, buat objek InputImage dari baik Bitmap, media.Image, ByteBuffer, array byte, atau file pada perangkat seluler. Lalu, teruskan objek InputImage ke Metode processImage TextRecognizer.

Anda dapat membuat InputImage dari berbagai sumber, masing-masing akan dijelaskan di bawah ini.

Menggunakan media.Image

Untuk membuat InputImage dari objek media.Image, seperti saat Anda mengambil gambar dari kamera perangkat, teruskan objek media.Image dan objek rotasi ke InputImage.fromMediaImage().

Jika Anda menggunakan Library CameraX, OnImageCapturedListener dan Class ImageAnalysis.Analyzer menghitung nilai rotasi keamanan untuk Anda.

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

Jika Anda tidak menggunakan pustaka kamera yang memberi derajat rotasi gambar, Anda bisa menghitungnya dari derajat rotasi perangkat dan orientasi kamera sensor di perangkat:

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

Lalu, teruskan objek media.Image dan nilai derajat rotasi ke InputImage.fromMediaImage():

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

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

Menggunakan URI file

Untuk membuat InputImage dari URI file, teruskan konteks aplikasi dan URI file ke InputImage.fromFilePath(). Hal ini berguna ketika Anda gunakan intent ACTION_GET_CONTENT untuk meminta pengguna memilih gambar dari aplikasi galeri mereka.

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();
}

Menggunakan ByteBuffer atau ByteArray

Untuk membuat InputImage dari ByteBuffer atau ByteArray, hitung gambar terlebih dahulu derajat rotasi seperti yang dijelaskan sebelumnya untuk input media.Image. Lalu, buat objek InputImage dengan buffer atau array, beserta elemen tinggi, lebar, format encoding warna, dan derajat rotasi:

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
);

Menggunakan Bitmap

Untuk membuat InputImage dari objek Bitmap, buat deklarasi berikut:

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

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

Gambar direpresentasikan oleh objek Bitmap bersama dengan derajat rotasi.

3. Memproses gambar

Teruskan gambar ke metode process:

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. Ekstrak teks dari blok teks yang dikenali

Jika operasi pengenalan teks berhasil, objek Text akan diteruskan ke pemroses yang berhasil. Objek Text berisi teks lengkap yang dikenali di gambar dan nol atau beberapa objek TextBlock.

Setiap TextBlock mewakili blok teks persegi panjang, yang berisi nol atau beberapa objek Line. Masing-masing Objek Line mewakili baris teks, yang berisi nol objek Element atau lebih. Setiap Element objek mewakili kata atau entitas seperti kata, yang berisi nol atau lebih Objek Symbol. Setiap Symbol mewakili karakter, digit, atau entitas seperti kata.

Untuk setiap TextBlock, Line, Element dan Symbol objek, Anda bisa mendapatkan teks yang dikenali di area, koordinat pembatas wilayah dan banyak atribut lain seperti informasi rotasi, skor keyakinan dll.

Contoh:

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();
            }
        }
    }
}

Panduan gambar input

  • Agar ML Kit dapat mengenali teks secara akurat, gambar input harus berisi teks yang diwakili oleh data piksel yang memadai. Idealnya, setiap karakter minimal harus 16x16 piksel. Umumnya tidak ada akurasi keuntungan akurasi untuk karakter yang menjadi lebih besar dari 24x24 {i>pixel<i}.

    Jadi, misalnya, gambar 640x480 mungkin cocok untuk memindai kartu nama yang menempati lebar penuh gambar. Untuk memindai dokumen yang dicetak pada kertas berukuran huruf, gambar 720x1280 piksel mungkin diperlukan.

  • Fokus gambar yang buruk dapat memengaruhi akurasi pengenalan teks. Jika Anda tidak mendapatkan hasil yang dapat diterima, coba minta pengguna untuk mengambil ulang gambar.

  • Jika Anda mengenali teks dalam aplikasi {i>real-time<i}, Anda harus mempertimbangkan dimensi keseluruhan gambar input. Lebih kecil gambar dapat diproses lebih cepat. Untuk mengurangi latensi, pastikan teks menempati sebanyak gambar sebaik mungkin, dan ambil gambar dengan resolusi yang lebih rendah (mengingat akurasi persyaratan yang disebutkan di atas). Untuk informasi selengkapnya, lihat Tips untuk meningkatkan performa.

Tips untuk meningkatkan performa

  • Jika Anda menggunakan Camera atau camera2 API, men-throttle panggilan ke detektor. Jika video baru {i>frame<i} menjadi tersedia saat detektor sedang berjalan, hapus {i>frame<i} tersebut. Lihat VisionProcessorBase dalam aplikasi contoh panduan memulai untuk digunakan sebagai contoh.
  • Jika Anda menggunakan CameraX API, pastikan strategi tekanan balik ditetapkan ke nilai defaultnya ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. Hal ini menjamin hanya satu gambar yang akan dikirimkan untuk analisis pada satu waktu. Jika lebih banyak gambar yang dihasilkan ketika penganalisis sedang sibuk, mereka akan dibuang secara otomatis dan tidak diantrekan pengiriman. Setelah gambar yang dianalisis ditutup dengan memanggil ImageProxy.close(), gambar terbaru berikutnya akan dikirimkan.
  • Jika Anda menggunakan output detektor untuk menempatkan grafik gambar input, pertama-tama dapatkan hasilnya dari ML Kit, lalu render gambar dan overlay dalam satu langkah. Tindakan ini merender ke permukaan tampilan hanya sekali untuk setiap {i>input frame<i}. Lihat CameraSourcePreview dan GraphicOverlay dalam aplikasi contoh panduan memulai sebagai contoh.
  • Jika Anda menggunakan Camera2 API, ambil gambar dengan Format ImageFormat.YUV_420_888. Jika Anda menggunakan Camera API versi lama, ambil gambar dengan Format ImageFormat.NV21.
  • Sebaiknya ambil gambar dengan resolusi yang lebih rendah. Namun, perlu diingat persyaratan dimensi gambar API ini.