التعرّف على النص في الصور باستخدام حزمة تعلُّم الآلة على Android

يمكنك استخدام حزمة تعلُّم الآلة للتعرّف على النص في الصور. تحتوي ML Kit على تناسب واجهة برمجة التطبيقات للأغراض العامة التعرف على النص في الصور، مثل ونص لافتة الشارع وواجهة برمجة تطبيقات محسنة للتعرف على نصوص المستندات. تحتوي واجهة برمجة التطبيقات للأغراض العامة على نماذج على الجهاز ونماذج مستنِدة إلى السحابة الإلكترونية. لا يتوفر التعرف على نص المستند إلا كنموذج مستند إلى السحابة الإلكترونية. يمكنك الاطّلاع على نظرة عامة لمقارنة السحابة الإلكترونية والنماذج المتوفّرة على الجهاز فقط.

قبل البدء

  1. إذا لم تكن قد فعلت ذلك بالفعل، إضافة Firebase إلى مشروع Android
  2. إضافة الموارد التابعة لمكتبات ML Kit على Android إلى الوحدة (على مستوى التطبيق) ملف Gradle (عادةً 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. إجراء اختياري ولكنّنا ننصح به: إذا كنت تستخدم واجهة برمجة التطبيقات على الجهاز، اضبط تطبيقًا لتنزيل نموذج تعلُّم الآلة تلقائيًا على الجهاز بعد إنشاء التطبيقات المثبّتة من متجر Play.

    لإجراء ذلك، يُرجى إضافة البيان التالي إلى صفحة تطبيقك. ملف 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>
    في حال عدم تفعيل عمليات تنزيل نموذج وقت التثبيت، سيتم تنفيذ تم تنزيله في المرة الأولى التي تُشغِّل فيها أداة الرصد على الجهاز. الطلبات التي تقدّمها قبل اكتمال التنزيل، فلن تظهر أي نتائج.
  4. إذا أردت استخدام النموذج المستند إلى السحابة الإلكترونية، ولم يسبق لك تفعيل لواجهات برمجة التطبيقات القائمة على السحابة الإلكترونية لمشروعك، عليك القيام بذلك الآن:

    1. فتح ML Kit صفحة واجهات برمجة التطبيقات في وحدة تحكُّم Firebase.
    2. إذا لم تكن قد أجريت ترقية لمشروعك إلى خطة أسعار Blaze، انقر على يجب الترقية لإجراء ذلك. (ستتم مطالبتك بالترقية فقط إذا كان مشروعك ليس على خطة Blaze).

      يمكن للمشروعات على مستوى Blaze فقط استخدام واجهات برمجة التطبيقات المستنِدة إلى السحابة الإلكترونية.

    3. إذا لم تكن واجهات برمجة التطبيقات المستنِدة إلى السحابة الإلكترونية مُفعَّلة، انقر على تفعيل البيانات المستندة إلى السحابة الإلكترونية. API.

    إذا كنت تريد استخدام النموذج على الجهاز فقط، يمكنك تخطّي هذه الخطوة.

أنت الآن جاهز لبدء التعرف على النص في الصور.

إرشادات إدخال الصور

  • لكي تتعرّف أداة تعلّم الآلة على النص بدقة، يجب أن تحتوي الصور المدخلة على نص يتم تمثيله ببيانات بكسل كافية. من الناحية المثالية، للّغة اللاتينية نص، يجب أن يبلغ حجم كل حرف 16×16 بكسل على الأقل. بالنسبة إلى اللغة الصينية، النصوص اليابانية والكورية (المدعومة فقط من خلال واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية)، يجب أن يكون حجم الحرف 24×24 بكسل. بالنسبة لجميع اللغات، لا يوجد عمومًا على الدقة المحدد للأحرف الأكبر من 24×24 بكسل.

    لذلك، على سبيل المثال، قد تعمل صورة بحجم 640×480 جيدًا لمسح بطاقة عمل ضوئيًا تشغل العرض الكامل للصورة لإجراء مسح ضوئي لمستند مطبوع على ورق بحجم حرف، فقد يلزم صورة 720×1280 بكسل.

  • يمكن أن يؤدي التركيز الضعيف للصورة إلى التأثير سلبًا في دقة التعرّف على النص. إذا لم تكن كذلك والحصول على نتائج مقبولة، فحاول أن تطلب من المستخدم تلخيص الصورة.

  • إذا كنت تتعرف على النص في تطبيق في الوقت الفعلي، فيمكنك أيضًا يريدون في الاعتبار الأبعاد الكلية لصور الإدخال. أصغر يمكن معالجة الصور بشكل أسرع، لذلك لتقليل وقت الاستجابة، التقط الصور درجات دقة أقل (مع الأخذ في الاعتبار متطلبات الدقة المذكورة أعلاه) التأكد من أن النص يشغل أكبر قدر ممكن من الصورة. راجع أيضًا نصائح لتحسين الأداء في الوقت الفعلي.


التعرّف على النص في الصور

للتعرّف على النص في صورة باستخدام نموذج على الجهاز أو نموذج مستند إلى السحابة، تشغيل أداة التعرف على النص كما هو موضح أدناه.

1- تشغيل أداة التعرّف على النص

للتعرّف على نص في صورة، عليك إنشاء عنصر FirebaseVisionImage. من Bitmap أو media.Image أو ByteBuffer أو مصفوفة بايت أو ملف على الجهاز. مرِّر بعد ذلك الكائن FirebaseVisionImage إلى طريقة processImage لـ FirebaseVisionTextRecognizer.

  1. أنشئ عنصر FirebaseVisionImage من صورتك.

    • لإنشاء عنصر FirebaseVisionImage من كائن media.Image، مثل عند التقاط صورة من كاميرا الجهاز، يُرجى تمرير كائن media.Image تدوير إلى FirebaseVisionImage.fromMediaImage().

      إذا كنت تستخدم CameraX وOnImageCapturedListener تحتسب صفوف ImageAnalysis.Analyzer قيمة عرض الإعلانات بالتناوب. لك، لذا ما عليك سوى تحويل الدوران إلى إحدى أدوات تعلّم الآلة ROTATION_ ثابت قبل إجراء الطلب 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
                  // ...
              }
          }
      }

      إذا لم تكن تستخدم مكتبة كاميرا تمنحك تدوير الصورة، يمكنك من دوران الجهاز واتجاه الكاميرا جهاز الاستشعار في الجهاز:

      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
      }

      بعد ذلك، مرِّر الكائن media.Image قيمة التدوير إلى FirebaseVisionImage.fromMediaImage():

      Java

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

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • لإنشاء كائن FirebaseVisionImage من معرّف موارد منتظم (URI) لملف، مرِّر سياق التطبيق ومعرّف الموارد المنتظم (URI) للملف FirebaseVisionImage.fromFilePath() يكون ذلك مفيدًا عندما يجب استخدام هدف ACTION_GET_CONTENT لتطلب من المستخدم الاختيار. صورة من تطبيق المعرض الخاص به.

      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()
      }
    • لإنشاء عنصر FirebaseVisionImage من ByteBuffer أو صفيف بايت، احسب الصورة أولاً تدوير كما هو موضح أعلاه لإدخال media.Image.

      بعد ذلك، يمكنك إنشاء كائن FirebaseVisionImageMetadata. يتضمن ارتفاع الصورة وعرضها وتنسيق ترميز الألوان لها وتدوير:

      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()

      استخدم المخزن المؤقت أو الصفيفة وكائن البيانات الوصفية لإنشاء كائن 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)
    • لإنشاء عنصر FirebaseVisionImage من كائن Bitmap:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      يجب أن تكون الصورة التي يمثّلها الكائن Bitmap مستقيمًا، دون الحاجة إلى دوران إضافي.

  2. الحصول على مثال FirebaseVisionTextRecognizer

    لاستخدام النموذج على الجهاز فقط:

    Java

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

    Kotlin+KTX

    val detector = FirebaseVision.getInstance()
            .onDeviceTextRecognizer

    لاستخدام النموذج المستند إلى السحابة الإلكترونية:

    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. أخيرًا، ضع الصورة في طريقة 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- استخراج النص من مجموعات النصوص التي تم التعرّف عليها

إذا نجحت عملية التعرّف على النص، فسيتم سيتم تمرير الكائن FirebaseVisionText بنجاح. المستمع. يحتوي كائن FirebaseVisionText على النص الكامل الذي تم التعرّف عليه في الصورة وصفر أو أكثر من عناصر TextBlock.

يمثل كل TextBlock كتلة نص مستطيلة تحتوي على صفر أو المزيد من عناصر Line. كل عنصر Line يحتوي على صفر أو أكثر كائنات Element، التي تمثل كلمات وتشبه الكلمات الكيانات (التواريخ والأرقام وما إلى ذلك).

بالنسبة إلى كل كائن TextBlock وLine وElement، يمكنك الحصول على النص المعترف بها في المنطقة وإحداثيات الحدود للمنطقة.

على سبيل المثال:

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
        }
    }
}

نصائح لتحسين الأداء في الوقت الفعلي

إذا أردت استخدام النموذج على الجهاز فقط للتعرّف على النص في الوقت الفعلي التطبيق، يُرجى اتباع الإرشادات التالية لتحقيق أفضل معدلات عرض الإطارات:

  • تقييد الطلبات إلى أداة التعرّف على النص. إذا أصبح إطار فيديو جديد يكون متاحًا أثناء تشغيل أداة التعرّف على النص، أفلِت الإطار.
  • إذا كنت تستخدم ناتج أداة التعرف على النص لتراكب الرسومات على الصورة المدخلة، والحصول أولاً على النتيجة من ML Kit، ثم عرض الصورة وتراكبها في خطوة واحدة. ومن خلال القيام بذلك، يمكنك العرض على سطح الشاشة مرة واحدة فقط لكل إطار إدخال
  • في حال استخدام واجهة برمجة التطبيقات Camera2 API، يمكنك التقاط الصور في تنسيق ImageFormat.YUV_420_888

    إذا كنت تستخدم واجهة برمجة التطبيقات للكاميرا القديمة، يمكنك التقاط الصور في تنسيق ImageFormat.NV21

  • يمكنك التقاط صور بدقة أقل. ومع ذلك، ضع في اعتبارك أيضًا متطلبات أبعاد الصورة في واجهة برمجة التطبيقات هذه.

الخطوات التالية


التعرّف على النص في صور المستندات

للتعرف على نص المستند، قم بتهيئة وتشغيل أداة التعرّف على النص في المستند كما هو موضّح أدناه.

وتوفر واجهة برمجة التطبيقات للتعرف على نص المستندات، الموضحة أدناه، واجهة أن يكون أكثر ملاءمة للعمل على صور المستندات. ومع ذلك، إذا كنت تفضّل استخدام الواجهة التي توفّرها واجهة برمجة التطبيقات FirebaseVisionTextRecognizer يمكنك استخدامه بدلاً من ذلك لمسح المستندات ضوئيًا من خلال ضبط إعدادات النصوص على السحابة الإلكترونية أداة التعرف على استخدام نموذج النص الكثيف.

لاستخدام واجهة برمجة تطبيقات التعرف على النص في المستندات:

1- تشغيل أداة التعرّف على النص

للتعرّف على النص في صورة، أنشِئ كائن FirebaseVisionImage من أي مما يلي: Bitmap أو media.Image أو ByteBuffer أو مصفوفة بايت أو ملف على الجهاز. مرِّر بعد ذلك الكائن FirebaseVisionImage إلى طريقة processImage لـ FirebaseVisionDocumentTextRecognizer.

  1. أنشئ عنصر FirebaseVisionImage من صورتك.

    • لإنشاء عنصر FirebaseVisionImage من كائن media.Image، مثل عند التقاط صورة من كاميرا الجهاز، يُرجى تمرير كائن media.Image تدوير إلى FirebaseVisionImage.fromMediaImage().

      إذا كنت تستخدم CameraX وOnImageCapturedListener تحتسب صفوف ImageAnalysis.Analyzer قيمة عرض الإعلانات بالتناوب. لك، لذا ما عليك سوى تحويل الدوران إلى إحدى أدوات تعلّم الآلة ROTATION_ ثابت قبل إجراء الطلب 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
                  // ...
              }
          }
      }

      إذا لم تكن تستخدم مكتبة كاميرا تمنحك تدوير الصورة، يمكنك من دوران الجهاز واتجاه الكاميرا جهاز الاستشعار في الجهاز:

      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
      }

      بعد ذلك، مرِّر الكائن media.Image قيمة التدوير إلى FirebaseVisionImage.fromMediaImage():

      Java

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

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • لإنشاء كائن FirebaseVisionImage من معرّف موارد منتظم (URI) لملف، مرِّر سياق التطبيق ومعرّف الموارد المنتظم (URI) للملف FirebaseVisionImage.fromFilePath() يكون ذلك مفيدًا عندما يجب استخدام هدف ACTION_GET_CONTENT لتطلب من المستخدم الاختيار. صورة من تطبيق المعرض الخاص به.

      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()
      }
    • لإنشاء عنصر FirebaseVisionImage من ByteBuffer أو صفيف بايت، احسب الصورة أولاً تدوير كما هو موضح أعلاه لإدخال media.Image.

      بعد ذلك، يمكنك إنشاء كائن FirebaseVisionImageMetadata. يتضمن ارتفاع الصورة وعرضها وتنسيق ترميز الألوان لها وتدوير:

      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()

      استخدم المخزن المؤقت أو الصفيفة وكائن البيانات الوصفية لإنشاء كائن 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)
    • لإنشاء عنصر FirebaseVisionImage من كائن Bitmap:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      يجب أن تكون الصورة التي يمثّلها الكائن Bitmap مستقيمًا، دون الحاجة إلى دوران إضافي.

  2. الحصول على مثال 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. أخيرًا، ضع الصورة في طريقة 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- استخراج النص من مجموعات النصوص التي تم التعرّف عليها

إذا نجحت عملية التعرف على النص، فستعرض FirebaseVisionDocumentText. حاسمة يحتوي كائن FirebaseVisionDocumentText على النص الكامل الذي تم التعرّف عليه في وتدرج هرمي للكائنات التي تعكس بنية المستند:

بالنسبة إلى كل عناصر Block وParagraph وWord وSymbol، يمكنك الحصول على للنص المعترف به في المنطقة وإحداثيات الإحاطة بهذه المنطقة.

على سبيل المثال:

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
            }
        }
    }
}

الخطوات التالية