Nhận dạng văn bản trong hình ảnh bằng ML Kit trên Android

Bạn có thể sử dụng ML Kit để nhận dạng văn bản trong hình ảnh. ML Kit 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 hóa để 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à dựa trên đám mây. Nhận dạng văn bản tài liệu chỉ khả dụng dưới dạng mô hình dựa trên đám mây. Xem phần tổng quan để so sánh giữa mô hình đám mây và mô hình trên thiết bị.

Trước khi bắt đầu

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

    Để làm như vậy, hãy thêm phần khai báo sau vào tệp AndroidManifest.xml của ứng dụng của bạn:

    <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 bật tính năng tải xuống mô hình tại thời điểm cài đặt thì mô hình sẽ đượ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ị. Những yêu cầu bạn thực hiện trước khi quá trình tải xuống hoàn tất sẽ không mang lại kết quả.
  4. Nếu bạn muốn sử dụng mô hình dựa trên Đám mây và bạn chưa bật API dựa trên Đám mây cho dự án của mình, hãy thực hiện ngay bây giờ:

    1. Mở trang API ML Kit 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 định giá Blaze, hãy nhấp vào Nâng cấp để thực hiện. (Bạn sẽ chỉ được nhắc nâng cấp nếu dự án của bạn không nằm trong 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.

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

    Nếu chỉ muốn sử dụng kiểu máy 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.

Hướng dẫn nhập hình ảnh

  • Để Bộ ML 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 thể hiện bằng đủ dữ liệu pixel. Lý tưởng nhất là đối với văn bản Latinh, 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 hỗ trợ bởi API dựa trên đám mây), mỗi ký tự phải có kích thước 24x24 pixel. Đối với tất cả các ngôn ngữ, nhìn chung không có lợi ích về độ chính xác đối với các ký tự lớn hơn 24x24 pixel.

    Vì vậy, 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 được in trên giấy cỡ letter, có thể cần phải có hình ảnh 720x1280 pixel.

  • Lấy nét 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ả 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 văn bản trong ứng dụng thời gian thực, bạn cũng có thể muốn xem xét 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, do đó, để 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 càng nhiều diện tích hình ảnh càng tốt. Đồng thời xem Mẹo để cải thiện hiệu suất 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 mô hình trên thiết bị hoặc dựa trên đám mây, hãy chạy trình nhận dạng văn bản như 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ừ Bitmap , media.Image , ByteBuffer , mảng byte hoặc tệp trên thiết bị. Sau đó, chuyển đối tượng FirebaseVisionImage cho phương thức processImage của FirebaseVisionTextRecognizer .

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

    • Để tạo đối tượng FirebaseVisionImage từ đối tượng media.Image , chẳng hạn như khi chụp ảnh từ camera của thiết bị, hãy chuyển đối tượng media.Image và góc xoay của hình ảnh tới FirebaseVisionImage.fromMediaImage() .

      Nếu bạn sử dụng thư viện CameraX , các lớp OnImageCapturedListenerImageAnalysis.Analyzer sẽ tính toán giá trị xoay cho bạn, vì vậy bạn chỉ cần chuyển đổi phép xoay thành một trong các hằng số ROTATION_ của ML Kit 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 bạn không sử dụng thư viện camera cung cấp cho bạn góc quay của hình ảnh, bạn có thể tính toán nó từ góc quay của thiết bị và hướng của cảm biến camera 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 đó, chuyển đối tượng media.Image và giá trị xoay cho FirebaseVisionImage.fromMediaImage() :

      Java

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

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Để tạo đối tượng FirebaseVisionImage từ URI tệp, hãy chuyển ngữ cảnh ứng dụng và URI tệp tới FirebaseVisionImage.fromFilePath() . Điều này hữu ích khi bạn sử dụng ý định ACTION_GET_CONTENT để nhắc người dùng chọn hình ảnh từ ứ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()
      }
    • Để tạo đối tượng FirebaseVisionImage từ ByteBuffer hoặc mảng byte, trước tiên hãy tính toán góc xoay hình ảnh như mô tả ở trên cho đầu vào media.Image .

      Sau đó, tạo một đối tượng FirebaseVisionImageMetadata chứa chiều cao, chiều rộng, định dạng mã hóa 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+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 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+KTX

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

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

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

  2. Lấy một phiên bản của FirebaseVisionTextRecognizer .

    Để sử dụng mẫu trên thiết bị:

    Java

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

    Kotlin+KTX

    val detector = FirebaseVision.getInstance()
            .onDeviceTextRecognizer

    Để 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+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, chuyển hình ảnh sang 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ừ 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 trình xử lý 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 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 không hoặc nhiều đối tượng Element , đại diện cho các từ và các thực thể giống từ (ngày, số, v.v.).

Đối với mỗi đối tượng TextBlock , LineElement , bạn có thể nhận dạng văn bản trong vùng và tọa độ 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+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 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 ứng dụng 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 cuộc 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 đó xuống.
  • Nếu bạn đang sử dụng đầu ra của trình nhận dạng văn bản để phủ đồ họa lên hình ảnh đầu vào, trước tiên hãy lấy kết quả từ Bộ công cụ ML, sau đó kết xuất hình ảnh và lớp phủ trong một bước duy nhất. Bằng cách đó, bạn chỉ hiển thị trên bề mặt hiển thị một lần cho mỗi khung hình đầu vào.
  • 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ơn, hãy chụp ảnh ở định dạng ImageFormat.NV21 .

  • Hãy cân nhắc việc chụp ảnh ở độ phân giải thấp hơn. Tuy nhiên, cũng hãy ghi nhớ các yêu cầu về kích thước hình ảnh của API này.

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 trình nhận dạng văn bản tài liệu dựa trên đám mây như mô tả bên dưới.

API nhận dạng văn bản tài liệu, được mô tả bên dưới, cung cấp giao diện nhằm mục đích 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 thích giao diện do API FirebaseVisionTextRecognizer cung cấp, bạn có thể sử 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 .

Để 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ừ Bitmap , media.Image , ByteBuffer , mảng byte hoặc tệp trên thiết bị. Sau đó, chuyển đối tượng FirebaseVisionImage cho phương thức processImage của FirebaseVisionDocumentTextRecognizer .

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

    • Để tạo đối tượng FirebaseVisionImage từ đối tượng media.Image , chẳng hạn như khi chụp ảnh từ camera của thiết bị, hãy chuyển đối tượng media.Image và góc xoay của hình ảnh tới FirebaseVisionImage.fromMediaImage() .

      Nếu bạn sử dụng thư viện CameraX , các lớp OnImageCapturedListenerImageAnalysis.Analyzer sẽ tính toán giá trị xoay cho bạn, vì vậy bạn chỉ cần chuyển đổi phép xoay thành một trong các hằng số ROTATION_ của ML Kit 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 bạn không sử dụng thư viện camera cung cấp cho bạn góc quay của hình ảnh, bạn có thể tính toán nó từ góc quay của thiết bị và hướng của cảm biến camera 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 đó, chuyển đối tượng media.Image và giá trị xoay cho FirebaseVisionImage.fromMediaImage() :

      Java

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

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Để tạo đối tượng FirebaseVisionImage từ URI tệp, hãy chuyển ngữ cảnh ứng dụng và URI tệp tới FirebaseVisionImage.fromFilePath() . Điều này hữu ích khi bạn sử dụng ý định ACTION_GET_CONTENT để nhắc người dùng chọn hình ảnh từ ứ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()
      }
    • Để tạo đối tượng FirebaseVisionImage từ ByteBuffer hoặc mảng byte, trước tiên hãy tính toán góc xoay hình ảnh như mô tả ở trên cho đầu vào media.Image .

      Sau đó, tạo một đối tượng FirebaseVisionImageMetadata chứa chiều cao, chiều rộng, định dạng mã hóa 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+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 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+KTX

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

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

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

  2. Lấy một phiên bản 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, chuyển hình ảnh sang 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ừ 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, 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ệ thống phân cấp của các đối tượng phản ánh cấu trúc của tài liệu được nhận dạng:

Đối với mỗi đối tượng Block , Paragraph , WordSymbol , bạn có thể nhận dạng văn bản trong vùng và tọa độ 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+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
            }
        }
    }
}

Bước tiếp theo