Bạn có thể sử dụng Bộ công cụ học máy để nhận dạng văn bản trong hình ảnh. Bộ công cụ học máy có cả API đa năng phù hợp để nhận dạng văn bản trong hình ảnh, chẳng hạn như văn bản của biển báo đường phố và API được tối ưu hoá để nhận dạng văn bản của tài liệu. API đa năng có cả mô hình trên thiết bị và mô hình dựa trên đám mây. Tính năng nhận dạng văn bản trong tài liệu chỉ có sẵn dưới dạng mô hình dựa trên đám mây. Hãy xem phần tổng quan để so sánh mô hình trên đám mây và mô hình trên thiết bị.
Trước khi bắt đầu
- Nếu bạn chưa thực hiện, hãy thêm Firebase vào dự án Android.
- Thêm các phần phụ thuộc cho thư viện Android của Bộ công cụ học máy vào tệp Gradle (ở cấp ứng dụng) của mô-đun (thường là
app/build.gradle
):apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' }
-
Không bắt buộc nhưng nên dùng: Nếu bạn sử dụng API trên thiết bị, hãy định cấu hình ứng dụng để tự động tải mô hình học máy xuống thiết bị sau khi cài đặt ứng dụng từ Cửa hàng Play.
Để thực hiện việc này, hãy thêm nội dung khai báo sau vào tệp
AndroidManifest.xml
của ứng dụng: Nếu bạn không bật tính năng tải mô hình xuống tại thời điểm cài đặt, thì mô hình sẽ được tải xuống trong lần đầu tiên bạn chạy trình phát hiện trên thiết bị. Các yêu cầu bạn đưa ra trước khi quá trình tải xuống hoàn tất sẽ không có kết quả.<application ...> ... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="ocr" /> <!-- To use multiple models: android:value="ocr,model2,model3" --> </application>
-
Nếu bạn muốn sử dụng mô hình trên đám mây và chưa bật API trên đám mây cho dự án của mình, hãy làm như sau:
- Mở trang API Bộ công cụ học máy của bảng điều khiển Firebase.
-
Nếu bạn chưa nâng cấp dự án lên gói giá Blaze, hãy nhấp vào Nâng cấp để thực hiện việc này. (Bạn sẽ chỉ được nhắc nâng cấp nếu dự án của bạn không sử dụng gói Blaze.)
Chỉ các dự án cấp Blaze mới có thể sử dụng API dựa trên đám mây.
- Nếu bạn chưa bật API dựa trên đám mây, hãy nhấp vào Bật API dựa trên đám mây.
Nếu chỉ muốn sử dụng mô hình trên thiết bị, bạn có thể bỏ qua bước này.
Bây giờ, bạn đã có thể bắt đầu nhận dạng văn bản trong hình ảnh.
Nguyên tắc về hình ảnh đầu vào
-
Để Bộ công cụ học máy nhận dạng chính xác văn bản, hình ảnh đầu vào phải chứa văn bản được biểu thị bằng đủ dữ liệu pixel. Lý tưởng nhất là đối với văn bản La Mã, mỗi ký tự phải có kích thước tối thiểu là 16x16 pixel. Đối với văn bản tiếng Trung, tiếng Nhật và tiếng Hàn (chỉ được các API dựa trên đám mây hỗ trợ), mỗi ký tự phải có kích thước 24x24 pixel. Đối với tất cả ngôn ngữ, kích thước ký tự lớn hơn 24x24 pixel thường không mang lại lợi ích về độ chính xác.
Ví dụ: hình ảnh 640x480 có thể hoạt động tốt để quét danh thiếp chiếm toàn bộ chiều rộng của hình ảnh. Để quét tài liệu in trên giấy cỡ thư, bạn có thể phải có hình ảnh có kích thước 720x1280 pixel.
-
Độ nét hình ảnh kém có thể làm giảm độ chính xác của tính năng nhận dạng văn bản. Nếu bạn không nhận được kết quả chấp nhận được, hãy thử yêu cầu người dùng chụp lại hình ảnh.
-
Nếu đang nhận dạng văn bản trong một ứng dụng theo thời gian thực, bạn cũng nên cân nhắc các kích thước tổng thể của hình ảnh đầu vào. Hình ảnh nhỏ hơn có thể được xử lý nhanh hơn, vì vậy, để giảm độ trễ, hãy chụp hình ở độ phân giải thấp hơn (lưu ý các yêu cầu về độ chính xác ở trên) và đảm bảo văn bản chiếm nhiều không gian hình ảnh nhất có thể. Ngoài ra, hãy xem Mẹo cải thiện hiệu suất theo thời gian thực.
Nhận dạng văn bản trong hình ảnh
Để nhận dạng văn bản trong hình ảnh bằng cách sử dụng mô hình trên thiết bị hoặc trên đám mây, hãy chạy trình nhận dạng văn bản như mô tả dưới đây.
1. Chạy trình nhận dạng văn bản
Để nhận dạng văn bản trong hình ảnh, hãy tạo đối tượngFirebaseVisionImage
từ Bitmap
, media.Image
, ByteBuffer
, mảng byte hoặc tệp trên thiết bị. Sau đó, hãy truyền đối tượng FirebaseVisionImage
vào phương thức processImage
của FirebaseVisionTextRecognizer
.
Tạo đối tượng
FirebaseVisionImage
từ hình ảnh của bạn.-
Để tạo đối tượng
FirebaseVisionImage
từ đối tượngmedia.Image
, chẳng hạn như khi chụp ảnh từ máy ảnh của thiết bị, hãy truyền đối tượngmedia.Image
và độ xoay của hình ảnh đếnFirebaseVisionImage.fromMediaImage()
.Nếu sử dụng thư viện CameraX, các lớp
OnImageCapturedListener
vàImageAnalysis.Analyzer
sẽ tính toán giá trị xoay cho bạn, vì vậy, bạn chỉ cần chuyển đổi giá trị xoay thành một trong các hằng sốROTATION_
của Bộ công cụ học máy trước khi gọiFirebaseVisionImage.fromMediaImage()
:Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } }
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } }
Nếu không sử dụng thư viện máy ảnh cung cấp độ xoay của hình ảnh, bạn có thể tính toán độ xoay đó từ độ xoay của thiết bị và hướng của cảm biến máy ảnh trong thiết bị:
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 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, Context context) 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); // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360; // Return the corresponding FirebaseVisionImageMetadata rotation value. int result; switch (rotationCompensation) { case 0: result = FirebaseVisionImageMetadata.ROTATION_0; break; case 90: result = FirebaseVisionImageMetadata.ROTATION_90; break; case 180: result = FirebaseVisionImageMetadata.ROTATION_180; break; case 270: result = FirebaseVisionImageMetadata.ROTATION_270; break; default: result = FirebaseVisionImageMetadata.ROTATION_0; Log.e(TAG, "Bad rotation value: " + rotationCompensation); } return result; }
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * 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, context: Context): 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) // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result }
Sau đó, truyền đối tượng
media.Image
và giá trị xoay vàoFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Để tạo đối tượng
FirebaseVisionImage
từ URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp đếnFirebaseVisionImage.fromFilePath()
. Điều này hữu ích khi bạn sử dụng ý địnhACTION_GET_CONTENT
để nhắc người dùng chọn một hình ảnh trong ứng dụng thư viện.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Để tạo đối tượng
FirebaseVisionImage
từByteBuffer
hoặc mảng byte, trước tiên, hãy tính toán độ xoay hình ảnh như mô tả ở trên cho dữ liệu đầu vàomedia.Image
.Sau đó, hãy tạo một đối tượng
FirebaseVisionImageMetadata
chứa chiều cao, chiều rộng, định dạng mã hoá màu và độ xoay của hình ảnh:Java
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build();
Kotlin
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
Sử dụng bộ đệm hoặc mảng và đối tượng siêu dữ liệu để tạo đối tượng
FirebaseVisionImage
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- Cách tạo đối tượng
FirebaseVisionImage
từ đối tượngBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
biểu thị phải thẳng đứng, không cần xoay thêm.
-
Lấy một thực thể của
FirebaseVisionTextRecognizer
.Cách sử dụng mô hình trên thiết bị:
Java
FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance() .getOnDeviceTextRecognizer();
Kotlin
val detector = FirebaseVision.getInstance() .onDeviceTextRecognizer
Cách sử dụng mô hình dựa trên đám mây:
Java
FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance() .getCloudTextRecognizer(); // Or, to change the default settings: // FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance() // .getCloudTextRecognizer(options);
// Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages FirebaseVisionCloudTextRecognizerOptions options = new FirebaseVisionCloudTextRecognizerOptions.Builder() .setLanguageHints(Arrays.asList("en", "hi")) .build();
Kotlin
val detector = FirebaseVision.getInstance().cloudTextRecognizer // Or, to change the default settings: // val detector = FirebaseVision.getInstance().getCloudTextRecognizer(options)
// Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages val options = FirebaseVisionCloudTextRecognizerOptions.Builder() .setLanguageHints(listOf("en", "hi")) .build()
Cuối cùng, hãy truyền hình ảnh đến phương thức
processImage
:Java
Task<FirebaseVisionText> result = detector.processImage(image) .addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() { @Override public void onSuccess(FirebaseVisionText firebaseVisionText) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin
val result = detector.processImage(image) .addOnSuccessListener { firebaseVisionText -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
2. Trích xuất văn bản từ các khối văn bản được nhận dạng
Nếu thao tác nhận dạng văn bản thành công, đối tượngFirebaseVisionText
sẽ được truyền đến trình nghe thành công. Đối tượng FirebaseVisionText
chứa toàn bộ văn bản được nhận dạng trong hình ảnh và không có hoặc có nhiều đối tượng TextBlock
.
Mỗi TextBlock
đại diện cho một khối văn bản hình chữ nhật, chứa 0 hoặc nhiều đối tượng Line
. Mỗi đối tượng Line
chứa từ 0 đến nhiều đối tượng Element
, đại diện cho các từ và thực thể giống từ (ngày, số, v.v.).
Đối với mỗi đối tượng TextBlock
, Line
và Element
, bạn có thể nhận được văn bản được nhận dạng trong vùng và toạ độ giới hạn của vùng.
Ví dụ:
Java
String resultText = result.getText(); for (FirebaseVisionText.TextBlock block: result.getTextBlocks()) { String blockText = block.getText(); Float blockConfidence = block.getConfidence(); List<RecognizedLanguage> blockLanguages = block.getRecognizedLanguages(); Point[] blockCornerPoints = block.getCornerPoints(); Rect blockFrame = block.getBoundingBox(); for (FirebaseVisionText.Line line: block.getLines()) { String lineText = line.getText(); Float lineConfidence = line.getConfidence(); List<RecognizedLanguage> lineLanguages = line.getRecognizedLanguages(); Point[] lineCornerPoints = line.getCornerPoints(); Rect lineFrame = line.getBoundingBox(); for (FirebaseVisionText.Element element: line.getElements()) { String elementText = element.getText(); Float elementConfidence = element.getConfidence(); List<RecognizedLanguage> elementLanguages = element.getRecognizedLanguages(); Point[] elementCornerPoints = element.getCornerPoints(); Rect elementFrame = element.getBoundingBox(); } } }
Kotlin
val resultText = result.text for (block in result.textBlocks) { val blockText = block.text val blockConfidence = block.confidence val blockLanguages = block.recognizedLanguages val blockCornerPoints = block.cornerPoints val blockFrame = block.boundingBox for (line in block.lines) { val lineText = line.text val lineConfidence = line.confidence val lineLanguages = line.recognizedLanguages val lineCornerPoints = line.cornerPoints val lineFrame = line.boundingBox for (element in line.elements) { val elementText = element.text val elementConfidence = element.confidence val elementLanguages = element.recognizedLanguages val elementCornerPoints = element.cornerPoints val elementFrame = element.boundingBox } } }
Mẹo cải thiện hiệu suất theo thời gian thực
Nếu bạn muốn sử dụng mô hình trên thiết bị để nhận dạng văn bản trong một ứng dụng theo thời gian thực, hãy làm theo các nguyên tắc sau để đạt được tốc độ khung hình tốt nhất:
- Điều tiết các lệnh gọi đến trình nhận dạng văn bản. Nếu có khung video mới trong khi trình nhận dạng văn bản đang chạy, hãy thả khung đó.
- Nếu bạn đang sử dụng kết quả của trình nhận dạng văn bản để phủ hình ảnh đồ hoạ lên hình ảnh đầu vào, trước tiên, hãy lấy kết quả từ Bộ công cụ học máy, sau đó kết xuất hình ảnh và lớp phủ trong một bước. Bằng cách này, bạn chỉ kết xuất một lần cho mỗi khung đầu vào trên bề mặt hiển thị.
-
Nếu bạn sử dụng API Camera2, hãy chụp ảnh ở định dạng
ImageFormat.YUV_420_888
.Nếu bạn sử dụng API Máy ảnh cũ, hãy chụp ảnh ở định dạng
ImageFormat.NV21
. - Cân nhắc chụp ảnh ở độ phân giải thấp hơn. Tuy nhiên, hãy lưu ý đến các yêu cầu về kích thước hình ảnh của API này.
Các bước tiếp theo
- Trước khi triển khai một ứng dụng sử dụng API trên đám mây cho phiên bản chính thức, bạn nên thực hiện thêm một số bước để ngăn chặn và giảm thiểu tác động của việc truy cập trái phép vào API.
Nhận dạng văn bản trong hình ảnh tài liệu
Để nhận dạng văn bản của một tài liệu, hãy định cấu hình và chạy trình nhận dạng văn bản tài liệu dựa trên đám mây như mô tả dưới đây.
API nhận dạng văn bản tài liệu được mô tả bên dưới cung cấp một giao diện nhằm giúp bạn thuận tiện hơn khi xử lý hình ảnh tài liệu. Tuy nhiên, nếu muốn sử dụng giao diện do API FirebaseVisionTextRecognizer
cung cấp, bạn có thể dùng giao diện này để quét tài liệu bằng cách định cấu hình trình nhận dạng văn bản trên đám mây để sử dụng mô hình văn bản dày đặc.
Cách sử dụng API nhận dạng văn bản trong tài liệu:
1. Chạy trình nhận dạng văn bản
Để nhận dạng văn bản trong hình ảnh, hãy tạo một đối tượngFirebaseVisionImage
từ Bitmap
, media.Image
, ByteBuffer
, mảng byte hoặc tệp trên thiết bị.
Sau đó, hãy truyền đối tượng FirebaseVisionImage
vào phương thức processImage
của FirebaseVisionDocumentTextRecognizer
.
Tạo đối tượng
FirebaseVisionImage
từ hình ảnh của bạn.-
Để tạo đối tượng
FirebaseVisionImage
từ đối tượngmedia.Image
, chẳng hạn như khi chụp ảnh từ máy ảnh của thiết bị, hãy truyền đối tượngmedia.Image
và độ xoay của hình ảnh đếnFirebaseVisionImage.fromMediaImage()
.Nếu sử dụng thư viện CameraX, các lớp
OnImageCapturedListener
vàImageAnalysis.Analyzer
sẽ tính toán giá trị xoay cho bạn, vì vậy, bạn chỉ cần chuyển đổi giá trị xoay thành một trong các hằng sốROTATION_
của Bộ công cụ học máy trước khi gọiFirebaseVisionImage.fromMediaImage()
:Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } }
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } }
Nếu không sử dụng thư viện máy ảnh cung cấp độ xoay của hình ảnh, bạn có thể tính toán độ xoay đó từ độ xoay của thiết bị và hướng của cảm biến máy ảnh trong thiết bị:
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 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, Context context) 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); // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360; // Return the corresponding FirebaseVisionImageMetadata rotation value. int result; switch (rotationCompensation) { case 0: result = FirebaseVisionImageMetadata.ROTATION_0; break; case 90: result = FirebaseVisionImageMetadata.ROTATION_90; break; case 180: result = FirebaseVisionImageMetadata.ROTATION_180; break; case 270: result = FirebaseVisionImageMetadata.ROTATION_270; break; default: result = FirebaseVisionImageMetadata.ROTATION_0; Log.e(TAG, "Bad rotation value: " + rotationCompensation); } return result; }
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * 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, context: Context): 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) // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result }
Sau đó, truyền đối tượng
media.Image
và giá trị xoay vàoFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Để tạo đối tượng
FirebaseVisionImage
từ URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp đếnFirebaseVisionImage.fromFilePath()
. Điều này hữu ích khi bạn sử dụng ý địnhACTION_GET_CONTENT
để nhắc người dùng chọn một hình ảnh trong ứng dụng thư viện.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Để tạo đối tượng
FirebaseVisionImage
từByteBuffer
hoặc mảng byte, trước tiên, hãy tính toán độ xoay hình ảnh như mô tả ở trên cho dữ liệu đầu vàomedia.Image
.Sau đó, hãy tạo một đối tượng
FirebaseVisionImageMetadata
chứa chiều cao, chiều rộng, định dạng mã hoá màu và độ xoay của hình ảnh:Java
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build();
Kotlin
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
Sử dụng bộ đệm hoặc mảng và đối tượng siêu dữ liệu để tạo đối tượng
FirebaseVisionImage
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- Cách tạo đối tượng
FirebaseVisionImage
từ đối tượngBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
biểu thị phải thẳng đứng, không cần xoay thêm.
-
Tạo một thực thể của
FirebaseVisionDocumentTextRecognizer
:Java
FirebaseVisionDocumentTextRecognizer detector = FirebaseVision.getInstance() .getCloudDocumentTextRecognizer();
// Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages FirebaseVisionCloudDocumentRecognizerOptions options = new FirebaseVisionCloudDocumentRecognizerOptions.Builder() .setLanguageHints(Arrays.asList("en", "hi")) .build(); FirebaseVisionDocumentTextRecognizer detector = FirebaseVision.getInstance() .getCloudDocumentTextRecognizer(options);
Kotlin
val detector = FirebaseVision.getInstance() .cloudDocumentTextRecognizer
// Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages val options = FirebaseVisionCloudDocumentRecognizerOptions.Builder() .setLanguageHints(listOf("en", "hi")) .build() val detector = FirebaseVision.getInstance() .getCloudDocumentTextRecognizer(options)
Cuối cùng, hãy truyền hình ảnh đến phương thức
processImage
:Java
detector.processImage(myImage) .addOnSuccessListener(new OnSuccessListener<FirebaseVisionDocumentText>() { @Override public void onSuccess(FirebaseVisionDocumentText result) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin
detector.processImage(myImage) .addOnSuccessListener { firebaseVisionDocumentText -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
2. Trích xuất văn bản từ các khối văn bản được nhận dạng
Nếu thành công, thao tác nhận dạng văn bản sẽ trả về đối tượng FirebaseVisionDocumentText
. Đối tượng FirebaseVisionDocumentText
chứa toàn bộ văn bản được nhận dạng trong hình ảnh và hệ phân cấp các đối tượng phản ánh cấu trúc của tài liệu được nhận dạng:
FirebaseVisionDocumentText.Block
FirebaseVisionDocumentText.Paragraph
FirebaseVisionDocumentText.Word
FirebaseVisionDocumentText.Symbol
Đối với mỗi đối tượng Block
, Paragraph
, Word
và Symbol
, bạn có thể nhận được văn bản được nhận dạng trong vùng và toạ độ giới hạn của vùng.
Ví dụ:
Java
String resultText = result.getText(); for (FirebaseVisionDocumentText.Block block: result.getBlocks()) { String blockText = block.getText(); Float blockConfidence = block.getConfidence(); List<RecognizedLanguage> blockRecognizedLanguages = block.getRecognizedLanguages(); Rect blockFrame = block.getBoundingBox(); for (FirebaseVisionDocumentText.Paragraph paragraph: block.getParagraphs()) { String paragraphText = paragraph.getText(); Float paragraphConfidence = paragraph.getConfidence(); List<RecognizedLanguage> paragraphRecognizedLanguages = paragraph.getRecognizedLanguages(); Rect paragraphFrame = paragraph.getBoundingBox(); for (FirebaseVisionDocumentText.Word word: paragraph.getWords()) { String wordText = word.getText(); Float wordConfidence = word.getConfidence(); List<RecognizedLanguage> wordRecognizedLanguages = word.getRecognizedLanguages(); Rect wordFrame = word.getBoundingBox(); for (FirebaseVisionDocumentText.Symbol symbol: word.getSymbols()) { String symbolText = symbol.getText(); Float symbolConfidence = symbol.getConfidence(); List<RecognizedLanguage> symbolRecognizedLanguages = symbol.getRecognizedLanguages(); Rect symbolFrame = symbol.getBoundingBox(); } } } }
Kotlin
val resultText = result.text for (block in result.blocks) { val blockText = block.text val blockConfidence = block.confidence val blockRecognizedLanguages = block.recognizedLanguages val blockFrame = block.boundingBox for (paragraph in block.paragraphs) { val paragraphText = paragraph.text val paragraphConfidence = paragraph.confidence val paragraphRecognizedLanguages = paragraph.recognizedLanguages val paragraphFrame = paragraph.boundingBox for (word in paragraph.words) { val wordText = word.text val wordConfidence = word.confidence val wordRecognizedLanguages = word.recognizedLanguages val wordFrame = word.boundingBox for (symbol in word.symbols) { val symbolText = symbol.text val symbolConfidence = symbol.confidence val symbolRecognizedLanguages = symbol.recognizedLanguages val symbolFrame = symbol.boundingBox } } } }
Các bước tiếp theo
- Trước khi triển khai một ứng dụng sử dụng API trên đám mây cho phiên bản chính thức, bạn nên thực hiện thêm một số bước để ngăn chặn và giảm thiểu tác động của việc truy cập trái phép vào API.