Last updated: August 14

<span aria-hidden="true" id="implementing-ocr-in-android-apps-with-google-ml-kit"></span>

# Implementing OCR in Android apps with Google ML Kit

![Tim Koschützki](/assets/images/teammates/avatar-tim-kos-1.jpg?dpl=dpl_C6YH6XrtnwbHLJcm1LDKywQn4CB3)

#### Tim Koschützki

Co-founder · Berlin, Germany · Show bio

[](https://x.com/tim%5Fkos)[](https://github.com/tim-kos)

Optical Character Recognition (OCR) enables your Android app to detect and extract text from images. Google ML Kit provides an on-device OCR library, so the example below can recognize text without sending each image to a remote API. This guide adds Latin-script text recognition, captures or selects an image, and displays the recognized text.

<span aria-hidden="true" id="prerequisites"></span>

## Prerequisites

Before starting, ensure you have:

* A current stable Android Studio and its recommended Android Gradle plugin
* An Android device or emulator running Android API level 23 or higher
* Basic knowledge of Android development and Kotlin

<span aria-hidden="true" id="setting-up-the-android-project"></span>

## Setting up the Android project

Create a new Android project in Android Studio:

1. Open Android Studio and select **File > New > New Project**.
2. Choose **Empty Activity** and click **Next**.
3. Name the project, for example `TextRecognitionApp`.
4. Select **Kotlin** as the language.
5. Set the **Minimum SDK** to **API 23** or newer, as required by the current Text Recognition v2 API.
6. Click **Finish** to create the project.

<span aria-hidden="true" id="adding-the-ml-kit-dependency"></span>

## Adding the ML Kit dependency

Enable view binding and add the bundled Latin-script model to your app-level `build.gradle` file:

```gradle
android {
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation 'com.google.mlkit:text-recognition:16.0.1'
}

```

This dependency includes the recognition model in your app, so it is available immediately. If download size matters more than first-run availability, Google also offers the smaller`com.google.android.gms:play-services-mlkit-text-recognition:19.0.1` dependency. That version downloads its model through Google Play services. See Google's[bundled and unbundled model comparison⁠](https://developers.google.com/ml-kit/vision/text-recognition/v2/android#before-you-begin)before choosing.

<span aria-hidden="true" id="configuring-permissions"></span>

## Configuring permissions

The implementation below uses Android's system camera app and Photo Picker. It does not need`READ_EXTERNAL_STORAGE`, broad photo-library access, or direct camera access. The Photo Picker grants access only to the selected item and falls back to `ACTION_OPEN_DOCUMENT` on older supported devices. If you later replace the camera intent with CameraX for an in-app preview, request the`CAMERA` permission at runtime.

<span aria-hidden="true" id="creating-the-layout"></span>

## Creating the layout

Create `activity_main.xml` with controls for capturing and selecting images, an image preview, and a scrollable text result:

```xml
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <Button
        android:id="@+id/btnCapture"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Capture Image" />

    <Button
        android:id="@+id/btnGallery"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Select from Gallery" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_marginTop="16dp"
        android:contentDescription="Selected image"
        android:scaleType="centerCrop" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="16dp"
        android:layout_weight="1">

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp" />
    </ScrollView>
</LinearLayout>

```

<span aria-hidden="true" id="implementing-ocr-functionality"></span>

## Implementing OCR functionality

Use `TakePicture` to save a full-resolution image to a content URI, and use `PickVisualMedia` to give the user the system Photo Picker. `InputImage.fromFilePath()` creates the ML Kit input directly from that URI.

```kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var photoUri: Uri
    private lateinit var recognizer: TextRecognizer

    private val takePictureLauncher = registerForActivityResult(
        ActivityResultContracts.TakePicture()
    ) { success ->
        if (success) {
            processImage(photoUri)
        }
    }

    private val selectPictureLauncher = registerForActivityResult(
        ActivityResultContracts.PickVisualMedia()
    ) { uri ->
        if (uri != null) {
            processImage(uri)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)

        binding.btnCapture.setOnClickListener {
            captureImage()
        }

        binding.btnGallery.setOnClickListener {
            selectPictureLauncher.launch(
                PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
            )
        }
    }

    private fun captureImage() {
        val imageFile = File.createTempFile("IMG_", ".jpg", cacheDir)
        photoUri = FileProvider.getUriForFile(this, "${packageName}.fileprovider", imageFile)
        takePictureLauncher.launch(photoUri)
    }

    private fun processImage(uri: Uri) {
        binding.imageView.setImageURI(uri)
        val image = try {
            InputImage.fromFilePath(this, uri)
        } catch (error: IOException) {
            showMessage("The selected image could not be opened")
            return
        }

        recognizer.process(image)
            .addOnSuccessListener { visionText ->
                binding.textView.text = visionText.text
            }
            .addOnFailureListener {
                showMessage("Text recognition failed. Try a clearer image")
            }
    }

    private fun showMessage(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        recognizer.close()
    }
}

```

Add the FileProvider configuration to `AndroidManifest.xml` within the `<application>` tag:

```xml
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

```

Create `res/xml/file_paths.xml`:

```xml
<paths>
    <cache-path name="cache" path="." />
</paths>

```

<span aria-hidden="true" id="optimizing-ocr-accuracy-and-performance"></span>

## Optimizing OCR accuracy and performance

Follow Google's[input image guidelines⁠](https://developers.google.com/ml-kit/vision/text-recognition/v2/android#input%5Fimage%5Fguidelines):

* **Character size**: Aim for at least 16x16 pixels per character. Increasing characters beyond roughly 24x24 pixels generally does not improve recognition accuracy.
* **Image quality**: Use good lighting, sharp focus, and minimal motion blur.
* **Image dimensions**: Use the lowest resolution that still gives every character enough pixels. Smaller images reduce latency when scanning in real time.
* **Real-time analysis**: With CameraX, keep the default`ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST` backpressure strategy so frames do not queue while the recognizer is busy.

<span aria-hidden="true" id="testing-the-application"></span>

## Testing the application

Test your OCR implementation with:

* Printed text in different fonts and sizes
* Latin-script languages supported by `TextRecognizerOptions.DEFAULT_OPTIONS`
* Varying lighting, focus, and text orientation
* Images from both the camera and Photo Picker
* Device rotations and process recreation

Chinese, Devanagari, Japanese, and Korean each require their own ML Kit dependency and recognizer options. Add the corresponding library before including one of those scripts in your test matrix.

<span aria-hidden="true" id="troubleshooting"></span>

## Troubleshooting

If text recognition fails:

* Verify that the image is clear, well lit, and gives each character enough pixels.
* Confirm that FileProvider is configured and the temporary image file is accessible.
* If you chose the Google Play services dependency, confirm that its model has finished downloading. The bundled dependency used in this example does not need a model download.
* Use Logcat to inspect the underlying error during development.

<span aria-hidden="true" id="conclusion"></span>

## Conclusion

You now have an Android OCR flow that accepts camera and Photo Picker images and recognizes Latin-script text on-device. For server-side OCR across uploaded files, see Transloadit's[/document/ocr Robot](/docs/robots/document-ocr.md) and[OCR demo](/demos/artificial-intelligence/recognize-text-in-images.md).

\#android#ocr#google-ml-kit#android-ocr#artificial-intelligence-service

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed · 5 GB included in the free plan

Cancel anytime
