Catch up on everthing we announced at this year's Firebase Summit. Learn more

ติดป้ายกำกับรูปภาพอย่างปลอดภัยด้วย Cloud Vision โดยใช้ Firebase Auth และฟังก์ชั่นบน Android

ในการเรียก Google Cloud API จากแอปของคุณ คุณต้องสร้าง REST API ระดับกลางที่จัดการการให้สิทธิ์และปกป้องค่าลับ เช่น คีย์ API จากนั้นคุณจะต้องเขียนโค้ดในแอพมือถือของคุณเพื่อตรวจสอบสิทธิ์และสื่อสารกับบริการระดับกลางนี้

วิธีหนึ่งในการสร้าง REST API นี้คือการใช้ Firebase Authentication and Functions ซึ่งให้เกตเวย์แบบไร้เซิร์ฟเวอร์ที่มีการจัดการไปยัง Google Cloud API ที่จัดการการตรวจสอบสิทธิ์ และสามารถเรียกได้จากแอปบนอุปกรณ์เคลื่อนที่ด้วย SDK ที่สร้างไว้ล่วงหน้า

คู่มือนี้สาธิตวิธีใช้เทคนิคนี้เพื่อเรียก Cloud Vision API จากแอปของคุณ วิธีนี้จะอนุญาตให้ผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์ทั้งหมดเข้าถึงบริการที่เรียกเก็บเงิน Cloud Vision ผ่านโปรเจ็กต์ Cloud ของคุณ ดังนั้นให้พิจารณาว่ากลไกการตรวจสอบสิทธิ์นี้เพียงพอสำหรับกรณีการใช้งานของคุณหรือไม่ก่อนดำเนินการต่อ

ก่อนจะเริ่ม

กำหนดค่าโครงการของคุณ

  1. หากคุณยังไม่ได้ เพิ่ม Firebase กับโครงการ Android ของคุณ
  2. หากคุณยังไม่ได้เปิดใช้งาน API แบบ Cloud-based สำหรับโปรเจ็กต์ของคุณ ให้ดำเนินการดังนี้:

    1. เปิด หน้า Firebase ML APIs ของคอนโซล Firebase
    2. หากคุณไม่ได้อัปเกรดแล้วโครงการของคุณแผนกำหนดราคา Blaze คลิกอัพเกรดจะทำเช่นนั้น (คุณจะได้รับแจ้งให้อัปเกรดเฉพาะเมื่อโปรเจ็กต์ของคุณไม่อยู่ในแผน Blaze)

      เฉพาะโปรเจ็กต์ระดับ Blaze เท่านั้นที่สามารถใช้ API แบบคลาวด์ได้

    3. หาก APIs cloud-based ยังไม่ได้เปิดใช้งานแล้วคลิกเปิดใช้งาน API ของ cloud-based
  3. กำหนดค่าคีย์ Firebase API ที่มีอยู่ของคุณเพื่อไม่อนุญาตให้เข้าถึง Cloud Vision API:
    1. เปิด ข้อมูลประจำตัว หน้าของคอนโซลเมฆ
    2. สำหรับแต่ละคีย์ API ในรายการเปิดมุมมองการแก้ไขและในส่วนข้อ จำกัด ที่สำคัญเพิ่มทั้งหมดของ API ที่มีอยู่ยกเว้น API เมฆวิสัยทัศน์ในรายการ

ปรับใช้ฟังก์ชันที่เรียกได้

ขั้นต่อไป ปรับใช้ Cloud Function ที่คุณจะใช้เพื่อเชื่อมโยงแอปของคุณและ Cloud Vision API functions-samples ที่เก็บมีตัวอย่างเช่นคุณสามารถใช้

ตามค่าเริ่มต้น การเข้าถึง Cloud Vision API ผ่านฟังก์ชันนี้จะอนุญาตให้เฉพาะผู้ใช้แอปที่ตรวจสอบสิทธิ์เข้าถึง Cloud Vision API เท่านั้น คุณสามารถปรับเปลี่ยนฟังก์ชันสำหรับข้อกำหนดต่างๆ ได้

ในการปรับใช้ฟังก์ชัน:

  1. โคลนหรือ Download ฟังก์ชั่นตัวอย่าง repo และการเปลี่ยนแปลงไปสู่ vision-annotate-image ไดเรกทอรี:
    git clone https://github.com/firebase/functions-samples
    cd vision-annotate-image
    
  2. ติดตั้งการอ้างอิง:
    cd functions
    npm install
    cd ..
    
  3. หากคุณไม่ได้มี Firebase CLI, ติดตั้ง
  4. เริ่มต้นโครงการ Firebase ใน vision-annotate-image ไดเรกทอรี เมื่อได้รับแจ้ง ให้เลือกโครงการของคุณในรายการ
    firebase init
  5. การจัดวางฟังก์ชั่น:
    firebase deploy --only functions:annotateImage

เพิ่ม Firebase Auth ในแอปของคุณ

ฟังก์ชัน callable ที่ปรับใช้ด้านบนจะปฏิเสธคำขอใดๆ จากผู้ใช้ที่ไม่ผ่านการตรวจสอบสิทธิ์ของแอปของคุณ หากคุณยังไม่ได้ทำเช่นนั้นคุณจะต้อง เพิ่ม Firebase Auth แอปของคุณ

เพิ่มการพึ่งพาที่จำเป็นให้กับแอปของคุณ

  • เพิ่มการอ้างอิงสำหรับฟังก์ชั่นและ Firebase gson Android ห้องสมุดโมดูลของคุณ (app ระดับ) ไฟล์ Gradle (ปกติ app / build.gradle):
    implementation 'com.google.firebase:firebase-functions:20.0.1'
    implementation 'com.google.code.gson:gson:2.8.6'
    
  • ตอนนี้คุณพร้อมที่จะติดป้ายกำกับรูปภาพแล้ว

    1. เตรียมภาพอินพุต

    ในการเรียก Cloud Vision รูปภาพต้องได้รับการจัดรูปแบบเป็นสตริงที่เข้ารหัส base64 ในการประมวลผลรูปภาพจากไฟล์ URI ที่บันทึกไว้:
    1. รับภาพเป็น Bitmap วัตถุ:

      Java

      Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);

      คอตลิน+KTX

      var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
    2. คุณสามารถเลือกลดขนาดรูปภาพเพื่อประหยัดแบนด์วิดท์ ดู เมฆวิสัยทัศน์ขนาดภาพแนะนำ

      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);
      }

      คอตลิน+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

      // Scale down bitmap size
      bitmap = scaleBitmapDown(bitmap, 640);

      คอตลิน+KTX

      // Scale down bitmap size
      bitmap = scaleBitmapDown(bitmap, 640)
    3. แปลงวัตถุบิตแมปเป็นสตริงที่เข้ารหัส base64:

      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);

      คอตลิน+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)
    4. ภาพที่แสดงโดย Bitmap วัตถุต้องตรงกับที่ไม่มีการหมุนเพิ่มเติมที่จำเป็น

    2. เรียกใช้ฟังก์ชันที่เรียกได้เพื่อติดป้ายกำกับรูปภาพ

    จะติดป้ายวัตถุในภาพเรียกฟังก์ชั่น callable ผ่าน คำขอ JSON เมฆวิสัยทัศน์

    1. ขั้นแรก เริ่มต้นอินสแตนซ์ของ Cloud Functions:

      Java

      private FirebaseFunctions mFunctions;
      // ...
      mFunctions = FirebaseFunctions.getInstance();
      

      คอตลิน+KTX

      private lateinit var functions: FirebaseFunctions
      // ...
      functions = Firebase.functions
      
    2. กำหนดวิธีการเรียกใช้ฟังก์ชัน:

      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()));
                      }
                  });
      }
      

      คอตลิน+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))
                  }
      }
      
    3. สร้างการร้องขอ JSON กับ ประเภท ชุด LABEL_DETECTION :

      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("LABEL_DETECTION"));
      JsonArray features = new JsonArray();
      features.add(feature);
      request.add("features", features);
      

      คอตลิน+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("LABEL_DETECTION"))
      val features = JsonArray()
      features.add(feature)
      request.add("features", features)
      
    4. ในที่สุด เรียกใช้ฟังก์ชัน:

      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
                          // ...
                      }
                  }
              });
      

      คอตลิน+KTX

      annotateImage(request.toString())
              .addOnCompleteListener { task ->
                  if (!task.isSuccessful) {
                      // Task failed with an exception
                      // ...
                  } else {
                      // Task completed successfully
                      // ...
                  }
              }
      

    3. รับข้อมูลเกี่ยวกับวัตถุที่มีป้ายกำกับ

    หากการดำเนินการติดฉลากภาพประสบความสำเร็จในการตอบสนองของ JSON BatchAnnotateImagesResponse จะกลับมาในผลงานของ วัตถุในแต่ละ labelAnnotations อาร์เรย์แสดงให้เห็นถึงสิ่งที่ถูกตราหน้าว่าในภาพ สำหรับแต่ละป้ายคุณจะได้รับรายละเอียดของป้ายข้อความของ กราฟความรู้ ID นิติบุคคล (ถ้ามี) และคะแนนความเชื่อมั่นของการแข่งขัน ตัวอย่างเช่น:

    Java

    for (JsonElement label : task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("labelAnnotations").getAsJsonArray()) {
        JsonObject labelObj = label.getAsJsonObject();
        String text = labelObj.get("description").getAsString();
        String entityId = labelObj.get("mid").getAsString();
        float score = labelObj.get("score").getAsFloat();
    }
    

    คอตลิน+KTX

    for (label in task.result!!.asJsonArray[0].asJsonObject["labelAnnotations"].asJsonArray) {
        val labelObj = label.asJsonObject
        val text = labelObj["description"]
        val entityId = labelObj["mid"]
        val confidence = labelObj["score"]
    }