หากต้องการเรียกใช้ Google Cloud API จากแอป คุณต้องสร้าง REST API ระดับกลาง ที่จัดการการให้สิทธิ์และปกป้องค่าลับ เช่น คีย์ API จากนั้นคุณต้อง เขียนโค้ดในแอปบนอุปกรณ์เคลื่อนที่เพื่อตรวจสอบสิทธิ์และสื่อสารกับบริการตัวกลางนี้
วิธีหนึ่งในการสร้าง REST API นี้คือการใช้ การตรวจสอบสิทธิ์ Firebase และ ฟังก์ชัน ซึ่งจะช่วยให้คุณมีเกตเวย์แบบ Serverless ที่มีการจัดการไปยัง Cloud APIs ซึ่งจัดการการตรวจสอบสิทธิ์และเรียกใช้จากแอปบนอุปกรณ์เคลื่อนที่ได้ด้วย SDK ที่สร้างไว้ล่วงหน้า
คู่มือนี้แสดงวิธีใช้เทคนิคนี้เพื่อเรียกใช้ Cloud Vision API จากแอป วิธีนี้จะช่วยให้ผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ทั้งหมดเข้าถึงบริการที่เรียกเก็บเงินของ Cloud Vision ผ่านโปรเจ็กต์ที่อยู่ในระบบคลาวด์ของคุณได้ ดังนั้น โปรดพิจารณาว่ากลไกการตรวจสอบสิทธิ์นี้เพียงพอสำหรับ Use Case ของคุณหรือไม่ก่อนดำเนินการต่อ
ก่อนเริ่มต้น
กำหนดค่าโปรเจ็กต์
- เพิ่ม Firebase ลงในโปรเจ็กต์ Android หากยังไม่ได้เพิ่ม
-
หากยังไม่ได้เปิดใช้ API บนระบบคลาวด์สำหรับโปรเจ็กต์ ให้ทำดังนี้ ตอนนี้
- เปิดFirebase ML หน้า API ในคอนโซล Firebase
-
หากยังไม่ได้อัปเกรดโปรเจ็กต์เป็นแพ็กเกจราคา Blaze แบบจ่ายเมื่อใช้ ให้คลิกอัปเกรดเพื่อดำเนินการ (ระบบจะแจ้งให้คุณอัปเกรดก็ต่อเมื่อโปรเจ็กต์ไม่ได้ใช้แพ็กเกจราคา Blaze)
เฉพาะโปรเจ็กต์ในแพ็กเกจราคา Blaze เท่านั้นที่ใช้ API บนระบบคลาวด์ได้
- หากยังไม่ได้เปิดใช้ API บนระบบคลาวด์ ให้คลิก เปิดใช้ API บนระบบคลาวด์
- กำหนดค่าคีย์ API ของ Firebase ที่มีอยู่เพื่อไม่อนุญาตให้เข้าถึง Cloud
Vision API ดังนี้
- เปิดหน้าข้อมูลเข้าสู่ระบบของ Cloud Console
- สำหรับคีย์ API แต่ละรายการในรายการ ให้เปิดมุมมองการแก้ไข แล้วเพิ่ม API ที่พร้อมใช้งานทั้งหมดยกเว้น Cloud Vision API ลงในรายการในส่วนข้อจำกัดของคีย์
ทําให้ฟังก์ชันที่เรียกใช้ได้ใช้งานได้
จากนั้น ให้ติดตั้งใช้งาน Cloud Function ที่คุณจะใช้เพื่อเชื่อมต่อแอปกับ Cloud
Vision API functions-samplesที่เก็บมีตัวอย่าง
ที่คุณใช้ได้
โดยค่าเริ่มต้น การเข้าถึง Cloud Vision API ผ่านฟังก์ชันนี้จะอนุญาตให้ เฉพาะผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์ของแอปเข้าถึง Cloud Vision API ได้ คุณ แก้ไขฟังก์ชันสำหรับข้อกำหนดต่างๆ ได้
วิธีทำให้ฟังก์ชันใช้งานได้
- โคลนหรือดาวน์โหลดที่เก็บฟังก์ชันตัวอย่าง
แล้วเปลี่ยนเป็นไดเรกทอรี
Node-1st-gen/vision-annotate-imagegit clone https://github.com/firebase/functions-samplescd Node-1st-gen/vision-annotate-image - ติดตั้งการอ้างอิงด้วยคำสั่งต่อไปนี้
cd functionsnpm installcd .. - หากยังไม่มี Firebase CLI ให้ติดตั้ง
- เริ่มต้นโปรเจ็กต์ Firebase ในไดเรกทอรี
vision-annotate-imageเมื่อได้รับข้อความแจ้ง ให้เลือกโปรเจ็กต์ในรายการfirebase init
- ทำให้ฟังก์ชันใช้งานได้
firebase deploy --only functions:annotateImage
เพิ่ม Firebase Auth ไปยังแอป
ฟังก์ชันที่เรียกใช้ได้ซึ่งติดตั้งใช้งานข้างต้นจะปฏิเสธคำขอจากผู้ใช้แอปที่ไม่ได้รับการตรวจสอบสิทธิ์ หากยังไม่ได้ดำเนินการ คุณจะต้องเพิ่ม Firebase Auth ลงในแอป
เพิ่มทรัพยากร Dependency ที่จำเป็นลงในแอป
<project>/<app-module>/build.gradle.kts หรือ
<project>/<app-module>/build.gradle)
implementation("com.google.firebase:firebase-functions:22.1.0") implementation("com.google.code.gson:gson:2.8.6")
ตอนนี้คุณพร้อมที่จะเริ่มจดจำข้อความในรูปภาพแล้ว
1. เตรียมรูปภาพอินพุต
หากต้องการเรียกใช้ Cloud Vision รูปภาพต้องอยู่ในรูปแบบสตริงที่เข้ารหัส Base64 หากต้องการประมวลผลรูปภาพจาก URI ของไฟล์ที่บันทึกไว้ ให้ทำดังนี้- รับรูปภาพเป็นออบเจ็กต์
BitmapKotlin
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 ต้องตั้งตรงโดยไม่ต้องหมุนเพิ่มเติม
2. เรียกใช้ฟังก์ชันที่เรียกใช้ได้เพื่อจดจำข้อความ
หากต้องการจดจำข้อความในรูปภาพ ให้เรียกใช้ฟังก์ชันที่เรียกใช้ได้โดยส่งคำขอ 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 รองรับการตรวจหาข้อความ 2 ประเภท ได้แก่
TEXT_DETECTIONและDOCUMENT_TEXT_DETECTIONดูความแตกต่างระหว่างกรณีการใช้งานทั้ง 2 แบบได้ในเอกสาร OCR ของ Cloud VisionKotlin
// 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 // ... } } });
3. แยกข้อความจากบล็อกข้อความที่ระบบจดจำได้
หากการดำเนินการจดจำข้อความสำเร็จ ระบบจะแสดงการตอบกลับ 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);
}
}