يمكنك استخدام "حزمة تعلُّم الآلة" للتعرّف على النص في الصور. تتضمّن حزمة 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 فقط استخدام واجهات برمجة التطبيقات المستندة إلى Cloud.
- إذا لم تكن واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية مفعّلة، انقر على تفعيل واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية.
إذا كنت تريد استخدام النموذج على الجهاز فقط، يمكنك تخطّي هذه الخطوة.
أنت الآن جاهز لبدء التعرّف على النص في الصور.
إرشادات حول إدخال الصور
-
لكي تتمكّن حزمة ML Kit من التعرّف على النص بدقة، يجب أن تحتوي صور الإدخال على نص يمثّله بيانات بكسل كافية. من الأفضل أن يكون حجم كل حرف في النص اللاتيني 16×16 بكسل على الأقل. بالنسبة إلى النصوص باللغة الصينية واليابانية والكورية (التي تتيحها واجهات برمجة التطبيقات المستندة إلى السحابة الإلكترونية فقط)، يجب أن يكون حجم كل حرف 24×24 بكسل. بالنسبة إلى جميع اللغات، لا يُحسِّن بشكل عام حجم الحروف الأكبر من 24×24 بكسل من دقتها.
على سبيل المثال، قد تكون الصورة بحجم 640×480 مناسبة لمسح بطاقة تعريف ملف شخصي تشغل العرض الكامل للصورة ضوئيًا. لمسح مستند مطبوع على ورقة بحجم A4 ضوئيًا، قد تكون صورة بدقة 720×1280 بكسل مطلوبة.
-
يمكن أن يؤثّر تركيز الصورة السيئ في دقة التعرّف على النص. إذا لم يكن بإمكانك الحصول على نتائج مقبولة، حاوِل أن تطلب من المستخدم إعادة التقاط الصورة.
-
إذا كنت تتعرّف على النص في تطبيق يعمل في الوقت الفعلي، قد تحتاج أيضًا إلى مراعاة الأبعاد العامة للصور المُدخلة. يمكن معالجة الصور الأصغر حجمًا بشكل أسرع، لذا لتقليل وقت الاستجابة، يمكنك التقاط الصور بدقة أقل (مع مراعاة متطلبات الدقة المذكورة أعلاه) ومحاولة أن يشغل النص أكبر مساحة ممكنة من الصورة. اطّلِع أيضًا على نصائح لتحسين الأداء في الوقت الفعلي.
التعرّف على النص في الصور
للتعرّف على النص في صورة باستخدام نموذج على الجهاز أو نموذج مستند إلى السحابة الإلكترونية، يمكنك تشغيل معرّف النصوص كما هو موضّح أدناه.
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
من معرّف موارد منتظم لملف، عليك تمرير سياق التطبيق ومعرّف الموارد المنتظم للملف إلىFirebaseVisionImage.fromFilePath()
. يكون ذلك مفيدًا عند استخدام نيةACTION_GET_CONTENT
لطلب تحديد صورة من تطبيق معرض الصور.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 } } }
نصائح لتحسين الأداء في الوقت الفعلي
إذا كنت تريد استخدام النموذج على الجهاز لتمييز النص في تطبيق يعمل في الوقت الفعلي، اتّبِع الإرشادات التالية لتحقيق أفضل معدّلات لإطارات الفيديو:
- الحد من عدد المكالمات إلى معرّف النصوص إذا أصبح إطار فيديو جديد متاحًا أثناء تشغيل معرّف النصوص، أسقط الإطار.
- إذا كنت تستخدِم ناتج معرّف النصوص لوضع الرسومات على صورة الإدخال، احصل أولاً على النتيجة من ML Kit، ثمّ أعِد عرض الصورة والعنصر المتراكب في خطوة واحدة. وبذلك، يتم عرض المحتوى على سطح العرض مرّة واحدة فقط لكل إطار إدخال.
-
إذا كنت تستخدم واجهة برمجة التطبيقات Camera2 API، يمكنك التقاط الصور بتنسيق
ImageFormat.YUV_420_888
.إذا كنت تستخدم الإصدار القديم من Camera API، يمكنك التقاط الصور بتنسيق
ImageFormat.NV21
. - ننصحك بالتقاط الصور بدرجة دقة أقل. ومع ذلك، يجب أيضًا مراعاة متطلبات أبعاد الصورة في واجهة برمجة التطبيقات هذه.
الخطوات التالية
- قبل نشر تطبيق يستخدم واجهة برمجة تطبيقات Cloud في قناة الإصدار العلني، عليك اتّخاذ بعض الخطوات الإضافية لمنع أثر الوصول غير المصرّح به إلى واجهة برمجة التطبيقات والحدّ منه.
التعرّف على النص في صور المستندات
للتعرّف على نص مستند، عليك ضبط ميزة التعرّف على نص المستند المستندة إلى السحابة الإلكترونية وتشغيلها كما هو موضّح أدناه.
توفّر واجهة برمجة التطبيقات لميزة التعرّف على نص المستندات، الموضّحة أدناه، واجهة
مُعدّة لتسهيل العمل مع صور المستندات. ومع ذلك،
إذا كنت تفضّل الواجهة التي تقدّمها واجهة برمجة التطبيقات 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
من معرّف موارد منتظم لملف، عليك تمرير سياق التطبيق ومعرّف الموارد المنتظم للملف إلىFirebaseVisionImage.fromFilePath()
. يكون ذلك مفيدًا عند استخدام نيةACTION_GET_CONTENT
لطلب تحديد صورة من تطبيق معرض الصور.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 في قناة الإصدار العلني، عليك اتّخاذ بعض الخطوات الإضافية لمنع أثر الوصول غير المصرّح به إلى واجهة برمجة التطبيقات والحدّ منه.