منذ بضعة أيام انتهى حدث 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);
}
}
}
}
كما نرى فإنه تم التعرف على كلمة لوحدها
مصادر قد تهمك
رابط المشروع على Github
التعليقات (5)
اكيد ، الSDK متوفرة أيضا للIOS بنفس الوظائف
احصل عندك اخوي رابط مشروع جاهز للIOS ؟
يعطيك العافية اخوي ماقصرت
لايوجد لديك حساب في عالم البرمجة؟
تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !