Вы можете использовать ML Kit для распознавания текста на изображениях или видео, например текста уличного знака. Основными характеристиками этой функции являются:
Особенность | Разделенный | В комплекте |
---|---|---|
Имя библиотеки | com.google.android.gms:play-services-mlkit-распознавание текста com.google.android.gms:play-services-mlkit-распознавание текста-китайский com.google.android.gms:play-services-mlkit-распознавание текста-деванагари com.google.android.gms:play-services-mlkit-текст-распознавание-японский com.google.android.gms:play-services-mlkit-распознавание текста-корейский | com.google.mlkit:распознавание текста com.google.mlkit: распознавание текста на китайском языке com.google.mlkit:распознавание текста-деванагари com.google.mlkit: распознавание текста на японском языке com.google.mlkit: распознавание текста на корейском языке |
Выполнение | Модель динамически загружается через сервисы Google Play. | Модель статически связана с вашим приложением во время сборки. |
Размер приложения | Увеличение размера примерно на 260 КБ для каждой архитектуры скрипта. | Увеличение размера сценария примерно на 4 МБ для каждой архитектуры. |
Время инициализации | Возможно, придется дождаться загрузки модели перед первым использованием. | Модель доступна сразу. |
Производительность | В реальном времени на большинстве устройств для библиотеки латинского алфавита, медленнее на других. | В реальном времени на большинстве устройств для библиотеки латинского алфавита, медленнее на других. |
Попробуйте
- Поэкспериментируйте с образцом приложения , чтобы увидеть пример использования этого API.
- Попробуйте код самостоятельно с помощью codelab .
Прежде чем вы начнете
- В файле
build.gradle
на уровне проекта обязательно включите репозиторий Google Maven как в разделыbuildscript
, так и в разделыallprojects
. Добавьте зависимости для Android-библиотек ML Kit в файл gradle вашего модуля на уровне приложения, обычно это
app/build.gradle
:Для связывания модели с вашим приложением:
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' }
Для использования модели в сервисах 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' }
Если вы решите использовать модель в Сервисах Google Play , вы можете настроить приложение для автоматической загрузки модели на устройство после установки приложения из Play Store. Для этого добавьте следующее объявление в файл
AndroidManifest.xml
вашего приложения:<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>
Вы также можете явно проверить доступность модели и запросить загрузку через ModuleInstallClient API сервисов Google Play. Если вы не включите загрузку модели во время установки или не запросите явную загрузку, модель будет загружена при первом запуске сканера. Запросы, которые вы делаете до завершения загрузки, не дают результатов.
1. Создайте экземпляр TextRecognizer
Создайте экземпляр TextRecognizer
, передав параметры, относящиеся к библиотеке, зависимость от которой вы объявили выше:Котлин
// 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())
Джава
// 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. Подготовьте входное изображение
Чтобы распознать текст в изображении, создайте объект InputImage
либо из Bitmap
, media.Image
, ByteBuffer
, массива байтов, либо из файла на устройстве. Затем передайте объект InputImage
методу TextRecognizer
processImage
.
Вы можете создать объект InputImage
из разных источников, каждый из которых описан ниже.
Использование media.Image
Чтобы создать объект InputImage
из объекта media.Image
, например, когда вы захватываете изображение с камеры устройства, передайте объект media.Image
и поворот изображения в InputImage.fromMediaImage()
.
Если вы используете библиотеку CameraX , классы OnImageCapturedListener
и ImageAnalysis.Analyzer
вычисляют для вас значение поворота.
Котлин
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 // ... } } }
Джава
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 // ... } } }
Если вы не используете библиотеку камеры, которая дает вам степень поворота изображения, вы можете рассчитать ее по степени поворота устройства и ориентации датчика камеры в устройстве:
Котлин
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 }
Джава
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; }
Затем передайте объект media.Image
и значение степени поворота в InputImage.fromMediaImage()
:
Котлин
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Использование URI файла
Чтобы создать объект InputImage
из URI файла, передайте контекст приложения и URI файла в InputImage.fromFilePath()
. Это полезно, когда вы используете намерение ACTION_GET_CONTENT
, чтобы предложить пользователю выбрать изображение из своего приложения-галереи.
Котлин
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
Чтобы создать объект InputImage
из ByteBuffer
или ByteArray
, сначала вычислите степень поворота изображения, как описано ранее для ввода media.Image
. Затем создайте объект InputImage
с буфером или массивом вместе с высотой изображения, шириной, форматом кодирования цвета и степенью поворота:
Котлин
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 )
Джава
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
Чтобы создать объект InputImage
из объекта Bitmap
, сделайте следующее объявление:
Котлин
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Изображение представлено объектом Bitmap
вместе с градусами поворота.
3. Обработайте изображение
Передайте изображение методу process
:
Котлин
val result = recognizer.process(image) .addOnSuccessListener { visionText -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Джава
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. Извлечение текста из блоков распознанного текста
Если операция распознавания текста завершается успешно, объект Text
передается обработчику успеха. Объект Text
содержит полный текст, распознанный на изображении, и ноль или более объектов TextBlock
.
Каждый TextBlock
представляет собой прямоугольный блок текста, который содержит ноль или более объектов Line
. Каждый объект Line
представляет строку текста, которая содержит ноль или более объектов Element
. Каждый объект Element
представляет слово или словоподобную сущность, которая содержит ноль или более объектов Symbol
. Каждый объект Symbol
представляет собой символ, цифру или словосочетание.
Для каждого объекта TextBlock
, Line
, Element
и Symbol
вы можете получить текст, распознаваемый в регионе, ограничивающие координаты региона и многие другие атрибуты, такие как информация о вращении, оценка достоверности и т. д.
Например:
Котлин
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 } } }
Джава
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(); } } } }
Инструкции по входному изображению
Чтобы ML Kit точно распознавал текст, входные изображения должны содержать текст, представленный достаточным количеством данных пикселей. В идеале каждый символ должен быть размером не менее 16х16 пикселей. Как правило, символы размером более 24x24 пикселей не улучшают точность.
Так, например, изображение размером 640x480 может хорошо подойти для сканирования визитной карточки, занимающей всю ширину изображения. Для сканирования документа, напечатанного на бумаге формата Letter, может потребоваться изображение размером 720x1280 пикселей.
Плохая фокусировка изображения может повлиять на точность распознавания текста. Если вы не получаете приемлемых результатов, попробуйте попросить пользователя повторно захватить изображение.
Если вы распознаете текст в приложении реального времени, вам следует учитывать общие размеры входных изображений. Меньшие изображения могут быть обработаны быстрее. Чтобы уменьшить задержку, убедитесь, что текст занимает как можно большую часть изображения, и захватывайте изображения с более низким разрешением (учитывая упомянутые выше требования к точности). Дополнительные сведения см. в разделе Советы по повышению производительности .
Советы по повышению производительности
- Если вы используете
Camera
илиcamera2
API, дросселируйте вызовы детектора. Если новый видеокадр становится доступным во время работы детектора, удалите кадр. В качестве примера см. классVisionProcessorBase
в примере приложения с кратким руководством. - Если вы используете
CameraX
API, убедитесь, что для стратегии обратного давления установлено значение по умолчаниюImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Это гарантирует, что только одно изображение будет доставлено для анализа за раз. Если в то время, когда анализатор занят, создается больше изображений, они будут автоматически удалены и не будут поставлены в очередь на доставку. Как только анализируемое изображение будет закрыто вызовом ImageProxy.close(), будет доставлено следующее последнее изображение. - Если вы используете выходные данные детектора для наложения графики на входное изображение, сначала получите результат от ML Kit, а затем выполните визуализацию изображения и наложение за один шаг. Это визуализирует поверхность дисплея только один раз для каждого входного кадра. В качестве примера см. классы
CameraSourcePreview
иGraphicOverlay
в примере приложения с кратким руководством. - Если вы используете Camera2 API, захватывайте изображения в формате
ImageFormat.YUV_420_888
. Если вы используете старый API камеры, захватывайте изображения в форматеImageFormat.NV21
. - Рассмотрите возможность захвата изображений с более низким разрешением. Однако также имейте в виду требования этого API к размеру изображения.