Nhận dạng văn bản trong hình ảnh bằng Bộ công cụ học máy trên Android

Bạn có thể 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à một 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 trên đám mây. Tính năng nhận dạng văn bản tài liệu hiện chỉ được cung cấp dưới dạng mô hình dựa trên đám mây. Xem Overview (Tổng quan) để so sánh đám mây và trên thiết bị.

Trước khi bắt đầu

  1. Nếu bạn chưa làm như vậy, thêm Firebase vào dự án Android của bạn.
  2. Thêm các phần phụ thuộc của thư viện Android cho Bộ công cụ học máy vào mô-đun của bạn Tệp Gradle (cấp ứng dụng) (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'
    }
  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 để tự động tải mô hình ML xuống thiết bị sau khi ứng dụng của bạn đã cài đặt từ Cửa hàng Play.

    Để làm như vậy, hãy thêm nội dung khai báo sau vào Tệp AndroidManifest.xml:

    <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 không cho phép tải mô hình xuống tại thời điểm cài đặt, mô hình đó sẽ bị được tải xuống vào lần đầu tiên bạn chạy trình phát hiện trên thiết bị. 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 tạo ra kết quả nào.
  4. Nếu bạn muốn sử dụng mô hình trên đám mây và chưa bật các API trên đám mây cho dự án của bạn, hãy thực hiện ngay bây giờ:

    1. Mở Bộ công cụ học máy trang API của bảng điều khiển Firebase.
    2. Nếu bạn chưa nâng cấp dự án của mình lên Gói giá linh hoạt, hãy nhấp vào Hãy nâng cấp để làm điều này. (Bạn sẽ chỉ được nhắc nâng cấp nếu không có trong Kế hoạch linh hoạt.)

      Chỉ các dự án cấp Blaze mới có thể sử dụng API trên đám mây.

    3. Nếu bạn chưa bật API trên đám mây, hãy nhấp vào Bật API dựa trên đám mây API.

    Nếu chỉ muốn sử dụng mẫu trên thiết bị, bạn có thể bỏ qua bước này.

Bây giờ, bạn đã sẵn sàng bắt đầu nhận dạng văn bản trong hình ảnh.

Nguyên tắc nhập hình ảnh

  • Để 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 được biểu thị bằng đủ dữ liệu pixel. Lý tưởng cho tiếng Latinh văn bản, mỗi ký tự phải tối thiểu 16x16 pixel. Đối với tiếng Trung, Văn bản tiếng Nhật và tiếng Hàn (chỉ được hỗ trợ bởi API trên đám mây), mỗi loại ký tự phải là 24x24 pixel. Đối với tất cả các ngôn ngữ, thường không có tính chính xác cho các ký tự lớn hơn 24x24 pixel.

    Vì vậy, ví dụ: hình ảnh 640x480 có thể phù hợp để 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 được in trên giấy có kích thước chữ cái, có thể yêu cầu hình ảnh 720x1280 pixel.

  • Tiêu điểm hình ảnh kém có thể ảnh hưởng đến độ chính xác của nhận dạng văn bản. Nếu bạn không để nhận được kết quả có thể 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 bạn đang nhận dạng được văn bản trong ứng dụng theo thời gian thực, bạn cũng có thể xem xét kích thước tổng thể của các hình ảnh đầu vào. Nhỏ hơn có thể được xử lý nhanh hơn, nên để giảm độ trễ, hãy chụp ả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 rằng văn bản chiếm nhiều diện tích hình ảnh nhất có thể. Xem thêm Mẹo cải thiện hiệu suất theo thời gian thực.


Nhận biết văn bản trong hình ảnh

Để nhận dạng văn bản trong hình ảnh bằng mô hình trên thiết bị hoặc trên đám mây, chạy trình nhận dạng văn bản như được mô tả bên dưới.

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ượng FirebaseVisionImage từ một mảng Bitmap, media.Image, ByteBuffer, byte hoặc một tệp trên thiết bị. Sau đó, hãy truyền đối tượng FirebaseVisionImage vào phương thức Phương thức processImage của FirebaseVisionTextRecognizer.

  1. Tạo một đối tượng FirebaseVisionImage từ hình ảnh của bạn.

    • Cách tạo đối tượng FirebaseVisionImage qua Đối tượng media.Image, chẳng hạn như khi chụp ảnh từ camera của thiết bị, hãy truyền đối tượng media.Image và xoay thành FirebaseVisionImage.fromMediaImage().

      Nếu bạn sử dụng Thư viện CameraX, OnImageCapturedListener và Các lớp ImageAnalysis.Analyzer tính toán giá trị xoay cho bạn, nên bạn chỉ cần chuyển đổi chế độ xoay vòng thành một Hằng số ROTATION_ trước khi gọi FirebaseVisionImage.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+KTX

      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 cho phép bạn xoay hình ảnh, có thể tính toán kích thước này dựa trên hướng xoay của thiết bị và hướng của máy ảnh cảm biến 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+KTX

      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 đó, hãy truyền đối tượng media.Image và thành FirebaseVisionImage.fromMediaImage():

      Java

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

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Để tạo đối tượng FirebaseVisionImage qua URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp để FirebaseVisionImage.fromFilePath() Điều này rất hữu ích khi bạn sử dụng ý định ACTION_GET_CONTENT để nhắc người dùng chọn một bức ảnh trong ứng dụng thư viện của họ.

      Java

      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

      Kotlin+KTX

      val image: FirebaseVisionImage
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          e.printStackTrace()
      }
    • Cách tạo đối tượng FirebaseVisionImage qua ByteBuffer hoặc một mảng byte, trước tiên, hãy tính hình ảnh như mô tả ở trên cho đầu vào media.Image.

      Sau đó, hãy tạo một đối tượng FirebaseVisionImageMetadata có chứa chiều cao, chiều rộng, định dạng mã hoá màu của hình ảnh, và xoay:

      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+KTX

      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 vùng đệm hoặc mảng và đối tượng siêu dữ liệu để tạo một Đối tượng FirebaseVisionImage:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Cách tạo đối tượng FirebaseVisionImage qua Đối tượng Bitmap:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Hình ảnh mà đối tượng Bitmap đại diện phải thẳng đứng mà không cần xoay thêm.

  2. Nhận một thực thể của FirebaseVisionTextRecognizer.

    Cách sử dụng mẫu trên thiết bị:

    Java

    FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance()
            .getOnDeviceTextRecognizer();

    Kotlin+KTX

    val detector = FirebaseVision.getInstance()
            .onDeviceTextRecognizer

    Cách sử dụng mô hình 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+KTX

    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()
  3. Cuối cùng, hãy truyền hình ảnh vào 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+KTX

    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ượng FirebaseVisionText sẽ được chuyển đến thành công trình nghe. Đối tượng FirebaseVisionText chứa văn bản đầy đủ được nhận dạng trong hình ảnh đó và không 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, trong đó có số 0 hoặc các đối tượng Line khác. Mỗi đối tượng Line chứa từ 0 đối tượng trở lên Đối tượng Element biểu thị từ và giống từ thực thể (ngày, số, v.v.).

Bạn có thể lấy văn bản đối với mỗi đối tượng TextBlock, LineElement được nhận dạng trong khu vực và toạ độ giới hạn của khu vực.

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+KTX

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ẫu trên thiết bị để nhận dạng văn bản 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 cuộc gọi đến trình nhận dạng tin nhắn văn bản. Nếu một 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 đầu ra của trình nhận dạng văn bản để phủ đồ 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à phủ lên trên trong một bước duy nhất. Khi làm vậy, bạn sẽ kết xuất lên giao diện màn hình một lần cho mỗi khung đầu vào.
  • Nếu bạn sử dụng API Camera2, hãy chụp ảnh trong Định dạng ImageFormat.YUV_420_888.

    Nếu bạn sử dụng API Máy ảnh cũ, hãy chụp ảnh trong Định dạng ImageFormat.NV21.

  • Hãy cân nhắc chụp ảnh ở độ phân giải thấp hơn. Tuy nhiên, hãy lưu ý rằng 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


Nhận dạng văn bản trong hình ảnh của tài liệu

Để nhận dạng văn bản của tài liệu, hãy định cấu hình và chạy ứng dụng trình nhận dạng văn bản tài liệu như được mô tả bên dưới.

API nhận dạng văn bản tài liệu, được mô tả dưới đây, cung cấp một giao diện nhằm thuận tiện hơn khi làm việc với hình ảnh của tài liệu. Tuy nhiên, nếu bạn muốn giao diện do API FirebaseVisionTextRecognizer cung cấp, bạn có thể dùng ứng dụng này để quét tài liệu bằng cách định cấu hình văn bản trên đám mây trình nhận dạng để sử dụng mô hình văn bản dày đặc.

Để sử dụng API nhận dạng văn bản 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 đối tượng FirebaseVisionImage từ một trong hai Bitmap, media.Image, ByteBuffer, mảng byte hoặc một tệp trên thiết bị. Sau đó, hãy truyền đối tượng FirebaseVisionImage vào phương thức Phương thức processImage của FirebaseVisionDocumentTextRecognizer.

  1. Tạo một đối tượng FirebaseVisionImage từ hình ảnh của bạn.

    • Cách tạo đối tượng FirebaseVisionImage qua Đối tượng media.Image, chẳng hạn như khi chụp ảnh từ camera của thiết bị, hãy truyền đối tượng media.Image và xoay thành FirebaseVisionImage.fromMediaImage().

      Nếu bạn sử dụng Thư viện CameraX, OnImageCapturedListener và Các lớp ImageAnalysis.Analyzer tính toán giá trị xoay cho bạn, nên bạn chỉ cần chuyển đổi chế độ xoay vòng thành một Hằng số ROTATION_ trước khi gọi FirebaseVisionImage.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+KTX

      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 cho phép bạn xoay hình ảnh, có thể tính toán kích thước này dựa trên hướng xoay của thiết bị và hướng của máy ảnh cảm biến 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+KTX

      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 đó, hãy truyền đối tượng media.Image và thành FirebaseVisionImage.fromMediaImage():

      Java

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

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Để tạo đối tượng FirebaseVisionImage qua URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp để FirebaseVisionImage.fromFilePath() Điều này rất hữu ích khi bạn sử dụng ý định ACTION_GET_CONTENT để nhắc người dùng chọn một bức ảnh trong ứng dụng thư viện của họ.

      Java

      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

      Kotlin+KTX

      val image: FirebaseVisionImage
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          e.printStackTrace()
      }
    • Cách tạo đối tượng FirebaseVisionImage qua ByteBuffer hoặc một mảng byte, trước tiên, hãy tính hình ảnh như mô tả ở trên cho đầu vào media.Image.

      Sau đó, hãy tạo một đối tượng FirebaseVisionImageMetadata có chứa chiều cao, chiều rộng, định dạng mã hoá màu của hình ảnh, và xoay:

      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+KTX

      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 vùng đệm hoặc mảng và đối tượng siêu dữ liệu để tạo một Đối tượng FirebaseVisionImage:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Cách tạo đối tượng FirebaseVisionImage qua Đối tượng Bitmap:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Hình ảnh mà đối tượng Bitmap đại diện phải thẳng đứng mà không cần xoay thêm.

  2. Nhận một bản sao 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+KTX

    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)

  3. Cuối cùng, hãy truyền hình ảnh vào 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+KTX

    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 thao tác nhận dạng văn bản thành công, thao tác sẽ trả về một Đối tượng FirebaseVisionDocumentText. Đáp Đối tượng FirebaseVisionDocumentText chứa văn bản đầy đủ được nhận dạng trong và một hệ phân cấp các đối tượng phản ánh cấu trúc của tài liệu:

Đối với mỗi đối tượng Block, Paragraph, WordSymbol, bạn có thể lấy thuộc tính văn bản được nhận dạng ở khu vực và toạ độ giới hạn của khu vực.

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+KTX

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