Firebase ML Kit | التعرف على النصوص

Firebase ML Kit Text Recognition

AbdulAlim Rajjoubمنذ 6 سنوات

منذ بضعة أيام انتهى حدث Google السنوي I/O 18 والذي أتى بالعديد من التقنيات الجديدة والتحسينات الكثيرة.
من بينها Firebase ML Kit,وهي SDK  Machine Learning للموبايل سواءً كان Android أو IOS.

ML Kit تقدم حالياً الخدمات التالية

  • Text Recongition التعرف على النصوص
  • Face Detection التعرف على الوجه
  • Image Labeling التعرف على الأشياء (أشخاص,نباتات, أماكن الخ..)
  • Barcode Scanning مسح الباركود
  • Landmark Recognition التعرف على المعالم

 بالإضافة الى امكانية استخدام أي Tensorflow Lite Model ,يمكنك رفعه الى Firebase ثم استخدامه على الجهاز

الآن جميع هذه الميزات يمكنها أن تعمل بطريقتين:

الأولى والتي سنشرحها في هذا الدرس وكافة الدروس القادمة وهي أن نقوم باستخدام هذه المزايا على الجهاز نفسه ,بدون استخدام الإنترنت او Cloud,وبالنسبة للنصوص فإنه يدعم فقط الأحرف اللاتينية,ممايعني أنه لا يدعم اللغة العربية

مزايا هذه الطريقة أنها لا تحتاج الى انترنت,فقط لأول مرة تقوم SDK بتحميلها تلقائياً ,كما أنها مجانية,ولكن عيب هذه الطريقة أنها قد لا تكون دقيقة بشكل كامل مثل Cloud

الثانية وهي باستخدام Cloud ,أي أنه عند تنفيذ أي أمر سيتم إرساله الى Cloud وسيعود بالنتيجة,ما يميزها هي الدقة التي تقدمها,ولكن للأسف فهي غير مجانية ,فيجب عليك ترقية مشروعك الFirebase الى Blaze Plan

سنقوم بهذا الشرح بأخذ صورة تحتوي على بعض النصوص وسنقوم باستخراج هذه النصوص ووضعها في TextView

يكفي كلاماً فلننتقل المشروع

بدايةً يجب عليك إنشاء مشروع على Firebase  ثم تقوم بربطه بتطبيقك الأندرويد(مثلما تفعل مع أي مشروع Firebase آخر)

ثم تقوم بإضافة مكتبة Text Recognition 

implementation 'com.google.firebase:firebase-ml-vision:15.0.0'

ونقوم بعمل Sync للمشروع

ثم نذهب الى Android Manifest ونضيف هذا السطر داخل وسم application لنجعل Firebase تقوم بتحميل الملفات اللازمة بعد تثبيت التطبيق

<meta-data
            android:name="com.google.firebase.ml.vision.DEPENDENCIES"
            android:value="text" />

سنقوم بإنشاء زر لاختيار الصورة من المعرض ونضع فوقه الصورة التي اختارها المستخدم  و TextView  النص الذي سيتم استخراجه في الأعلى

 

 

ونقوم بربطهم في MainActivity.java

 

private static final int PICK_IMG_REQUEST = 1458;
........................
        Button button = findViewById(R.id.btn);
        imageView = findViewById(R.id.img);
        textView = findViewById(R.id.textView);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                pickImageFromGallery();
            }
        });

وعند الضغط على الزر سنقوم باختيار الصورة من المعرض

private void pickImageFromGallery() {
        Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
        photoPickerIntent.setType("image/*");
        startActivityForResult(photoPickerIntent, PICK_IMG_REQUEST);
    }

ونستلم نتيجة اختيار الصورة عبر onActivityResult

 @Override
    protected void onActivityResult(int reqCode, int resultCode, Intent data) {
        super.onActivityResult(reqCode, resultCode, data);
        if (reqCode == PICK_IMG_REQUEST && resultCode == RESULT_OK) {

        }
}

الآن عند اختيار صورة سنقوم بأخذ مسارها عبر uri ونقوم بتحويلها الى Bitmap (يمكنك عدم استخدام Bitmap ولكنني سأحتاجها لاحقاً في هذا الدرس)

وقمنا باستخدام try catch في حال كانت الصورة غير موجودة او uri غير صحيح

 final Uri imageUri = data.getData();
            Bitmap bitmap = null;

            try {
                bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
            } catch (IOException e) {
                e.printStackTrace();
            }

الآن سنبدأ عملية استخراج النص ولهذا سننشئ FirebaseVisionImage  

يمكنك استخدام Bitmap او Uri (ملف) او ByteArray الخ..

FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

اذا اردت استخدام ملف بدل Bitmap فيمكنك بهذا الشكل

FirebaseVisionImage image = FirebaseVisionImage.fromFilePath(this, uri);

 

ثم نقوم بتعريف FirebaseVisionTextDetector والذي سيقوم بالتعرف على النصوص 

ثم نستدعي الميثود detectInImage لبدء عملية التعرف على النصوص,ونضيف Callback عند نجاح العملية

FirebaseVisionTextDetector detector = FirebaseVision.getInstance().getVisionTextDetector();
detector.detectInImage(image)
                    .addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() {
                        @Override
                        public void onSuccess(FirebaseVisionText firebaseVisionText) {

                        }
                    });

 عند نجاح العملية فإن Callback تعود ب FirebaseVisionText  والذي يحتوي على <List<Blocks.

كل Block يجتوي على :

Text النص الذي تم استخراجه بشكل كامل

BoundingBox وهي النقاط الذي تم التعرف فيها على النص (x,y) من الصورة

Lines يتم التعرف على النصوص بشكل أسطر, أي أنه في كل loop يتم التعرف على سطر واحد

 

سنقوم بعمل for loop على blocks (يمكنك استعمال Enhanced for loop ,ولكنني أحتاج index  لهذا استخدمت for العادية)

وقمنا بتعريف String فارغ قبل for loop وفي كل حلقة يقوم بإضافة النص الذي تم التعرف عليه الى هذا String

ثم نتحقق اذا كانت هذه الحلقة هي آخر حلقة عندها سنقوم بعمل setText لهذا String  ونقوم بوضع الصورة داخل ImageView

String text = "";
  for (int i = 0; i < firebaseVisionText.getBlocks().size(); i++) {

                 FirebaseVisionText.Block block = firebaseVisionText.getBlocks().get(i);
                 text += block.getText();

                  if (i == firebaseVisionText.getBlocks().size() -1) {
                       textView.setText(text);
                       imageView.setImageBitmap(bitmap);
                 }


}

نجرب تشغيل التطبيق ونختار صورة ما

 

كما نرى تم التعرف على النص ,ولكن ليست بالدقة الممتازة, فقد تعرف على أول حرف على أنه "O" بدل من "I" !

ماذا اذا أردنا ان نعرف ماهو النص الذي استطاع التعرف عليه بطريقة موضحة؟

سنقوم الآن بوضع مستطيل حول كل كلمة استطاع التعرف عليها وذلك عبر BoundingBox

بدايةً سنقوم بنسخ Bitmap الأصلية لأننا سنقوم بالتعديل عليها ونغير اسمها الى pickedBitmap (حيث أننا لا نستيطع التعديل مباشرة على Bitmap الأصلية)

ونقوم بتعريف Canvas وهو المكان الذي سيمكننا من رسم المستطيل عليه,ونعطيه mBitmap 

 try {
                pickedBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
                mBitmap = pickedBitmap.copy(Bitmap.Config.ARGB_8888, true);
            } catch (IOException e) {
                e.printStackTrace();
            }

            final Canvas canvas = new Canvas(mBitmap);

الآن داخل onSuccess سنعرف مايسمى ب Paint يمكنك أن تعتبره أنه شكل هذه المستطيل ولونه ,ولهذا سأقوم بإنشائه بميثود لوحده

 private Paint getPaint() {
        final Paint p = new Paint();
        p.setStyle(Paint.Style.STROKE);
        p.setAntiAlias(true);
        p.setFilterBitmap(true);
        p.setDither(true);
        p.setColor(Color.RED);
        return p;
    }

داخل for loop سنقوم برسم Rect المستطيل عبر block.getBoundingBox ونعطيه paint

وعند انتهاء ال for نقوم بعمل setImageBitmap ولكن هذه المرة للBitmap الجديدة

                                @Override
                                public void onSuccess(FirebaseVisionText firebaseVisionText) {
                                    Paint paint = getPaint();
                                    String text = "";
                                    for (int i = 0; i < firebaseVisionText.getBlocks().size(); i++) {

                                        FirebaseVisionText.Block block = firebaseVisionText.getBlocks().get(i);
                                        canvas.drawRect(block.getBoundingBox(), paint);
                                        text += block.getText();


                                        if (i == firebaseVisionText.getBlocks().size() - 1) {
                                            textView.setText(text);
                                            imageView.setImageBitmap(mBitmap);
                                        }

                                    }

نعيد تشغيل التطبيق 

 

وكما نرى تم وضع مستطيل حول الblocks الذي تم التعرف عليها 

 

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

  for (FirebaseVisionText.Block block : firebaseVisionText.getBlocks()) {


            for (int i = 0; i < block.getLines().size(); i++) {
                FirebaseVisionText.Line line = block.getLines().get(i);
                text += line.getText();
                canvas.drawRect(line.getBoundingBox(), paint);


                if (i == block.getLines().size() - 1) {
                    textView.setText(text);
                    imageView.setImageBitmap(mBitmap);
                }


            }
        }

نشغل التطبيق لنرى النتيجة

 

 

كل Line يحتوي على Element ,وElement هو كل حرف أو كل كلمة مفصولة بمسافة ,ستتضح لك الفكرة بالصورة القادمة

 

 for (FirebaseVisionText.Block block : firebaseVisionText.getBlocks()) {
            for (FirebaseVisionText.Line line : block.getLines()) {
                for (int i = 0; i < line.getElements().size(); i++) {
                    FirebaseVisionText.Element element = line.getElements().get(i);
                    text += element.getText();
                    canvas.drawRect(element.getBoundingBox(), paint);

                    if (i == line.getElements().size() - 1) {
                        textView.setText(text);
                        imageView.setImageBitmap(mBitmap);
                    }

                }
            }
        }

 

كما نرى فإنه تم التعرف على كلمة لوحدها 

 

 

مصادر قد تهمك

1 2

رابط المشروع على Github

 

كلمات دليلية: firebase mlkit text android
4
إعجاب
5731
مشاهدات
0
مشاركة
3
متابع
متميز
محتوى رهيب

التعليقات (5)

عبدالرحمن الخالدي:

شرح ممتاز الله يعطيك العافية.

لكن هل يوجد لل IOS ؟

AbdulAlim Rajjoub:

اكيد ، الSDK متوفرة أيضا للIOS بنفس الوظائف

https://firebase.google.com/docs/ml-kit/ios/recognize-text

عبدالرحمن الخالدي:

احصل عندك اخوي رابط مشروع جاهز للIOS ؟

AbdulAlim Rajjoub: https://github.com/chavitos/MLKit-TextRecognizer

 

عبدالرحمن الخالدي:

يعطيك العافية اخوي ماقصرت

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !