برای فراخوانی Google Cloud API از برنامهتان، باید یک میانای برنامهسازی کاربردی میانجی REST ایجاد کنید که مجوزدهی را مدیریت کند و از مقادیر محرمانه مثل کلیدهای API محافظت کند. سپس باید در برنامه تلفن همراهتان کد بنویسید تا با این سرویس واسطه ارتباط برقرار کنید و هویتتان را در آن بهتأیید برسانید.
یکی از روشهای ایجاد این REST API استفاده از «احراز هویت Firebase» و «توابع» است که درگاهی بدون سرور و مدیریتشده به Google Cloud APIs ارائه میدهد که احراز هویت را مدیریت میکند و میتواند از برنامه تلفن همراه شما با کیتهای توسعه نرمافزار ازپیش ساختهشده فراخوانی شود.
این راهنما نشان میدهد که چگونه از این تکنیک برای فراخوانی Cloud Vision API از برنامهتان استفاده کنید. این روش به همه کاربران اصالتسنجیشده اجازه میدهد ازطریق پروژه Cloud شما به سرویسهای صورتحسابدار Cloud Vision دسترسی داشته باشند، بنابراین قبلاز ادامه دادن، درنظر بگیرید که آیا این سازوکار اصالتسنجی برای مورد استفاده شما کافی است یا نه.
قبل از شروع
پیکربندی پروژه
- اگر قبلاً این کار را نکردهاید، Firebase را به پروژه Android خود اضافه کنید.
-
اگر قبلاً «میاناهای برنامهسازی کاربردی ابری» را برای پروژهتان فعال نکردهاید، اکنون این کار را انجام دهید:
- صفحه Firebase ML APIs را در کنسول Firebase باز کنید.
-
اگر هنوز پروژهتان را به طرح قیمتگذاری Blaze با پرداخت براساس مصرف ارتقا ندادهاید، روی ارتقا کلیک کنید تا این کار را انجام دهید. (فقط درصورتی از شما خواسته میشود پروژهتان را ارتقا دهید که در طرح قیمتگذاری Blaze نباشد.)
فقط پروژههای طرح قیمتگذاری Blaze میتوانند از میاناهای برنامهسازی کاربردی مبتنی بر ابر استفاده کنند.
- اگر «میاناهای برنامهسازی کاربردی مبتنی بر Cloud» ازقبل فعال نشده است، روی فعال کردن میاناهای برنامهسازی کاربردی مبتنی بر Cloud کلیک کنید.
- کلیدهای Firebase API موجود را پیکربندی کنید تا دسترسی به Cloud
Vision API را غیرمجاز کنید:
- صفحه اطلاعات اعتباری کنسول Cloud را باز کنید.
- برای هر کلید API در فهرست، نمای ویرایش را باز کنید و در بخش «محدودیتهای کلید»، همه APIهای دردسترس بهجز Cloud Vision API را به فهرست اضافه کنید.
استقرار تابع فراخوانشدنی
سپس، Cloud Function را که برای ایجاد پل بین برنامه و Cloud Vision API استفاده میکنید مستقر کنید. مخزن functions-samples حاوی نمونهای است که میتوانید از آن استفاده کنید.
بهطور پیشفرض، دسترسی به Cloud Vision API ازطریق این تابع فقط به کاربران اصیلسازیشده برنامه شما اجازه میدهد به Cloud Vision API دسترسی داشته باشند. میتوانید عملکرد را برای الزامات مختلف تغییر دهید.
برای استقرار تابع:
- مخزن functions-samples را شبیهسازی یا بارگیری کنید
و به دایرکتوری
Node-1st-gen/vision-annotate-imageتغییر دهید:git clone https://github.com/firebase/functions-samplescd Node-1st-gen/vision-annotate-image - نصب وابستگیها:
cd functionsnpm installcd .. - اگر «واسط خط فرمان Firebase» را ندارید، آن را نصب کنید.
- پروژه Firebase را در فهرست راهنمای
vision-annotate-imageمقداردهی اولیه کنید. وقتی درخواست شد، پروژه خود را در فهرست انتخاب کنید.firebase init
- استقرار تابع:
firebase deploy --only functions:annotateImage
افزودن «احراز هویت Firebase» به برنامه
تابع فراخواندنی که در بالا مستقر شده است هرگونه درخواست از کاربران غیرتأییدشده برنامهتان را رد خواهد کرد. اگر قبلاً این کار را نکردهاید، باید «احراز هویت Firebase» را به برنامهتان اضافه کنید.
افزودن وابستگیهای ضروری به برنامه
<project>/<app-module>/build.gradle.kts یا
<project>/<app-module>/build.gradle) اضافه کنید:
implementation("com.google.firebase:firebase-functions:22.1.1") implementation("com.google.code.gson:gson:2.8.6")
اکنون آمادهاید که تشخیص نوشتار در تصاویر را شروع کنید.
۱. آماده کردن تصویر ورودی
برای فراخوانی Cloud Vision، تصویر باید بهصورت رشته کدبندیشده با base64 قالببندی شود. برای پردازش تصویر از نشانی وب فایل ذخیرهشده:- تصویر را بهصورت شیء
Bitmapدریافت کنید:Kotlin
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
Java
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
- بهصورت اختیاری، تصویر را کوچک کنید تا در پهنای باند صرفهجویی شود.
اندازههای تصویر توصیهشده Cloud Vision را ببینید.
Kotlin
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
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640)
Java
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640);
- تبدیل کردن شیء بیتمپ به رشته کدبندیشده base64:
Kotlin
// 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 باید
عمودی باشد و نیازی به چرخش اضافی نداشته باشد.
۲. برای تشخیص نوشتار، تابع فراخوانشدنی را فراخوانی کنید
برای تشخیص نوشتار در تصویر، تابع فراخوانکردنی را فراخوانی کنید و درخواست JSON Cloud Vision را ارسال کنید.
ابتدا نمونهای از Cloud Functions را مقداردهی اولیه کنید:
Kotlin
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functionsJava
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();روشی برای فراخوانی تابع تعریف کنید:
Kotlin
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
// 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
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
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 // ... } } });
۳. استخراج نوشتار از بلوکهای نوشتار شناساییشده
اگر عملیات تشخیص نوشتار موفقیتآمیز باشد، پاسخ JSON از BatchAnnotateImagesResponse در نتیجه تکلیف برگردانده خواهد شد. یادداشتهای نوشتاری را میتوانید درfullTextAnnotation شیء پیدا کنید.
میتوانید نوشتار شناساییشده را بهعنوان رشته در فیلد text دریافت کنید. برای مثال:
Kotlin
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
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);
}
}