لاستدعاء Google Cloud API من تطبيقك، تحتاج إلى إنشاء واجهة REST API وسيطة تتعامل مع التفويض وتحمي القيم السرية مثل مفاتيح واجهة برمجة التطبيقات. ستحتاج بعد ذلك إلى كتابة التعليمات البرمجية في تطبيق الهاتف المحمول الخاص بك للمصادقة على هذه الخدمة الوسيطة والتواصل معها.
تتمثل إحدى طرق إنشاء REST API في استخدام مصادقة Firebase ووظائفها، والتي تمنحك بوابة مُدارة بدون خادم إلى Google Cloud APIs التي تتعامل مع المصادقة ويمكن استدعاؤها من تطبيق الهاتف المحمول الخاص بك باستخدام حزم SDK المُنشأة مسبقًا.
يوضح هذا الدليل كيفية استخدام هذه التقنية لاستدعاء Cloud Vision API من تطبيقك. ستسمح هذه الطريقة لجميع المستخدمين المعتمدين بالوصول إلى خدمات Cloud Vision التي تتم فوترتها من خلال مشروع Cloud الخاص بك، لذا فكر فيما إذا كانت آلية المصادقة هذه كافية لحالة الاستخدام الخاصة بك قبل المتابعة.
قبل ان تبدأ
تكوين المشروع الخاص بك
- إذا لم تكن قد قمت بذلك بالفعل، فأضف Firebase إلى مشروع Android الخاص بك .
إذا لم تكن قد قمت بالفعل بتمكين واجهات برمجة التطبيقات المستندة إلى السحابة لمشروعك، فقم بذلك الآن:
- افتح صفحة Firebase ML APIs لوحدة تحكم Firebase.
إذا لم تكن قد قمت بالفعل بترقية مشروعك إلى خطة تسعير Blaze، فانقر فوق ترقية للقيام بذلك. (سيُطلب منك الترقية فقط إذا لم يكن مشروعك مدرجًا في خطة Blaze.)
يمكن فقط للمشاريع على مستوى Blaze استخدام واجهات برمجة التطبيقات المستندة إلى السحابة.
- إذا لم تكن واجهات برمجة التطبيقات المستندة إلى السحابة ممكّنة بالفعل، فانقر على تمكين واجهات برمجة التطبيقات المستندة إلى السحابة .
- قم بتكوين مفاتيح Firebase API الموجودة لديك لمنع الوصول إلى Cloud Vision API:
- افتح صفحة بيانات الاعتماد الخاصة بوحدة التحكم السحابية.
- بالنسبة لكل مفتاح API في القائمة، افتح طريقة عرض التحرير، وفي قسم قيود المفاتيح، أضف جميع واجهات برمجة التطبيقات المتاحة باستثناء Cloud Vision API إلى القائمة.
نشر الدالة القابلة للاستدعاء
بعد ذلك، قم بنشر وظيفة السحابة التي ستستخدمها لربط تطبيقك وCloud Vision API. يحتوي مستودع functions-samples
على مثال يمكنك استخدامه.
افتراضيًا، سيسمح الوصول إلى Cloud Vision API من خلال هذه الوظيفة فقط للمستخدمين المعتمدين لتطبيقك بالوصول إلى Cloud Vision API. يمكنك تعديل الوظيفة لمتطلبات مختلفة.
لنشر الوظيفة:
- انسخ أو قم بتنزيل مستودع عينات الوظائف وقم بالتغيير إلى دليل
Node-1st-gen/vision-annotate-image
:git clone https://github.com/firebase/functions-samples
cd Node-1st-gen/vision-annotate-image
- تثبيت التبعيات:
cd functions
npm install
cd ..
- إذا لم يكن لديك Firebase CLI، فقم بتثبيته .
- قم بتهيئة مشروع Firebase في دليل
vision-annotate-image
. عندما يُطلب منك ذلك، حدد مشروعك في القائمة.firebase init
- نشر الوظيفة:
firebase deploy --only functions:annotateImage
أضف مصادقة Firebase إلى تطبيقك
سترفض الوظيفة القابلة للاستدعاء المنشورة أعلاه أي طلب من المستخدمين غير المصادق عليهم لتطبيقك. إذا لم تكن قد قمت بذلك بالفعل، فستحتاج إلى إضافة Firebase Auth إلى تطبيقك.
أضف التبعيات الضرورية إلى تطبيقك
<project>/<app-module>/build.gradle.kts
أو <project>/<app-module>/build.gradle
):implementation("com.google.firebase:firebase-functions:20.4.0") implementation("com.google.code.gson:gson:2.8.6")
أنت الآن جاهز لبدء التعرف على النص في الصور.
1. قم بإعداد صورة الإدخال
من أجل الاتصال بـ Cloud Vision، يجب تنسيق الصورة كسلسلة بترميز base64. لمعالجة صورة من ملف محفوظ URI:- الحصول على الصورة ككائن
Bitmap
:Kotlin+KTX
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
Java
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
- اختياريًا، قم بتصغير الصورة لحفظها في النطاق الترددي. راجع أحجام الصور الموصى بها من Cloud Vision.
Kotlin+KTX
private fun scaleBitmapDown(bitmap: Bitmap, maxDimension: Int): Bitmap { val originalWidth = bitmap.width val originalHeight = bitmap.height var resizedWidth = maxDimension var resizedHeight = maxDimension if (originalHeight > originalWidth) { resizedHeight = maxDimension resizedWidth = (resizedHeight * originalWidth.toFloat() / originalHeight.toFloat()).toInt() } else if (originalWidth > originalHeight) { resizedWidth = maxDimension resizedHeight = (resizedWidth * originalHeight.toFloat() / originalWidth.toFloat()).toInt() } else if (originalHeight == originalWidth) { resizedHeight = maxDimension resizedWidth = maxDimension } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false) }
Java
private Bitmap scaleBitmapDown(Bitmap bitmap, int maxDimension) { int originalWidth = bitmap.getWidth(); int originalHeight = bitmap.getHeight(); int resizedWidth = maxDimension; int resizedHeight = maxDimension; if (originalHeight > originalWidth) { resizedHeight = maxDimension; resizedWidth = (int) (resizedHeight * (float) originalWidth / (float) originalHeight); } else if (originalWidth > originalHeight) { resizedWidth = maxDimension; resizedHeight = (int) (resizedWidth * (float) originalHeight / (float) originalWidth); } else if (originalHeight == originalWidth) { resizedHeight = maxDimension; resizedWidth = maxDimension; } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false); }
Kotlin+KTX
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640)
Java
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640);
- تحويل كائن الصورة النقطية إلى سلسلة مشفرة base64:
Kotlin+KTX
// Convert bitmap to base64 encoded string val byteArrayOutputStream = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val imageBytes: ByteArray = byteArrayOutputStream.toByteArray() val base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP)
Java
// Convert bitmap to base64 encoded string ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream); byte[] imageBytes = byteArrayOutputStream.toByteArray(); String base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP);
يجب أن تكون الصورة التي يمثلها كائن
Bitmap
في وضع مستقيم، دون الحاجة إلى تدوير إضافي. 2. قم باستدعاء الوظيفة القابلة للاستدعاء للتعرف على النص
للتعرف على النص في صورة ما، قم باستدعاء الوظيفة القابلة للاستدعاء، وتمرير طلب JSON Cloud Vision .
أولاً، قم بتهيئة مثيل Cloud Functions:
Kotlin+KTX
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functions
Java
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();
تحديد طريقة لاستدعاء الوظيفة:
Kotlin+KTX
private fun annotateImage(requestJson: String): Task<JsonElement> { return functions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith { task -> // This continuation runs on either success or failure, but if the task // has failed then result will throw an Exception which will be // propagated down. val result = task.result?.data JsonParser.parseString(Gson().toJson(result)) } }
Java
private Task<JsonElement> annotateImage(String requestJson) { return mFunctions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith(new Continuation<HttpsCallableResult, JsonElement>() { @Override public JsonElement then(@NonNull Task<HttpsCallableResult> task) { // This continuation runs on either success or failure, but if the task // has failed then getResult() will throw an Exception which will be // propagated down. return JsonParser.parseString(new Gson().toJson(task.getResult().getData())); } }); }
قم بإنشاء طلب JSON. تدعم Cloud Vision API نوعين من اكتشاف النص:
TEXT_DETECTION
وDOCUMENT_TEXT_DETECTION
. راجع مستندات Cloud Vision OCR لمعرفة الفرق بين حالتي الاستخدام.Kotlin+KTX
// Create json request to cloud vision val request = JsonObject() // Add image to request val image = JsonObject() image.add("content", JsonPrimitive(base64encoded)) request.add("image", image) // Add features to the request val feature = JsonObject() feature.add("type", JsonPrimitive("TEXT_DETECTION")) // Alternatively, for DOCUMENT_TEXT_DETECTION: // feature.add("type", JsonPrimitive("DOCUMENT_TEXT_DETECTION")) val features = JsonArray() features.add(feature) request.add("features", features)
Java
// Create json request to cloud vision JsonObject request = new JsonObject(); // Add image to request JsonObject image = new JsonObject(); image.add("content", new JsonPrimitive(base64encoded)); request.add("image", image); //Add features to the request JsonObject feature = new JsonObject(); feature.add("type", new JsonPrimitive("TEXT_DETECTION")); // Alternatively, for DOCUMENT_TEXT_DETECTION: //feature.add("type", new JsonPrimitive("DOCUMENT_TEXT_DETECTION")); JsonArray features = new JsonArray(); features.add(feature); request.add("features", features);
بشكل اختياري، قم بتوفير تلميحات اللغة للمساعدة في اكتشاف اللغة (راجع اللغات المدعومة ):
Kotlin+KTX
val imageContext = JsonObject() val languageHints = JsonArray() languageHints.add("en") imageContext.add("languageHints", languageHints) request.add("imageContext", imageContext)
Java
JsonObject imageContext = new JsonObject(); JsonArray languageHints = new JsonArray(); languageHints.add("en"); imageContext.add("languageHints", languageHints); request.add("imageContext", imageContext);
أخيرًا، قم باستدعاء الدالة:
Kotlin+KTX
annotateImage(request.toString()) .addOnCompleteListener { task -> if (!task.isSuccessful) { // Task failed with an exception // ... } else { // Task completed successfully // ... } }
Java
annotateImage(request.toString()) .addOnCompleteListener(new OnCompleteListener<JsonElement>() { @Override public void onComplete(@NonNull Task<JsonElement> task) { if (!task.isSuccessful()) { // Task failed with an exception // ... } else { // Task completed successfully // ... } } });
3. استخرج النص من كتل النص التي تم التعرف عليها
إذا نجحت عملية التعرف على النص، فسيتم إرجاع استجابة JSON الخاصة بـ BatchAnnotateImagesResponse في نتيجة المهمة. يمكن العثور على التعليقات التوضيحية النصية في كائنfullTextAnnotation
. يمكنك الحصول على النص الذي تم التعرف عليه كسلسلة في حقل text
. على سبيل المثال:
Kotlin+KTX
val annotation = task.result!!.asJsonArray[0].asJsonObject["fullTextAnnotation"].asJsonObject
System.out.format("%nComplete annotation:")
System.out.format("%n%s", annotation["text"].asString)
Java
JsonObject annotation = task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("fullTextAnnotation").getAsJsonObject();
System.out.format("%nComplete annotation:%n");
System.out.format("%s%n", annotation.get("text").getAsString());
يمكنك أيضًا الحصول على معلومات خاصة بمناطق الصورة. بالنسبة لكل block
paragraph
word
symbol
، يمكنك التعرف على النص في المنطقة والإحداثيات المحيطة بالمنطقة. على سبيل المثال:
Kotlin+KTX
for (page in annotation["pages"].asJsonArray) {
var pageText = ""
for (block in page.asJsonObject["blocks"].asJsonArray) {
var blockText = ""
for (para in block.asJsonObject["paragraphs"].asJsonArray) {
var paraText = ""
for (word in para.asJsonObject["words"].asJsonArray) {
var wordText = ""
for (symbol in word.asJsonObject["symbols"].asJsonArray) {
wordText += symbol.asJsonObject["text"].asString
System.out.format(
"Symbol text: %s (confidence: %f)%n",
symbol.asJsonObject["text"].asString,
symbol.asJsonObject["confidence"].asFloat,
)
}
System.out.format(
"Word text: %s (confidence: %f)%n%n",
wordText,
word.asJsonObject["confidence"].asFloat,
)
System.out.format("Word bounding box: %s%n", word.asJsonObject["boundingBox"])
paraText = String.format("%s%s ", paraText, wordText)
}
System.out.format("%nParagraph: %n%s%n", paraText)
System.out.format("Paragraph bounding box: %s%n", para.asJsonObject["boundingBox"])
System.out.format("Paragraph Confidence: %f%n", para.asJsonObject["confidence"].asFloat)
blockText += paraText
}
pageText += blockText
}
}
Java
for (JsonElement page : annotation.get("pages").getAsJsonArray()) {
StringBuilder pageText = new StringBuilder();
for (JsonElement block : page.getAsJsonObject().get("blocks").getAsJsonArray()) {
StringBuilder blockText = new StringBuilder();
for (JsonElement para : block.getAsJsonObject().get("paragraphs").getAsJsonArray()) {
StringBuilder paraText = new StringBuilder();
for (JsonElement word : para.getAsJsonObject().get("words").getAsJsonArray()) {
StringBuilder wordText = new StringBuilder();
for (JsonElement symbol : word.getAsJsonObject().get("symbols").getAsJsonArray()) {
wordText.append(symbol.getAsJsonObject().get("text").getAsString());
System.out.format("Symbol text: %s (confidence: %f)%n", symbol.getAsJsonObject().get("text").getAsString(), symbol.getAsJsonObject().get("confidence").getAsFloat());
}
System.out.format("Word text: %s (confidence: %f)%n%n", wordText.toString(), word.getAsJsonObject().get("confidence").getAsFloat());
System.out.format("Word bounding box: %s%n", word.getAsJsonObject().get("boundingBox"));
paraText.append(wordText.toString()).append(" ");
}
System.out.format("%nParagraph:%n%s%n", paraText);
System.out.format("Paragraph bounding box: %s%n", para.getAsJsonObject().get("boundingBox"));
System.out.format("Paragraph Confidence: %f%n", para.getAsJsonObject().get("confidence").getAsFloat());
blockText.append(paraText);
}
pageText.append(blockText);
}
}