يمكنك استخدام ML Kit للتعرّف على النص في الصور. تتضمّن حزمة تعلُّم الآلة واجهة برمجة تطبيقات عامة الأغراض مناسبة للتعرّف على النص في الصور، مثل نص لوحة إرشادية في الشارع، وواجهة برمجة تطبيقات محسَّنة للتعرّف على نص المستندات. تتضمّن واجهة برمجة التطبيقات للأغراض العامة نماذج على الجهاز ونماذج مستندة إلى السحابة الإلكترونية. لا يتوفّر التعرّف على نص المستند إلا كنموذج مستند إلى السحابة الإلكترونية. يمكنك الاطّلاع على النظرة العامة لمقارنة النماذج المستندة إلى السحابة الإلكترونية والنماذج التي تعمل على الجهاز.
قبل البدء
- أضِف Firebase إلى مشروع Android الخاص بك، في حال لم يسبق لك إجراء ذلك.
- أضِف العناصر التابعة لمكتبات 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' }
-
اختياري ولكن يُنصح به: إذا كنت تستخدم واجهة برمجة التطبيقات التي تعمل على الجهاز، عليك إعداد تطبيقك لتنزيل نموذج تعلُّم الآلة تلقائيًا على الجهاز بعد تثبيت تطبيقك من "متجر 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>
-
إذا كنت تريد استخدام النموذج المستند إلى السحابة الإلكترونية ولم يسبق لك تفعيل واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية لمشروعك، عليك إجراء ذلك الآن باتّباع الخطوات التالية:
- افتح صفحة واجهات برمجة التطبيقات في ML Kit ضمن Firebase.
-
إذا لم يسبق لك ترقية مشروعك إلى خطة أسعار Blaze، انقر على ترقية لإجراء ذلك. (لن يُطلب منك الترقية إلا إذا لم يكن مشروعك مشتركًا في خطة Blaze).
يمكن للمشاريع على مستوى Blaze فقط استخدام واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية.
- إذا لم تكن واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية مفعّلة، انقر على تفعيل واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية.
يمكنك تخطّي هذه الخطوة إذا كنت تريد استخدام النموذج على الجهاز فقط.
أنت الآن جاهز لبدء التعرّف على النص في الصور.
إرشادات حول الصور المدخَلة
-
لكي يتعرّف ML Kit على النص بدقة، يجب أن تحتوي الصور المدخلة على نص يمثّله عدد كافٍ من وحدات البكسل. من المفترض أن يكون حجم كل حرف 16×16 بكسل على الأقل بالنسبة إلى النصوص اللاتينية. بالنسبة إلى النصوص الصينية واليابانية والكورية (التي تتوافق فقط مع واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية)، يجب أن يكون حجم كل حرف 24 × 24 بكسل. وبالنسبة إلى جميع اللغات، لا تتحسّن الدقة بشكل عام إذا كان حجم الأحرف أكبر من 24 × 24 بكسل.
على سبيل المثال، قد تكون صورة بحجم 640x480 مناسبة لمسح بطاقة عمل ضوئيًا تشغل العرض الكامل للصورة. لمسح مستند ضوئيًا مطبوع على ورق بحجم Letter، قد تحتاج إلى صورة بحجم 720x1280 بكسل.
-
يمكن أن يؤدي عدم وضوح الصورة إلى انخفاض دقة التعرّف على النص. إذا لم تحصل على نتائج مقبولة، اطلب من المستخدم إعادة التقاط الصورة.
-
إذا كنت تستخدم تطبيقًا يتعرّف على النص في الوقت الفعلي، قد تحتاج أيضًا إلى مراعاة الأبعاد الإجمالية للصور المدخلة. يمكن معالجة الصور الأصغر حجمًا بشكل أسرع، لذا لتقليل وقت الاستجابة، التقط الصور بدقة أقل (مع مراعاة متطلبات الدقة المذكورة أعلاه) وتأكَّد من أنّ النص يشغل أكبر قدر ممكن من الصورة. يمكنك أيضًا الاطّلاع على نصائح لتحسين الأداء في الوقت الفعلي.
التعرّف على النص في الصور
للتعرّف على نص في صورة باستخدام نموذج على الجهاز أو نموذج مستند إلى السحابة الإلكترونية، شغِّل أداة التعرّف على النص كما هو موضّح أدناه.
1. تشغيل أداة التعرّف على النص
للتعرّف على نص في صورة، أنشئ عنصرFirebaseVisionImage
من Bitmap
أو media.Image
أو ByteBuffer
أو مصفوفة بايت أو ملف على
الجهاز. بعد ذلك، مرِّر الكائن FirebaseVisionImage
إلى الطريقة processImage
الخاصة بالكائن FirebaseVisionTextRecognizer
.
أنشئ عنصر
FirebaseVisionImage
من صورتك.-
لإنشاء عنصر
FirebaseVisionImage
من عنصرmedia.Image
، مثلاً عند التقاط صورة من كاميرا جهاز، مرِّر عنصرmedia.Image
وزاوية دوران الصورة إلىFirebaseVisionImage.fromMediaImage()
.إذا كنت تستخدم مكتبة CameraX، سيحسب لك الفئتان
OnImageCapturedListener
وImageAnalysis.Analyzer
قيمة الدوران، لذلك ما عليك سوى تحويل الدوران إلى أحد الثوابتROTATION_
في ML Kit قبل استدعاء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
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
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
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- لإنشاء عنصر
FirebaseVisionImage
من معرّف URI لملف، مرِّر سياق التطبيق ومعرّف URI للملف إلىFirebaseVisionImage.fromFilePath()
. ويكون ذلك مفيدًا عند استخدامACTION_GET_CONTENT
intent لطلب أن يختار المستخدم صورة من تطبيق المعرض.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- لإنشاء عنصر
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
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
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- لإنشاء كائن
FirebaseVisionImage
من كائنBitmap
، اتّبِع الخطوات التالية:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
في الوضع العمودي، بدون الحاجة إلى تدوير إضافي.
-
احصل على مثيل من
FirebaseVisionTextRecognizer
.لاستخدام النموذج على الجهاز، اتّبِع الخطوات التالية:
Java
FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance() .getOnDeviceTextRecognizer();
Kotlin
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
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()
أخيرًا، مرِّر الصورة إلى الطريقة
processImage
:Java
Task<FirebaseVisionText> result = detector.processImage(image) .addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() { @Override public void onSuccess(FirebaseVisionText firebaseVisionText) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin
val result = detector.processImage(image) .addOnSuccessListener { firebaseVisionText -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
2. استخراج النص من كتل النص التي تم التعرّف عليها
في حال نجاح عملية التعرّف على النص، سيتم تمرير عنصر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
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 } } }
نصائح لتحسين الأداء في الوقت الفعلي
إذا كنت تريد استخدام النموذج على الجهاز للتعرّف على النص في تطبيق يعمل في الوقت الفعلي، اتّبِع الإرشادات التالية لتحقيق أفضل معدّلات عرض اللقطات:
- تقليل عدد طلبات التعرّف على النص إذا توفّر إطار فيديو جديد أثناء تشغيل أداة التعرّف على النصوص، يجب تجاهل الإطار.
- إذا كنت تستخدم ناتج أداة التعرّف على النصوص لتراكب الرسومات على صورة الإدخال، احصل أولاً على النتيجة من "حزمة تعلُّم الآلة"، ثم اعرض الصورة والتراكب في خطوة واحدة. وبذلك، يتم العرض على مساحة العرض مرة واحدة فقط لكل إطار إدخال.
-
إذا كنت تستخدم Camera2 API، التقط الصور بتنسيق
ImageFormat.YUV_420_888
.إذا كنت تستخدم الإصدار القديم من Camera API، التقط الصور بتنسيق
ImageFormat.NV21
. - ننصحك بالتقاط الصور بدقة أقل. ومع ذلك، يجب أيضًا مراعاة متطلبات أبعاد الصورة في واجهة برمجة التطبيقات هذه.
الخطوات التالية
- قبل نشر تطبيق يستخدم إحدى واجهات Cloud API في مرحلة الإنتاج، عليك اتّخاذ بعض الخطوات الإضافية لمنع تأثير الوصول غير المصرّح به إلى واجهة برمجة التطبيقات والحدّ منه.
التعرّف على النص في صور المستندات
للتعرّف على نص مستند، اضبط مشغّل التعرّف على نص المستند المستند إلى السحابة الإلكترونية وشغّله كما هو موضّح أدناه.
توفّر واجهة برمجة التطبيقات للتعرّف على نص المستندات، الموضّحة أدناه، واجهة تهدف إلى تسهيل التعامل مع صور المستندات. ومع ذلك، إذا كنت تفضّل الواجهة التي توفّرها واجهة برمجة التطبيقات FirebaseVisionTextRecognizer
، يمكنك استخدامها بدلاً من ذلك لفحص المستندات من خلال ضبط أداة التعرّف على النص في السحابة الإلكترونية على استخدام نموذج النص الكثيف.
لاستخدام واجهة برمجة التطبيقات للتعرّف على نص المستند، اتّبِع الخطوات التالية:
1. تشغيل أداة التعرّف على النص
للتعرّف على نص في صورة، أنشئ عنصرFirebaseVisionImage
من Bitmap
أو media.Image
أو ByteBuffer
أو مصفوفة بايت أو ملف على الجهاز.
بعد ذلك، مرِّر الكائن FirebaseVisionImage
إلى الطريقة processImage
الخاصة بالكائن FirebaseVisionDocumentTextRecognizer
.
أنشئ عنصر
FirebaseVisionImage
من صورتك.-
لإنشاء عنصر
FirebaseVisionImage
من عنصرmedia.Image
، مثلاً عند التقاط صورة من كاميرا جهاز، مرِّر عنصرmedia.Image
وزاوية دوران الصورة إلىFirebaseVisionImage.fromMediaImage()
.إذا كنت تستخدم مكتبة CameraX، سيحسب لك الفئتان
OnImageCapturedListener
وImageAnalysis.Analyzer
قيمة الدوران، لذلك ما عليك سوى تحويل الدوران إلى أحد الثوابتROTATION_
في ML Kit قبل استدعاء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
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
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
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- لإنشاء عنصر
FirebaseVisionImage
من معرّف URI لملف، مرِّر سياق التطبيق ومعرّف URI للملف إلىFirebaseVisionImage.fromFilePath()
. ويكون ذلك مفيدًا عند استخدامACTION_GET_CONTENT
intent لطلب أن يختار المستخدم صورة من تطبيق المعرض.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- لإنشاء عنصر
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
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
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- لإنشاء كائن
FirebaseVisionImage
من كائنBitmap
، اتّبِع الخطوات التالية:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
في الوضع العمودي، بدون الحاجة إلى تدوير إضافي.
-
الحصول على مثيل من
FirebaseVisionDocumentTextRecognizer
:Java
FirebaseVisionDocumentTextRecognizer detector = FirebaseVision.getInstance() .getCloudDocumentTextRecognizer();
// Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages FirebaseVisionCloudDocumentRecognizerOptions options = new FirebaseVisionCloudDocumentRecognizerOptions.Builder() .setLanguageHints(Arrays.asList("en", "hi")) .build(); FirebaseVisionDocumentTextRecognizer detector = FirebaseVision.getInstance() .getCloudDocumentTextRecognizer(options);
Kotlin
val detector = FirebaseVision.getInstance() .cloudDocumentTextRecognizer
// Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages val options = FirebaseVisionCloudDocumentRecognizerOptions.Builder() .setLanguageHints(listOf("en", "hi")) .build() val detector = FirebaseVision.getInstance() .getCloudDocumentTextRecognizer(options)
أخيرًا، مرِّر الصورة إلى الطريقة
processImage
:Java
detector.processImage(myImage) .addOnSuccessListener(new OnSuccessListener<FirebaseVisionDocumentText>() { @Override public void onSuccess(FirebaseVisionDocumentText result) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin
detector.processImage(myImage) .addOnSuccessListener { firebaseVisionDocumentText -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
2. استخراج النص من كتل النص التي تم التعرّف عليها
في حال نجاح عملية التعرّف على النص، سيتم عرض عنصر
FirebaseVisionDocumentText
. يحتوي عنصر FirebaseVisionDocumentText
على النص الكامل الذي تم التعرّف عليه في الصورة وعلى تسلسل هرمي للعناصر يعكس بنية المستند الذي تم التعرّف عليه:
FirebaseVisionDocumentText.Block
FirebaseVisionDocumentText.Paragraph
FirebaseVisionDocumentText.Word
FirebaseVisionDocumentText.Symbol
بالنسبة إلى كل عنصر من عناصر 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
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 } } } }
الخطوات التالية
- قبل نشر تطبيق يستخدم إحدى واجهات Cloud API في مرحلة الإنتاج، عليك اتّخاذ بعض الخطوات الإضافية لمنع تأثير الوصول غير المصرّح به إلى واجهة برمجة التطبيقات والحدّ منه.