หากต้องการเรียกใช้ Google Cloud API จากแอปของคุณ คุณต้องสร้างสื่อกลาง REST API ที่จัดการการให้สิทธิ์และปกป้องค่าข้อมูลลับ เช่น คีย์ API จากนั้นคุณต้อง เขียนโค้ดในแอปบนอุปกรณ์เคลื่อนที่เพื่อตรวจสอบสิทธิ์และสื่อสารกับบริการระดับกลางนี้
วิธีหนึ่งในการสร้าง REST API นี้คือการใช้การตรวจสอบสิทธิ์และฟังก์ชันของ Firebase ซึ่งช่วยให้คุณมีเกตเวย์แบบ Serverless ที่มีการจัดการไปยัง Google Cloud APIs ที่จัดการการตรวจสอบสิทธิ์และสามารถเรียกใช้จากแอปบนอุปกรณ์เคลื่อนที่ด้วย SDK ที่สร้างไว้ล่วงหน้า
คู่มือนี้จะสาธิตวิธีใช้เทคนิคนี้เพื่อเรียก Cloud Vision API จากแอปของคุณ วิธีนี้จะอนุญาตให้ผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์แล้วทั้งหมดเข้าถึงบริการที่เรียกเก็บเงินสำหรับ Cloud Vision ผ่านโปรเจ็กต์ระบบคลาวด์ได้ ดังนั้น ให้พิจารณาว่ากลไกการตรวจสอบสิทธิ์นี้เพียงพอสำหรับกรณีการใช้งานของคุณหรือไม่ก่อนที่จะดำเนินการต่อ
ก่อนเริ่มต้น
กำหนดค่าโปรเจ็กต์
- หากคุณยังไม่ได้ดำเนินการ เพิ่ม Firebase ลงในโปรเจ็กต์ Android
-
หากยังไม่ได้เปิดใช้ API ในระบบคลาวด์สำหรับโปรเจ็กต์ของคุณ ให้เปิดใช้ ในขณะนี้:
- เปิดFirebase ML หน้า API ของคอนโซล Firebase
-
หากคุณยังไม่ได้อัปเกรดโปรเจ็กต์เป็นแพ็กเกจราคา Blaze ให้คลิก โปรดอัปเกรดเพื่อดำเนินการ (คุณจะได้รับแจ้งให้อัปเกรดเฉพาะในกรณีต่อไปนี้ ไม่ได้อยู่ในแพ็กเกจ Blaze)
เฉพาะโปรเจ็กต์ระดับ Blaze เท่านั้นที่ใช้ API ในระบบคลาวด์ได้
- หากยังไม่ได้เปิดใช้ API ในระบบคลาวด์ ให้คลิกเปิดใช้ในระบบคลาวด์ API
- กำหนดค่าคีย์ Firebase API ที่มีอยู่เพื่อไม่ให้อนุญาตการเข้าถึงระบบคลาวด์
API ของ Vision
- เปิดหน้าข้อมูลเข้าสู่ระบบของ Cloud Console
- สำหรับคีย์ API แต่ละรายการในรายการ ให้เปิดมุมมองการแก้ไข และใน ส่วนข้อจำกัด ให้เพิ่ม API ที่มีอยู่ทั้งหมด ยกเว้น Cloud Vision API ลงในรายการ
ทำให้ฟังก์ชันที่เรียกใช้ได้ใช้งานได้
ถัดไป ให้ติดตั้งใช้งาน Cloud Function เพื่อเชื่อมโยงแอปและระบบคลาวด์
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 Auth ลงในแอป
ฟังก์ชันที่เรียกใช้ได้ที่ใช้งานด้านบนจะปฏิเสธคำขอทั้งหมดจากผู้ใช้ที่ไม่ได้ตรวจสอบสิทธิ์ ผู้ใช้แอปของคุณ คุณจะต้องเพิ่ม Firebase หากยังไม่ได้เพิ่ม ตรวจสอบสิทธิ์แอปของคุณ
เพิ่มทรัพยากร Dependency ที่จำเป็นลงในแอป
<project>/<app-module>/build.gradle.kts
หรือ
<project>/<app-module>/build.gradle
)
implementation("com.google.firebase:firebase-functions:21.1.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 ด้วย Type
LANDMARK_DETECTION
: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("maxResults", JsonPrimitive(5)) feature.add("type", JsonPrimitive("LANDMARK_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("maxResults", new JsonPrimitive(5)); feature.add("type", new JsonPrimitive("LANDMARK_DETECTION")); JsonArray features = new JsonArray(); features.add(feature); request.add("features", features);
สุดท้าย เรียกใช้ฟังก์ชัน
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 จะแสดงในผลลัพธ์ของงาน แต่ละออบเจ็กต์ในlandmarkAnnotations
อาร์เรย์แสดงจุดสังเกตที่เป็นที่รู้จักในภาพ สำหรับจุดสังเกตแต่ละรายการ
คุณจะเห็นพิกัดขอบเขตของจุดในรูปภาพอินพุต ชื่อจุดสังเกต
ละติจูดและลองจิจูด รหัสเอนทิตีของกราฟความรู้ (หากมี) และ
คะแนนความเชื่อมั่นของการแข่งขัน เช่น
Kotlin+KTX
for (label in task.result!!.asJsonArray[0].asJsonObject["landmarkAnnotations"].asJsonArray) {
val labelObj = label.asJsonObject
val landmarkName = labelObj["description"]
val entityId = labelObj["mid"]
val score = labelObj["score"]
val bounds = labelObj["boundingPoly"]
// Multiple locations are possible, e.g., the location of the depicted
// landmark and the location the picture was taken.
for (loc in labelObj["locations"].asJsonArray) {
val latitude = loc.asJsonObject["latLng"].asJsonObject["latitude"]
val longitude = loc.asJsonObject["latLng"].asJsonObject["longitude"]
}
}
Java
for (JsonElement label : task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("landmarkAnnotations").getAsJsonArray()) {
JsonObject labelObj = label.getAsJsonObject();
String landmarkName = labelObj.get("description").getAsString();
String entityId = labelObj.get("mid").getAsString();
float score = labelObj.get("score").getAsFloat();
JsonObject bounds = labelObj.get("boundingPoly").getAsJsonObject();
// Multiple locations are possible, e.g., the location of the depicted
// landmark and the location the picture was taken.
for (JsonElement loc : labelObj.get("locations").getAsJsonArray()) {
JsonObject latLng = loc.getAsJsonObject().get("latLng").getAsJsonObject();
double latitude = latLng.get("latitude").getAsDouble();
double longitude = latLng.get("longitude").getAsDouble();
}
}