Pic-a-daily: Lab 1 - จัดเก็บและวิเคราะห์รูปภาพ (Java เนทีฟ)

1. ภาพรวม

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

c0650ee4a76db35e.png

สิ่งที่คุณจะได้เรียนรู้

  • Cloud Storage
  • Cloud Run
  • Cloud Vision API
  • Cloud Firestore

2. การตั้งค่าและข้อกำหนด

การตั้งค่าสภาพแวดล้อมตามเวลาที่สะดวก

  1. ลงชื่อเข้าใช้ Google Cloud Console และสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • ชื่อโครงการคือชื่อที่แสดงของผู้เข้าร่วมโปรเจ็กต์นี้ เป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ โดยคุณจะอัปเดตวิธีการชำระเงินได้ทุกเมื่อ
  • รหัสโปรเจ็กต์จะไม่ซ้ำกันในทุกโปรเจ็กต์ของ Google Cloud และจะเปลี่ยนแปลงไม่ได้ (เปลี่ยนแปลงไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ปกติแล้วคุณไม่สนว่าอะไรเป็นอะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยปกติจะระบุเป็น PROJECT_ID) หากคุณไม่ชอบรหัสที่สร้างขึ้น คุณสามารถสร้างรหัสแบบสุ่มอื่นได้ หรือคุณจะลองดำเนินการเองแล้วดูว่าพร้อมให้ใช้งานหรือไม่ คุณจะเปลี่ยนแปลงหลังจากขั้นตอนนี้ไม่ได้และจะยังคงอยู่ตลอดระยะเวลาของโปรเจ็กต์
  • สำหรับข้อมูลของคุณ ค่าที่ 3 คือหมายเลขโปรเจ็กต์ ซึ่ง API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 ค่าเหล่านี้ในเอกสารประกอบ
  1. ถัดไป คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของระบบคลาวด์ การใช้งาน Codelab นี้จะไม่มีค่าใช้จ่ายใดๆ หากมี หากต้องการปิดทรัพยากรเพื่อหลีกเลี่ยงการเรียกเก็บเงินที่นอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่คุณสร้างหรือลบโปรเจ็กต์ได้ ผู้ใช้ Google Cloud ใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรี$300 USD

เริ่มต้น Cloud Shell

แม้ว่าคุณจะดำเนินการ Google Cloud จากระยะไกลได้จากแล็ปท็อป แต่คุณจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมแบบบรรทัดคำสั่งที่ทำงานในระบบคลาวด์ใน Codelab นี้

จากคอนโซล Google Cloud ให้คลิกไอคอน Cloud Shell ในแถบเครื่องมือด้านขวาบน ดังนี้

84688aa223b1c3a2.png

การจัดสรรและเชื่อมต่อกับสภาพแวดล้อมนี้ควรใช้เวลาเพียงครู่เดียว เมื่อเสร็จแล้ว คุณจะเห็นข้อมูลต่อไปนี้

320e18fedb7fbe0.png

เครื่องเสมือนนี้เต็มไปด้วยเครื่องมือการพัฒนาทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักขนาด 5 GB ที่ใช้งานได้ต่อเนื่องและทำงานบน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพของเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก งานทั้งหมดใน Codelab นี้ทำได้ในเบราว์เซอร์ คุณไม่จำเป็นต้องติดตั้งอะไรเลย

3. เปิดใช้ API

สำหรับห้องทดลองนี้ คุณจะใช้ Cloud Functions และ Vision API แต่ต้องเปิดใช้ใน Cloud Console หรือด้วย gcloud ก่อน

หากต้องการเปิดใช้ Vision API ใน Cloud Console ให้ค้นหา Cloud Vision API ในแถบค้นหา

8f3522d790bb026c.png

คุณจะเข้าสู่หน้า Cloud Vision API:

d785572fa14c87c2.png

คลิกปุ่ม ENABLE

หรือคุณจะเปิดใช้ Cloud Shell โดยใช้เครื่องมือบรรทัดคำสั่ง gcloud ก็ได้

เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell

gcloud services enable vision.googleapis.com

คุณควรเห็นการดำเนินการที่เสร็จสิ้นเรียบร้อยแล้ว

Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.

เปิดใช้ Cloud Run และ Cloud Build ด้วย:

gcloud services enable cloudbuild.googleapis.com \
  run.googleapis.com

4. สร้างที่เก็บข้อมูล (คอนโซล)

สร้างที่เก็บข้อมูลของพื้นที่เก็บข้อมูลสำหรับรูปภาพ โดยทำได้จากคอนโซล Google Cloud Platform ( console.cloud.google.com) หรือด้วยเครื่องมือบรรทัดคำสั่ง gsutil จาก Cloud Shell หรือสภาพแวดล้อมการพัฒนาในเครื่อง

จาก "แฮมเบอร์เกอร์" (⋮) ให้ไปที่หน้าStorage

d08ecb0ae29330a1.png

ตั้งชื่อที่เก็บข้อมูล

คลิกปุ่ม CREATE BUCKET

8951851554a430d2.png

คลิก CONTINUE

เลือกตำแหน่ง

24b24625157ab467.png

สร้างที่เก็บข้อมูลหลายภูมิภาคในภูมิภาคที่คุณเลือก (ที่นี่ Europe)

คลิก CONTINUE

เลือกคลาสพื้นที่เก็บข้อมูลเริ่มต้น

9e7bd365fa94a2e0.png

เลือกคลาสพื้นที่เก็บข้อมูล Standard สำหรับข้อมูลของคุณ

คลิก CONTINUE

ตั้งค่าการควบคุมการเข้าถึง

1ff4a1f6e57045f5.png

เนื่องจากคุณจะต้องใช้งานรูปภาพที่เข้าถึงได้แบบสาธารณะ คุณต้องการให้รูปภาพทั้งหมดที่จัดเก็บในที่เก็บข้อมูลนี้มีการควบคุมการเข้าถึงแบบเดียวกัน

เลือกตัวเลือกการควบคุมการเข้าถึง Uniform

คลิก CONTINUE

ตั้งค่าการป้องกัน/การเข้ารหัส

2d469b076029d365.png

เก็บค่าเริ่มต้นไว้ (Google-managed key) เนื่องจากคุณจะไม่ใช้คีย์การเข้ารหัสของคุณเอง

คลิก CREATE เพื่อสร้างที่เก็บข้อมูลขั้นสุดท้าย

เพิ่ม allUsers เป็นผู้ดูพื้นที่เก็บข้อมูล

ไปที่แท็บ Permissions:

19564b3ad8688ae8.png

เพิ่มสมาชิก allUsers ไปยังที่เก็บข้อมูล โดยมีบทบาทเป็น Storage > Storage Object Viewer ดังนี้

d655e760c76d62c1.png

คลิก SAVE

5. สร้างที่เก็บข้อมูล (gsutil)

คุณยังใช้เครื่องมือบรรทัดคำสั่ง gsutil ใน Cloud Shell เพื่อสร้างที่เก็บข้อมูลได้ด้วย

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

เช่น

export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}

สร้างโซนหลายภูมิภาคตามมาตรฐานในยุโรป

gsutil mb -l EU gs://${BUCKET_PICTURES}

ตรวจสอบว่าสิทธิ์เข้าถึงระดับที่เก็บข้อมูลแบบเดียวกัน

gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}

ทำให้ที่เก็บข้อมูลเป็นแบบสาธารณะ โดยทำดังนี้

gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}

หากไปที่ส่วน Cloud Storage ของคอนโซล คุณควรมีที่เก็บข้อมูล uploaded-pictures สาธารณะ

65c63ef4a6eb30ad.png

ทดสอบว่าคุณสามารถอัปโหลดรูปภาพไปยังที่เก็บข้อมูล และภาพที่อัปโหลดจะปรากฏต่อสาธารณะ ตามที่อธิบายไว้ในขั้นตอนก่อนหน้า

6. ทดสอบสิทธิ์เข้าถึงที่เก็บข้อมูลแบบสาธารณะ

การกลับไปยังเบราว์เซอร์พื้นที่เก็บข้อมูล คุณจะเห็นที่เก็บข้อมูลในรายการ โดยมี "สาธารณะ" (รวมถึงป้ายเตือนที่บอกว่าทุกคนมีสิทธิ์เข้าถึงเนื้อหาของที่เก็บข้อมูลนั้น)

e639a9ba625b71a6.png

ตอนนี้ที่เก็บข้อมูลของคุณพร้อมรับรูปภาพแล้ว

หากคลิกชื่อที่เก็บข้อมูล คุณจะเห็นรายละเอียดที่เก็บข้อมูล

1f88a2290290aba8.png

จากจุดนี้ คุณสามารถลองใช้ปุ่ม Upload files เพื่อทดสอบว่าคุณเพิ่มรูปภาพลงในที่เก็บข้อมูลได้ ป๊อปอัปตัวเลือกไฟล์จะขอให้คุณเลือกไฟล์ เมื่อเลือกแล้ว ระบบจะอัปโหลดไฟล์ดังกล่าวไปยังที่เก็บข้อมูลของคุณ และคุณจะเห็นสิทธิ์เข้าถึง public ที่ระบบระบุแหล่งที่มาไปยังไฟล์ใหม่นี้โดยอัตโนมัติอีกครั้ง

1209e7ebe1f63b10.png

คุณจะเห็นไอคอนลิงก์ขนาดเล็กที่ป้ายกำกับการเข้าถึง Public ด้วย เมื่อคลิกที่รูปภาพ เบราว์เซอร์ของคุณจะนำทางไปยัง URL สาธารณะของรูปภาพนั้น ซึ่งจะอยู่ในรูปแบบ:

https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png

โดย BUCKET_NAME เป็นชื่อที่ไม่ซ้ำกันทั่วโลกซึ่งคุณได้เลือกไว้สำหรับที่เก็บข้อมูล และตามด้วยชื่อไฟล์ของรูปภาพ

เมื่อคลิกช่องทำเครื่องหมายข้างชื่อภาพ ปุ่ม DELETE จะเปิดการใช้งาน และคุณจะสามารถลบรูปภาพแรกนี้ได้

7. เตรียมฐานข้อมูล

คุณจะจัดเก็บข้อมูลเกี่ยวกับรูปภาพที่ได้รับจาก Vision API ไว้ในฐานข้อมูล Cloud Firestore ซึ่งเป็นฐานข้อมูลเอกสาร NoSQL ที่ดำเนินการบนระบบคลาวด์และทำงานได้อย่างรวดเร็ว มีการจัดการครบวงจร เตรียมฐานข้อมูลด้วยการไปที่ส่วน Firestore ของ Cloud Console:

e57a673537b5deca.png

โดยมี 2 ตัวเลือก ได้แก่ Native mode หรือ Datastore mode ใช้โหมดเนทีฟซึ่งมีฟีเจอร์เพิ่มเติม เช่น การรองรับการใช้งานออฟไลน์และการซิงค์ข้อมูลแบบเรียลไทม์

คลิก SELECT NATIVE MODE

1a2e363fae5c7e96.png

เลือกหลายภูมิภาค (ที่นี่ในยุโรป แต่อย่างน้อยควรเป็นภูมิภาคเดียวกับฟังก์ชันและที่เก็บข้อมูลของพื้นที่เก็บข้อมูล)

คลิกปุ่ม CREATE DATABASE

เมื่อสร้างฐานข้อมูลแล้ว คุณจะเห็นสิ่งต่อไปนี้

7dcc82751ed483fb.png

สร้างคอลเล็กชันใหม่โดยคลิกปุ่ม + START COLLECTION

ชื่อคอลเล็กชัน pictures

dce3d73884ac8c83.png

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

คลิก Save

Firestore สร้างเอกสารเริ่มต้นรายการแรกในคอลเล็กชันที่สร้างขึ้นใหม่ คุณลบเอกสารดังกล่าวได้อย่างปลอดภัยเนื่องจากไม่มีข้อมูลที่เป็นประโยชน์ใดๆ ดังต่อไปนี้

63e95c844b3f79d3.png

เอกสารที่จะสร้างขึ้นแบบเป็นโปรแกรมในคอลเล็กชันของเราประกอบด้วย 4 ฟิลด์ ดังนี้

  • name (สตริง): ชื่อไฟล์ของรูปภาพที่อัปโหลด ซึ่งเป็นคีย์ของเอกสาร
  • labels (อาร์เรย์ของสตริง): ป้ายกำกับของรายการที่รู้จักโดย Vision API
  • color (สตริง): รหัสสีเลขฐาน 16 ของสีหลัก (เช่น #ab12ef)
  • สร้าง (วันที่): การประทับเวลาที่เก็บข้อมูลเมตาของรูปภาพนี้
  • ภาพขนาดย่อ (บูลีน): ช่องที่ไม่บังคับซึ่งจะแสดงและเป็น "จริง" หากมีการสร้างภาพขนาดย่อสำหรับรูปภาพนี้

เนื่องจากเราจะค้นหาใน Firestore เพื่อค้นหารูปภาพที่มีภาพขนาดย่อ และจัดเรียงตามวันที่สร้าง เราจะต้องสร้างดัชนีการค้นหา

คุณสร้างดัชนีด้วยคำสั่งต่อไปนี้ใน Cloud Shell ได้

gcloud firestore indexes composite create \
  --collection-group=pictures \
  --field-config field-path=thumbnail,order=descending \
  --field-config field-path=created,order=descending

หรือดำเนินการจาก Cloud Console โดยคลิก Indexes ในคอลัมน์การนำทางด้านซ้าย แล้วสร้างดัชนีผสมดังที่แสดงด้านล่างก็ได้

2236d3a024a59232.png

คลิก Create การสร้างดัชนีอาจใช้เวลาสักครู่

8. โคลนโค้ด

โคลนโค้ดหากยังไม่ได้ทำใน Code Lab ก่อนหน้านี้:

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

จากนั้นคุณสามารถไปที่ไดเรกทอรีที่มีบริการเพื่อเริ่มสร้าง Lab:

cd serverless-photosharing-workshop/services/image-analysis/java

คุณจะมีการจัดวางไฟล์ดังต่อไปนี้สำหรับบริการ:

4c2a18a2c8b69dc5.png

9. สำรวจรหัสบริการ

คุณเริ่มต้นด้วยการดูวิธีเปิดใช้ไลบรารีไคลเอ็นต์ Java ใน pom.xml โดยใช้ BOM ดังนี้

ก่อนอื่น ให้เปิดไฟล์ pom.xml ซึ่งแสดงรายการทรัพยากร Dependency ของแอป Java มุ่งเน้นที่การใช้งาน API ของ Vision, Cloud Storage และ Firestore

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0-M3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>services</groupId>
        <artifactId>image-analysis</artifactId>
        <version>0.0.1</version>
        <name>image-analysis</name>
        <description>Spring App for Image Analysis</description>
    <properties>
        <java.version>17</java.version>
        <maven.compiler.target>17</maven.compiler.target>
        <maven.compiler.source>17</maven.compiler.source>        
        <spring-cloud.version>2023.0.0-M2</spring-cloud.version>
        <testcontainers.version>1.19.1</testcontainers.version>
    </properties>
...
  <dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>libraries-bom</artifactId>
            <version>26.24.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
  </dependencyManagement>
— 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
                <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-function-web</artifactId>
                </dependency>
        <dependency>
            <groupId>com.google.cloud.functions</groupId>
            <artifactId>functions-framework-api</artifactId>
            <version>1.1.0</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>google-cloud-firestore</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>google-cloud-vision</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>google-cloud-storage</artifactId>
        </dependency>        

มีการติดตั้งใช้งานฟังก์ชันนี้ในคลาส EventController ทุกครั้งที่มีการอัปโหลดรูปภาพใหม่ลงในที่เก็บข้อมูล บริการจะได้รับการแจ้งเตือนให้ประมวลผลดังนี้

@RestController
public class EventController {
  private static final Logger logger = Logger.getLogger(EventController.class.getName());
    
  private static final List<String> requiredFields = Arrays.asList("ce-id", "ce-source", "ce-type", "ce-specversion");

  @RequestMapping(value = "/", method = RequestMethod.POST)
  public ResponseEntity<String> receiveMessage(
    @RequestBody Map<String, Object> body, @RequestHeader Map<String, String> headers) throws IOException, InterruptedException, ExecutionException {
...
}

โค้ดจะดำเนินการตรวจสอบส่วนหัว Cloud Events:

System.out.println("Header elements");
for (String field : requiredFields) {
    if (headers.get(field) == null) {
    String msg = String.format("Missing expected header: %s.", field);
    System.out.println(msg);
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
    } else {
    System.out.println(field + " : " + headers.get(field));
    }
}

System.out.println("Body elements");
for (String bodyField : body.keySet()) {
    System.out.println(bodyField + " : " + body.get(bodyField));
}

if (headers.get("ce-subject") == null) {
    String msg = "Missing expected header: ce-subject.";
    System.out.println(msg);
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
} 

สร้างคำขอได้แล้ว โค้ดจะเตรียมคำขอดังกล่าว 1 รายการให้ส่งไปยัง Vision API:

try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
    List<AnnotateImageRequest> requests = new ArrayList<>();
    
    ImageSource imageSource = ImageSource.newBuilder()
        .setGcsImageUri("gs://" + bucketName + "/" + fileName)
        .build();

    Image image = Image.newBuilder()
        .setSource(imageSource)
        .build();

    Feature featureLabel = Feature.newBuilder()
        .setType(Type.LABEL_DETECTION)
        .build();
    Feature featureImageProps = Feature.newBuilder()
        .setType(Type.IMAGE_PROPERTIES)
        .build();
    Feature featureSafeSearch = Feature.newBuilder()
        .setType(Type.SAFE_SEARCH_DETECTION)
        .build();
        
    AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
        .addFeatures(featureLabel)
        .addFeatures(featureImageProps)
        .addFeatures(featureSafeSearch)
        .setImage(image)
        .build();
    
    requests.add(request);

เราขอแนะนำความสามารถหลัก 3 อย่างของ Vision API ดังนี้

  • การตรวจจับป้ายกำกับ: เพื่อให้ทราบสิ่งที่อยู่ในรูปภาพเหล่านั้น
  • คุณสมบัติของรูปภาพ: เพื่อแสดงแอตทริบิวต์ที่น่าสนใจของรูปภาพ (เราสนใจสีที่โดดเด่นของรูปภาพ)
  • ค้นหาปลอดภัย: เพื่อดูว่ารูปภาพนั้นปลอดภัยหรือไม่ (ไม่ควรมีเนื้อหาสำหรับผู้ใหญ่ / การแพทย์ / สำหรับผู้ใหญ่ / ความรุนแรง)

ในตอนนี้ เราสามารถเรียกใช้ Vision API ได้โดยทำดังนี้

...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result = vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...

สำหรับการอ้างอิง การตอบสนองจาก Vision API จะมีลักษณะดังนี้

{
  "faceAnnotations": [],
  "landmarkAnnotations": [],
  "logoAnnotations": [],
  "labelAnnotations": [
    {
      "locations": [],
      "properties": [],
      "mid": "/m/01yrx",
      "locale": "",
      "description": "Cat",
      "score": 0.9959855675697327,
      "confidence": 0,
      "topicality": 0.9959855675697327,
      "boundingPoly": null
    },
    ✄ - - - ✄
  ],
  "textAnnotations": [],
  "localizedObjectAnnotations": [],
  "safeSearchAnnotation": {
    "adult": "VERY_UNLIKELY",
    "spoof": "UNLIKELY",
    "medical": "VERY_UNLIKELY",
    "violence": "VERY_UNLIKELY",
    "racy": "VERY_UNLIKELY",
    "adultConfidence": 0,
    "spoofConfidence": 0,
    "medicalConfidence": 0,
    "violenceConfidence": 0,
    "racyConfidence": 0,
    "nsfwConfidence": 0
  },
  "imagePropertiesAnnotation": {
    "dominantColors": {
      "colors": [
        {
          "color": {
            "red": 203,
            "green": 201,
            "blue": 201,
            "alpha": null
          },
          "score": 0.4175916016101837,
          "pixelFraction": 0.44456374645233154
        },
        ✄ - - - ✄
      ]
    }
  },
  "error": null,
  "cropHintsAnnotation": {
    "cropHints": [
      {
        "boundingPoly": {
          "vertices": [
            { "x": 0, "y": 118 },
            { "x": 1177, "y": 118 },
            { "x": 1177, "y": 783 },
            { "x": 0, "y": 783 }
          ],
          "normalizedVertices": []
        },
        "confidence": 0.41695669293403625,
        "importanceFraction": 1
      }
    ]
  },
  "fullTextAnnotation": null,
  "webDetection": null,
  "productSearchResults": null,
  "context": null
}

ถ้าไม่มีการส่งคืนข้อผิดพลาด เราสามารถดำเนินการต่อได้ ด้วยเหตุนี้เราจึงมีข้อผิดพลาดนี้หากบล็อก:

if (responses.size() == 0) {
    logger.info("No response received from Vision API.");
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
}

AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
    logger.info("Error: " + response.getError().getMessage());
    return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
}

เราจะทำป้ายกำกับของสิ่งต่างๆ หมวดหมู่ หรือธีมที่จำได้ในภาพ

List<String> labels = response.getLabelAnnotationsList().stream()
    .map(annotation -> annotation.getDescription())
    .collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
    logger.info("- " + label);
}

เราอยากทราบสีที่โดดเด่นของรูปภาพ

String mainColor = "#FFFFFF";
ImageProperties imgProps = response.getImagePropertiesAnnotation();
if (imgProps.hasDominantColors()) {
    DominantColorsAnnotation colorsAnn = imgProps.getDominantColors();
    ColorInfo colorInfo = colorsAnn.getColors(0);

    mainColor = rgbHex(
        colorInfo.getColor().getRed(), 
        colorInfo.getColor().getGreen(), 
        colorInfo.getColor().getBlue());

    logger.info("Color: " + mainColor);
}

ลองดูว่าภาพนั้นปลอดภัยไหม:

boolean isSafe = false;
if (response.hasSafeSearchAnnotation()) {
    SafeSearchAnnotation safeSearch = response.getSafeSearchAnnotation();

    isSafe = Stream.of(
        safeSearch.getAdult(), safeSearch.getMedical(), safeSearch.getRacy(),
        safeSearch.getSpoof(), safeSearch.getViolence())
    .allMatch( likelihood -> 
        likelihood != Likelihood.LIKELY && likelihood != Likelihood.VERY_LIKELY
    );

    logger.info("Safe? " + isSafe);
}

เรากำลังตรวจสอบลักษณะเฉพาะของผู้ใหญ่ / การปลอมแปลง / การแพทย์ / ความรุนแรง / การยั่วยวนเพื่อดูว่ามีลักษณะมีแนวโน้มหรือมีแนวโน้มมากหรือไม่

หากผลของฟีเจอร์ค้นหาปลอดภัยถูกต้อง เราสามารถจัดเก็บข้อมูลเมตาใน Firestore ได้ ดังนี้

// Saving result to Firestore
if (isSafe) {
          ApiFuture<WriteResult> writeResult = 
               eventService.storeImage(fileName, labels,
                                       mainColor);
          logger.info("Picture metadata saved in Firestore at " + 
               writeResult.get().getUpdateTime());
}
...
  public ApiFuture<WriteResult> storeImage(String fileName, 
                                           List<String> labels, 
                                           String mainColor) {
    FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance();
    Firestore pictureStore = firestoreOptions.getService();

    DocumentReference doc = pictureStore.collection("pictures").document(fileName);

    Map<String, Object> data = new HashMap<>();
    data.put("labels", labels);
    data.put("color", mainColor);
    data.put("created", new Date());

    return doc.set(data, SetOptions.merge());
  }

10. สร้างรูปภาพของแอปด้วย GraalVM

ในขั้นตอนที่ไม่บังคับนี้ คุณจะสร้าง JIT based app image แล้วตามด้วย Native Java app image โดยใช้ GraalVM

หากต้องการเรียกใช้บิลด์ คุณจะต้องตรวจสอบว่าได้ติดตั้งและกําหนดค่า JDK และเครื่องมือสร้างรูปภาพเนทีฟที่เหมาะสมแล้ว ซึ่งมีหลายตัวเลือกให้ใช้งาน

To start ให้ดาวน์โหลด GraalVM 22.3.x Community Edition และทำตามวิธีการในหน้าการติดตั้ง GraalVM

ขั้นตอนนี้สามารถทำได้ง่ายมากด้วยความช่วยเหลือของ SDKMAN!

หากต้องการติดตั้งการกระจาย JDK ที่เหมาะสมด้วย SDKman ให้เริ่มต้นโดยใช้คำสั่งติดตั้ง ดังนี้

sdk install java 17.0.8-graal

สั่งให้ SDKman ใช้เวอร์ชันนี้สำหรับทั้งบิลด์ JIT และ AOT:

sdk use java 17.0.8-graal

เพื่อความสะดวกของคุณ คุณสามารถติดตั้ง GraalVM และยูทิลิตีรูปภาพแบบเนทีฟใน Cloudshell ด้วยคำสั่งง่ายๆ ต่อไปนี้

# download GraalVM
wget https://download.oracle.com/graalvm/17/latest/graalvm-jdk-17_linux-x64_bin.tar.gz 
tar -xzf graalvm-jdk-17_linux-x64_bin.tar.gz

ls -lart

# configure Java 17 and GraalVM for Java 17
# note the name of the latest GraalVM version, as unpacked by the tar command
echo Existing JVM: $JAVA_HOME
cd graalvm-jdk-17.0.8+9.1

export JAVA_HOME=$PWD
cd bin
export PATH=$PWD:$PATH

echo JAVA HOME: $JAVA_HOME
echo PATH: $PATH

cd ../..

# validate the version with
java -version 

# observe
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, mixed mode, sharing)

ขั้นแรก ให้ตั้งค่าตัวแปรสภาพแวดล้อมโปรเจ็กต์ GCP ดังนี้

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)

จากนั้นคุณสามารถไปที่ไดเรกทอรีที่มีบริการเพื่อเริ่มสร้าง Lab:

cd serverless-photosharing-workshop/services/image-analysis/java

สร้างอิมเมจแอปพลิเคชัน JIT ด้วยคำสั่งต่อไปนี้

./mvnw package

สังเกตบันทึกของบิลด์ในเทอร์มินัลด้วยคำสั่งต่อไปนี้

...
[INFO] Results:
[INFO] 
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-jar-plugin:3.3.0:jar (default-jar) @ image-analysis ---
[INFO] Building jar: /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis-0.0.1.jar
[INFO] 
[INFO] --- spring-boot-maven-plugin:3.2.0-M3:repackage (repackage) @ image-analysis ---
[INFO] Replacing main artifact /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis-0.0.1.jar with repackaged archive, adding nested dependencies in BOOT-INF/.
[INFO] The original artifact has been renamed to /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis-0.0.1.jar.original
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  15.335 s
[INFO] Finished at: 2023-10-10T19:33:25Z
[INFO] ------------------------------------------------------------------------

สร้างรูปภาพเนทีฟ(ใช้ AOT):.

./mvnw native:compile -Pnative

สังเกตบันทึกของบิลด์ในเทอร์มินัล รวมถึงบันทึกของบิลด์อิมเมจในเครื่อง

โปรดทราบว่าบิลด์จะใช้เวลานานกว่าเล็กน้อย ทั้งนี้ขึ้นอยู่กับเครื่องที่คุณกำลังทดสอบ

...
[2/7] Performing analysis...  [*********]                                                              (124.5s @ 4.53GB)
  29,732 (93.19%) of 31,905 classes reachable
  60,161 (70.30%) of 85,577 fields reachable
 261,973 (67.29%) of 389,319 methods reachable
   2,940 classes, 2,297 fields, and 97,421 methods registered for reflection
      81 classes,    90 fields, and    62 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/7] Building universe...                                                                              (11.7s @ 4.67GB)
[4/7] Parsing methods...      [***]                                                                      (6.1s @ 5.91GB)
[5/7] Inlining methods...     [****]                                                                     (4.5s @ 4.39GB)
[6/7] Compiling methods...    [******]                                                                  (35.3s @ 4.60GB)
[7/7] Creating image...                                                                                 (12.9s @ 4.61GB)
  80.08MB (47.43%) for code area:   190,483 compilation units
  73.81MB (43.72%) for image heap:  660,125 objects and 189 resources
  14.95MB ( 8.86%) for other data
 168.84MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area:                               Top 10 object types in image heap:
   2.66MB com.google.cloud.vision.v1p4beta1                   18.51MB byte[] for code metadata
   2.60MB com.google.cloud.vision.v1                           9.27MB java.lang.Class
   2.49MB com.google.protobuf                                  7.34MB byte[] for reflection metadata
   2.40MB com.google.cloud.vision.v1p3beta1                    6.35MB byte[] for java.lang.String
   2.17MB com.google.storage.v2                                5.72MB java.lang.String
   2.12MB com.google.firestore.v1                              4.46MB byte[] for embedded resources
   1.64MB sun.security.ssl                                     4.30MB c.oracle.svm.core.reflect.SubstrateMethodAccessor
   1.51MB i.g.xds.shaded.io.envoyproxy.envoy.config.core.v3    4.27MB byte[] for general heap data
   1.47MB com.google.cloud.vision.v1p2beta1                    2.50MB com.oracle.svm.core.hub.DynamicHubCompanion
   1.34MB i.g.x.shaded.io.envoyproxy.envoy.config.route.v3     1.17MB java.lang.Object[]
  58.34MB for 977 more packages                                9.19MB for 4667 more object types
------------------------------------------------------------------------------------------------------------------------
                        13.5s (5.7% of total time) in 75 GCs | Peak RSS: 9.44GB | CPU load: 6.13
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis (executable)
 /home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis.build_artifacts.txt (txt)
========================================================================================================================
Finished generating '/home/user/serverless-photosharing-workshop/services/image-analysis/java/target/image-analysis' in 3m 57s.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  04:28 min
[INFO] Finished at: 2023-10-10T19:53:30Z
[INFO] ------------------------------------------------------------------------

11. สร้างและเผยแพร่อิมเมจคอนเทนเนอร์

ลองสร้างอิมเมจคอนเทนเนอร์ใน 2 เวอร์ชันที่แตกต่างกัน ได้แก่ เวอร์ชันหนึ่งเป็น JIT image และอีกเวอร์ชันเป็น Native Java image

ขั้นแรก ให้ตั้งค่าตัวแปรสภาพแวดล้อมโปรเจ็กต์ GCP ดังนี้

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)

สร้างอิมเมจ JIT:.

./mvnw spring-boot:build-image -Pji

สังเกตบันทึกของบิลด์ในเทอร์มินัลด้วยคำสั่งต่อไปนี้

[INFO]     [creator]     Timer: Saving docker.io/library/image-analysis-maven-jit:latest... started at 2023-10-10T20:00:31Z
[INFO]     [creator]     *** Images (4c84122a1826):
[INFO]     [creator]           docker.io/library/image-analysis-maven-jit:latest
[INFO]     [creator]     Timer: Saving docker.io/library/image-analysis-maven-jit:latest... ran for 6.975913605s and ended at 2023-10-10T20:00:38Z
[INFO]     [creator]     Timer: Exporter ran for 8.068588001s and ended at 2023-10-10T20:00:38Z
[INFO]     [creator]     Timer: Cache started at 2023-10-10T20:00:38Z
[INFO]     [creator]     Reusing cache layer 'paketo-buildpacks/syft:syft'
[INFO]     [creator]     Adding cache layer 'buildpacksio/lifecycle:cache.sbom'
[INFO]     [creator]     Timer: Cache ran for 200.449002ms and ended at 2023-10-10T20:00:38Z
[INFO] 
[INFO] Successfully built image 'docker.io/library/image-analysis-maven-jit:latest'
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  43.887 s
[INFO] Finished at: 2023-10-10T20:00:39Z
[INFO] ------------------------------------------------------------------------

สร้างอิมเมจ AOT(เนทีฟ)

./mvnw spring-boot:build-image -Pnative

สังเกตบันทึกของบิลด์ในเทอร์มินัล รวมถึงบันทึกของบิลด์อิมเมจในเครื่อง

หมายเหตุ:

  • บิลด์จะใช้เวลานานกว่าเล็กน้อย ขึ้นอยู่กับเครื่องที่คุณกำลังทดสอบ
  • ระบบสามารถบีบอัดรูปภาพเพิ่มเติมด้วย UPX แต่จะส่งผลเสียเล็กน้อยต่อประสิทธิภาพในการเริ่มต้นใช้งาน ดังนั้น บิลด์นี้จึงไม่ได้ใช้ UPX แต่จะมีผลเสียเล็กน้อยเสมอ
...
[INFO]     [creator]     Saving docker.io/library/image-analysis-maven-native:latest...
[INFO]     [creator]     *** Images (13167702674e):
[INFO]     [creator]           docker.io/library/image-analysis-maven-native:latest
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/bellsoft-liberica:native-image-svm'
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/syft:syft'
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/native-image:native-image'
[INFO]     [creator]     Adding cache layer 'buildpacksio/lifecycle:cache.sbom'
[INFO] 
[INFO] Successfully built image 'docker.io/library/image-analysis-maven-native:latest'
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  03:37 min
[INFO] Finished at: 2023-10-10T20:05:16Z
[INFO] ------------------------------------------------------------------------

ตรวจสอบว่ารูปภาพสร้างขึ้นแล้วโดยทำดังนี้

docker images | grep image-analysis

ติดแท็กและพุชรูปภาพ 2 รูปไปยัง GCR ดังนี้

# JIT image
docker tag image-analysis-maven-jit gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-jit
docker push gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-jit

# Native(AOT) image
docker tag image-analysis-maven-native gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-native
docker push  gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-native

12. ทำให้ใช้งานได้กับ Cloud Run

ได้เวลาทำให้บริการใช้งานได้แล้ว

คุณจะต้องทำให้บริการใช้งานได้ 2 ครั้ง โดยครั้งแรกจะใช้อิมเมจ JIT และครั้งที่ 2 โดยใช้อิมเมจ AOT(เนทีฟ) การทำให้บริการใช้งานได้ทั้ง 2 รายการจะประมวลผลอิมเมจเดียวกันจากที่เก็บข้อมูลพร้อมกัน เพื่อจุดประสงค์ในการเปรียบเทียบ

ขั้นแรก ให้ตั้งค่าตัวแปรสภาพแวดล้อมโปรเจ็กต์ GCP ดังนี้

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
gcloud config set project ${GOOGLE_CLOUD_PROJECT}
gcloud config set run/region 
gcloud config set run/platform managed
gcloud config set eventarc/location europe-west1

ทำให้อิมเมจ JIT ใช้งานได้และสังเกตบันทึกการทำให้ใช้งานได้ในคอนโซลโดยใช้คำสั่งต่อไปนี้

gcloud run deploy image-analysis-jit \
     --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-jit \
     --region europe-west1 \
     --memory 2Gi --allow-unauthenticated

...
Deploying container to Cloud Run service [image-analysis-jit] in project [...] region [europe-west1]
✓ Deploying... Done.                                                                                                                                                               
  ✓ Creating Revision...                                                                                                                                                           
  ✓ Routing traffic...                                                                                                                                                             
  ✓ Setting IAM Policy...                                                                                                                                                          
Done.                                                                                                                                                                              
Service [image-analysis-jit] revision [image-analysis-jvm-00009-huc] has been deployed and is serving 100 percent of traffic.
Service URL: https://image-analysis-jit-...-ew.a.run.app

ทำให้อิมเมจเนทีฟใช้งานได้และสังเกตบันทึกการทำให้ใช้งานได้ในคอนโซล โดยทำดังนี้

gcloud run deploy image-analysis-native \
     --image gcr.io/${GOOGLE_CLOUD_PROJECT}/image-analysis-maven-native \
     --region europe-west1 \
     --memory 2Gi --allow-unauthenticated 
...
Deploying container to Cloud Run service [image-analysis-native] in project [...] region [europe-west1]
✓ Deploying... Done.                                                                                                                                                               
  ✓ Creating Revision...                                                                                                                                                           
  ✓ Routing traffic...                                                                                                                                                             
  ✓ Setting IAM Policy...                                                                                                                                                          
Done.                                                                                                                                                                              
Service [image-analysis-native] revision [image-analysis-native-00005-ben] has been deployed and is serving 100 percent of traffic.
Service URL: https://image-analysis-native-...-ew.a.run.app

13. ตั้งค่าทริกเกอร์ Eventarc

Eventarc นำเสนอโซลูชันที่เป็นมาตรฐานเพื่อจัดการโฟลว์ของการเปลี่ยนแปลงสถานะ ซึ่งเรียกว่าเหตุการณ์ ระหว่าง Microservice ที่แยกส่วนออกจากกัน เมื่อทริกเกอร์ Eventarc จะกำหนดเส้นทางเหตุการณ์เหล่านี้ผ่านการสมัครใช้บริการ Pub/Sub ไปยังปลายทางต่างๆ (ในเอกสารนี้ดูปลายทางเหตุการณ์) ไปพร้อมกับจัดการการนำส่ง ความปลอดภัย การให้สิทธิ์ ความสามารถในการสังเกต และการจัดการข้อผิดพลาดให้คุณ

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

ขั้นแรก ให้ตั้งค่าตัวแปรสภาพแวดล้อมโปรเจ็กต์ GCP ดังนี้

export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
gcloud config set project ${GOOGLE_CLOUD_PROJECT}
gcloud config set run/region 
gcloud config set run/platform managed
gcloud config set eventarc/location europe-west1

ให้สิทธิ์ pubsub.publisher แก่บัญชีบริการ Cloud Storage:

SERVICE_ACCOUNT="$(gsutil kms serviceaccount -p ${GOOGLE_CLOUD_PROJECT})"

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member="serviceAccount:${SERVICE_ACCOUNT}" \
    --role='roles/pubsub.publisher'

ตั้งค่าทริกเกอร์ Eventarc สำหรับทั้งอิมเมจบริการ JIT และเนทีฟเพื่อประมวลผลรูปภาพ ดังนี้

gcloud eventarc triggers list --location=eu

gcloud eventarc triggers create image-analysis-jit-trigger \
     --destination-run-service=image-analysis-jit \
     --destination-run-region=europe-west1 \
     --location=eu \
     --event-filters="type=google.cloud.storage.object.v1.finalized" \
     --event-filters="bucket=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}" \
     --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com

gcloud eventarc triggers create image-analysis-native-trigger \
     --destination-run-service=image-analysis-native \
     --destination-run-region=europe-west1 \
     --location=eu \
     --event-filters="type=google.cloud.storage.object.v1.finalized" \
     --event-filters="bucket=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}" \
     --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com    

สังเกตว่ามีการสร้างทริกเกอร์ 2 ตัว ดังนี้

gcloud eventarc triggers list --location=eu

14. เวอร์ชันบริการทดสอบ

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

กลับไปที่ Cloud Storage แล้วคลิกที่เก็บข้อมูลที่เราสร้างไว้เมื่อเริ่มต้นห้องทดลอง

33442485a1d76921.png

เมื่ออยู่ในหน้ารายละเอียดที่เก็บข้อมูล ให้คลิกปุ่ม Upload files เพื่ออัปโหลดรูปภาพ

เช่น รูปภาพ GeekHour.jpeg จะมาพร้อมกับฐานของโค้ดภายใต้ /services/image-analysis/java เลือกรูปภาพแล้วกด Open button:

d57529452f62bd32.png

ตอนนี้คุณตรวจสอบการดำเนินการของบริการได้แล้ว โดยเริ่มจาก image-analysis-jit ตามด้วย image-analysis-native

จาก "แฮมเบอร์เกอร์" (⋮) ให้ไปที่บริการ Cloud Run > image-analysis-jit

คลิกบันทึกและสังเกตผลลัพธ์ ดังนี้

ae1a4a94c7c7a166.png

อันที่จริง ในรายการบันทึก เราพบว่ามีการเรียกบริการ JIT image-analysis-jit

บันทึกจะระบุการเริ่มต้นและสิ้นสุดการเรียกใช้บริการ และระหว่างนั้น เราจะเห็นบันทึกที่เราใส่ไว้ในฟังก์ชันพร้อมด้วยคำสั่งบันทึกที่ระดับ INFO เราพบว่า

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

คุณจะทำขั้นตอนนี้ซ้ำสำหรับบริการ image-analysis-native

จาก "แฮมเบอร์เกอร์" (⋮) ให้ไปที่บริการ Cloud Run > image-analysis-native

คลิกบันทึกและสังเกตผลลัพธ์ ดังนี้

4afe22833c1fd14c.png

คุณจะต้องตรวจสอบว่าข้อมูลเมตาของรูปภาพจัดเก็บอยู่ใน Fiorestore หรือไม่

อีกครั้งจาก "แฮมเบอร์เกอร์" (src) ไปที่ส่วน Firestore ในส่วนย่อย Data (แสดงโดยค่าเริ่มต้น) คุณจะเห็นคอลเล็กชัน pictures ที่มีเอกสารใหม่เพิ่มเข้ามา ซึ่งจะสอดคล้องกับภาพที่คุณเพิ่งอัปโหลด

82d6c468956e7cfc.png

15. ล้างข้อมูล (ไม่บังคับ)

หากไม่ต้องการใช้ห้องทดลองอื่นๆ ในชุดนี้ต่อ คุณสามารถล้างทรัพยากรเพื่อประหยัดต้นทุนและเป็นพลเมืองระบบคลาวด์ที่ดีโดยรวม คุณสามารถล้างทรัพยากรแต่ละรายการได้ดังนี้

ลบที่เก็บข้อมูล

gsutil rb gs://${BUCKET_PICTURES}

ลบฟังก์ชัน

gcloud functions delete picture-uploaded --region europe-west1 -q

ลบคอลเล็กชัน Firestore โดยเลือก "ลบคอลเล็กชัน" ออกจากคอลเล็กชัน ดังนี้

6cc86a7b88fdb4d3.png

หรือจะลบทั้งโปรเจ็กต์ก็ได้ โดยทำดังนี้

gcloud projects delete ${GOOGLE_CLOUD_PROJECT} 

16. ยินดีด้วย

ยินดีด้วย คุณใช้งานบริการจัดการคีย์แรกของโปรเจ็กต์เรียบร้อยแล้ว

หัวข้อที่ครอบคลุม

  • Cloud Storage
  • Cloud Run
  • Cloud Vision API
  • Cloud Firestore
  • รูปภาพ Java เนทีฟ

ขั้นตอนถัดไป