1. ภาพรวม
ใน Code Lab ครั้งแรก คุณจะต้องเก็บรูปภาพไว้ในที่เก็บข้อมูล การดำเนินการนี้จะสร้างเหตุการณ์การสร้างไฟล์ที่จะจัดการโดยบริการที่ทำให้ใช้งานได้ใน Cloud Run บริการจะเรียกใช้ Vision API เพื่อทำการวิเคราะห์รูปภาพและบันทึกผลลัพธ์ในพื้นที่เก็บข้อมูล
สิ่งที่คุณจะได้เรียนรู้
- Cloud Storage
- Cloud Run
- Cloud Vision API
- Cloud Firestore
2. การตั้งค่าและข้อกำหนด
การตั้งค่าสภาพแวดล้อมตามเวลาที่สะดวก
- ลงชื่อเข้าใช้ Google Cloud Console และสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี
- ชื่อโครงการคือชื่อที่แสดงของผู้เข้าร่วมโปรเจ็กต์นี้ เป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ โดยคุณจะอัปเดตวิธีการชำระเงินได้ทุกเมื่อ
- รหัสโปรเจ็กต์จะไม่ซ้ำกันในทุกโปรเจ็กต์ของ Google Cloud และจะเปลี่ยนแปลงไม่ได้ (เปลี่ยนแปลงไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ปกติแล้วคุณไม่สนว่าอะไรเป็นอะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยปกติจะระบุเป็น
PROJECT_ID
) หากคุณไม่ชอบรหัสที่สร้างขึ้น คุณสามารถสร้างรหัสแบบสุ่มอื่นได้ หรือคุณจะลองดำเนินการเองแล้วดูว่าพร้อมให้ใช้งานหรือไม่ คุณจะเปลี่ยนแปลงหลังจากขั้นตอนนี้ไม่ได้และจะยังคงอยู่ตลอดระยะเวลาของโปรเจ็กต์ - สำหรับข้อมูลของคุณ ค่าที่ 3 คือหมายเลขโปรเจ็กต์ ซึ่ง API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 ค่าเหล่านี้ในเอกสารประกอบ
- ถัดไป คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของระบบคลาวด์ การใช้งาน Codelab นี้จะไม่มีค่าใช้จ่ายใดๆ หากมี หากต้องการปิดทรัพยากรเพื่อหลีกเลี่ยงการเรียกเก็บเงินที่นอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่คุณสร้างหรือลบโปรเจ็กต์ได้ ผู้ใช้ Google Cloud ใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรี$300 USD
เริ่มต้น Cloud Shell
แม้ว่าคุณจะดำเนินการ Google Cloud จากระยะไกลได้จากแล็ปท็อป แต่คุณจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมแบบบรรทัดคำสั่งที่ทำงานในระบบคลาวด์ใน Codelab นี้
จากคอนโซล Google Cloud ให้คลิกไอคอน Cloud Shell ในแถบเครื่องมือด้านขวาบน ดังนี้
การจัดสรรและเชื่อมต่อกับสภาพแวดล้อมนี้ควรใช้เวลาเพียงครู่เดียว เมื่อเสร็จแล้ว คุณจะเห็นข้อมูลต่อไปนี้
เครื่องเสมือนนี้เต็มไปด้วยเครื่องมือการพัฒนาทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักขนาด 5 GB ที่ใช้งานได้ต่อเนื่องและทำงานบน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพของเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก งานทั้งหมดใน Codelab นี้ทำได้ในเบราว์เซอร์ คุณไม่จำเป็นต้องติดตั้งอะไรเลย
3. เปิดใช้ API
สำหรับห้องทดลองนี้ คุณจะใช้ Cloud Functions และ Vision API แต่ต้องเปิดใช้ใน Cloud Console หรือด้วย gcloud
ก่อน
หากต้องการเปิดใช้ Vision API ใน Cloud Console ให้ค้นหา Cloud Vision API
ในแถบค้นหา
คุณจะเข้าสู่หน้า Cloud Vision API:
คลิกปุ่ม 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
ตั้งชื่อที่เก็บข้อมูล
คลิกปุ่ม CREATE BUCKET
คลิก CONTINUE
เลือกตำแหน่ง
สร้างที่เก็บข้อมูลหลายภูมิภาคในภูมิภาคที่คุณเลือก (ที่นี่ Europe
)
คลิก CONTINUE
เลือกคลาสพื้นที่เก็บข้อมูลเริ่มต้น
เลือกคลาสพื้นที่เก็บข้อมูล Standard
สำหรับข้อมูลของคุณ
คลิก CONTINUE
ตั้งค่าการควบคุมการเข้าถึง
เนื่องจากคุณจะต้องใช้งานรูปภาพที่เข้าถึงได้แบบสาธารณะ คุณต้องการให้รูปภาพทั้งหมดที่จัดเก็บในที่เก็บข้อมูลนี้มีการควบคุมการเข้าถึงแบบเดียวกัน
เลือกตัวเลือกการควบคุมการเข้าถึง Uniform
คลิก CONTINUE
ตั้งค่าการป้องกัน/การเข้ารหัส
เก็บค่าเริ่มต้นไว้ (Google-managed key)
เนื่องจากคุณจะไม่ใช้คีย์การเข้ารหัสของคุณเอง
คลิก CREATE
เพื่อสร้างที่เก็บข้อมูลขั้นสุดท้าย
เพิ่ม allUsers เป็นผู้ดูพื้นที่เก็บข้อมูล
ไปที่แท็บ Permissions
:
เพิ่มสมาชิก allUsers
ไปยังที่เก็บข้อมูล โดยมีบทบาทเป็น Storage > Storage Object Viewer
ดังนี้
คลิก 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
สาธารณะ
ทดสอบว่าคุณสามารถอัปโหลดรูปภาพไปยังที่เก็บข้อมูล และภาพที่อัปโหลดจะปรากฏต่อสาธารณะ ตามที่อธิบายไว้ในขั้นตอนก่อนหน้า
6. ทดสอบสิทธิ์เข้าถึงที่เก็บข้อมูลแบบสาธารณะ
การกลับไปยังเบราว์เซอร์พื้นที่เก็บข้อมูล คุณจะเห็นที่เก็บข้อมูลในรายการ โดยมี "สาธารณะ" (รวมถึงป้ายเตือนที่บอกว่าทุกคนมีสิทธิ์เข้าถึงเนื้อหาของที่เก็บข้อมูลนั้น)
ตอนนี้ที่เก็บข้อมูลของคุณพร้อมรับรูปภาพแล้ว
หากคลิกชื่อที่เก็บข้อมูล คุณจะเห็นรายละเอียดที่เก็บข้อมูล
จากจุดนี้ คุณสามารถลองใช้ปุ่ม Upload files
เพื่อทดสอบว่าคุณเพิ่มรูปภาพลงในที่เก็บข้อมูลได้ ป๊อปอัปตัวเลือกไฟล์จะขอให้คุณเลือกไฟล์ เมื่อเลือกแล้ว ระบบจะอัปโหลดไฟล์ดังกล่าวไปยังที่เก็บข้อมูลของคุณ และคุณจะเห็นสิทธิ์เข้าถึง public
ที่ระบบระบุแหล่งที่มาไปยังไฟล์ใหม่นี้โดยอัตโนมัติอีกครั้ง
คุณจะเห็นไอคอนลิงก์ขนาดเล็กที่ป้ายกำกับการเข้าถึง Public
ด้วย เมื่อคลิกที่รูปภาพ เบราว์เซอร์ของคุณจะนำทางไปยัง URL สาธารณะของรูปภาพนั้น ซึ่งจะอยู่ในรูปแบบ:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
โดย BUCKET_NAME
เป็นชื่อที่ไม่ซ้ำกันทั่วโลกซึ่งคุณได้เลือกไว้สำหรับที่เก็บข้อมูล และตามด้วยชื่อไฟล์ของรูปภาพ
เมื่อคลิกช่องทำเครื่องหมายข้างชื่อภาพ ปุ่ม DELETE
จะเปิดการใช้งาน และคุณจะสามารถลบรูปภาพแรกนี้ได้
7. เตรียมฐานข้อมูล
คุณจะจัดเก็บข้อมูลเกี่ยวกับรูปภาพที่ได้รับจาก Vision API ไว้ในฐานข้อมูล Cloud Firestore ซึ่งเป็นฐานข้อมูลเอกสาร NoSQL ที่ดำเนินการบนระบบคลาวด์และทำงานได้อย่างรวดเร็ว มีการจัดการครบวงจร เตรียมฐานข้อมูลด้วยการไปที่ส่วน Firestore
ของ Cloud Console:
โดยมี 2 ตัวเลือก ได้แก่ Native mode
หรือ Datastore mode
ใช้โหมดเนทีฟซึ่งมีฟีเจอร์เพิ่มเติม เช่น การรองรับการใช้งานออฟไลน์และการซิงค์ข้อมูลแบบเรียลไทม์
คลิก SELECT NATIVE MODE
เลือกหลายภูมิภาค (ที่นี่ในยุโรป แต่อย่างน้อยควรเป็นภูมิภาคเดียวกับฟังก์ชันและที่เก็บข้อมูลของพื้นที่เก็บข้อมูล)
คลิกปุ่ม CREATE DATABASE
เมื่อสร้างฐานข้อมูลแล้ว คุณจะเห็นสิ่งต่อไปนี้
สร้างคอลเล็กชันใหม่โดยคลิกปุ่ม + START COLLECTION
ชื่อคอลเล็กชัน pictures
คุณไม่ต้องสร้างเอกสาร คุณจะเพิ่มรูปภาพเหล่านั้นโดยใช้โปรแกรมเมื่อมีการจัดเก็บรูปภาพใหม่ใน Cloud Storage และวิเคราะห์โดย Vision API
คลิก Save
Firestore สร้างเอกสารเริ่มต้นรายการแรกในคอลเล็กชันที่สร้างขึ้นใหม่ คุณลบเอกสารดังกล่าวได้อย่างปลอดภัยเนื่องจากไม่มีข้อมูลที่เป็นประโยชน์ใดๆ ดังต่อไปนี้
เอกสารที่จะสร้างขึ้นแบบเป็นโปรแกรมในคอลเล็กชันของเราประกอบด้วย 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
ในคอลัมน์การนำทางด้านซ้าย แล้วสร้างดัชนีผสมดังที่แสดงด้านล่างก็ได้
คลิก Create
การสร้างดัชนีอาจใช้เวลาสักครู่
8. โคลนโค้ด
โคลนโค้ดหากยังไม่ได้ทำใน Code Lab ก่อนหน้านี้:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
จากนั้นคุณสามารถไปที่ไดเรกทอรีที่มีบริการเพื่อเริ่มสร้าง Lab:
cd serverless-photosharing-workshop/services/image-analysis/java
คุณจะมีการจัดวางไฟล์ดังต่อไปนี้สำหรับบริการ:
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
แล้วคลิกที่เก็บข้อมูลที่เราสร้างไว้เมื่อเริ่มต้นห้องทดลอง
เมื่ออยู่ในหน้ารายละเอียดที่เก็บข้อมูล ให้คลิกปุ่ม Upload files
เพื่ออัปโหลดรูปภาพ
เช่น รูปภาพ GeekHour.jpeg
จะมาพร้อมกับฐานของโค้ดภายใต้ /services/image-analysis/java
เลือกรูปภาพแล้วกด Open button
:
ตอนนี้คุณตรวจสอบการดำเนินการของบริการได้แล้ว โดยเริ่มจาก image-analysis-jit
ตามด้วย image-analysis-native
จาก "แฮมเบอร์เกอร์" (⋮) ให้ไปที่บริการ Cloud Run > image-analysis-jit
คลิกบันทึกและสังเกตผลลัพธ์ ดังนี้
อันที่จริง ในรายการบันทึก เราพบว่ามีการเรียกบริการ JIT image-analysis-jit
บันทึกจะระบุการเริ่มต้นและสิ้นสุดการเรียกใช้บริการ และระหว่างนั้น เราจะเห็นบันทึกที่เราใส่ไว้ในฟังก์ชันพร้อมด้วยคำสั่งบันทึกที่ระดับ INFO เราพบว่า
- รายละเอียดของเหตุการณ์ที่ทริกเกอร์ฟังก์ชันของเรา
- ผลลัพธ์ดิบจากการเรียก Vision API
- ป้ายกำกับที่พบในภาพที่เราอัปโหลด
- ข้อมูลสีที่โดดเด่น
- รูปภาพปลอดภัยที่จะแสดงหรือไม่
- และท้ายที่สุดแล้วระบบได้จัดเก็บข้อมูลเมตาเกี่ยวกับรูปภาพไว้ใน Firestore
คุณจะทำขั้นตอนนี้ซ้ำสำหรับบริการ image-analysis-native
จาก "แฮมเบอร์เกอร์" (⋮) ให้ไปที่บริการ Cloud Run > image-analysis-native
คลิกบันทึกและสังเกตผลลัพธ์ ดังนี้
คุณจะต้องตรวจสอบว่าข้อมูลเมตาของรูปภาพจัดเก็บอยู่ใน Fiorestore หรือไม่
อีกครั้งจาก "แฮมเบอร์เกอร์" (src) ไปที่ส่วน Firestore
ในส่วนย่อย Data
(แสดงโดยค่าเริ่มต้น) คุณจะเห็นคอลเล็กชัน pictures
ที่มีเอกสารใหม่เพิ่มเข้ามา ซึ่งจะสอดคล้องกับภาพที่คุณเพิ่งอัปโหลด
15. ล้างข้อมูล (ไม่บังคับ)
หากไม่ต้องการใช้ห้องทดลองอื่นๆ ในชุดนี้ต่อ คุณสามารถล้างทรัพยากรเพื่อประหยัดต้นทุนและเป็นพลเมืองระบบคลาวด์ที่ดีโดยรวม คุณสามารถล้างทรัพยากรแต่ละรายการได้ดังนี้
ลบที่เก็บข้อมูล
gsutil rb gs://${BUCKET_PICTURES}
ลบฟังก์ชัน
gcloud functions delete picture-uploaded --region europe-west1 -q
ลบคอลเล็กชัน Firestore โดยเลือก "ลบคอลเล็กชัน" ออกจากคอลเล็กชัน ดังนี้
หรือจะลบทั้งโปรเจ็กต์ก็ได้ โดยทำดังนี้
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
16. ยินดีด้วย
ยินดีด้วย คุณใช้งานบริการจัดการคีย์แรกของโปรเจ็กต์เรียบร้อยแล้ว
หัวข้อที่ครอบคลุม
- Cloud Storage
- Cloud Run
- Cloud Vision API
- Cloud Firestore
- รูปภาพ Java เนทีฟ