1. ภาพรวม
ใน Code Lab ครั้งแรก คุณจะต้องอัปโหลดภาพในที่เก็บข้อมูล การดำเนินการนี้จะสร้างเหตุการณ์การสร้างไฟล์ที่ฟังก์ชันจะจัดการ ฟังก์ชันดังกล่าวจะเรียกใช้ Vision API เพื่อทำการวิเคราะห์รูปภาพและบันทึกผลลัพธ์ลงในพื้นที่เก็บข้อมูล
สิ่งที่คุณจะได้เรียนรู้
- Cloud Storage
- Cloud Functions
- 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 Functions ด้วยโดยทำดังนี้
gcloud services enable cloudfunctions.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. สร้างฟังก์ชัน
ในขั้นตอนนี้ คุณจะได้สร้างฟังก์ชันที่ตอบสนองต่อเหตุการณ์การอัปโหลดรูปภาพ
ไปที่ส่วนCloud Functions
ของคอนโซล Google Cloud การเข้าชมนี้จะเปิดใช้บริการ Cloud Functions โดยอัตโนมัติ
คลิกที่ Create function
เลือกชื่อ (เช่น picture-uploaded
) และภูมิภาค (โปรดทราบว่าต้องสอดคล้องกับตัวเลือกภูมิภาคสำหรับที่เก็บข้อมูล):
ฟังก์ชันมี 2 ประเภท ได้แก่
- ฟังก์ชัน HTTP ที่สามารถเรียกใช้ผ่าน URL (เช่น API ของเว็บ)
- ฟังก์ชันเบื้องหลังที่เหตุการณ์บางอย่างทริกเกอร์ได้
คุณต้องการสร้างฟังก์ชันพื้นหลังที่ทริกเกอร์เมื่อมีการอัปโหลดไฟล์ใหม่ไปยังที่เก็บข้อมูล Cloud Storage
ของเรา ดังนี้
คุณสนใจประเภทเหตุการณ์ Finalize/Create
ซึ่งเป็นเหตุการณ์ที่จะทริกเกอร์เมื่อมีการสร้างหรืออัปเดตไฟล์ในที่เก็บข้อมูล
เลือกที่เก็บข้อมูลที่สร้างขึ้นก่อนหน้านี้เพื่อแจ้งให้ Cloud Functions รับการแจ้งเตือนเมื่อมีการสร้าง / อัปเดตไฟล์ในที่เก็บข้อมูลเฉพาะนี้
คลิก Select
เพื่อเลือกที่เก็บข้อมูลที่คุณสร้างไว้ก่อนหน้านี้ จากนั้นคลิก Save
ก่อนคลิก "ถัดไป" คุณจะขยายและแก้ไขค่าเริ่มต้น (หน่วยความจำ 256 MB) ได้ในส่วนการตั้งค่ารันไทม์ บิลด์ การเชื่อมต่อ และความปลอดภัย แล้วอัปเดตเป็น 1 GB
หลังจากคลิก Next
คุณจะปรับแต่งรันไทม์ ซอร์สโค้ด และจุดเริ่มต้นได้
เก็บ Inline editor
ไว้สำหรับฟังก์ชันนี้:
เลือกรันไทม์ของ Java เช่น Java 11
ซอร์สโค้ดประกอบด้วยไฟล์ Java
และไฟล์ Maven pom.xml
ที่มีข้อมูลเมตาและการอ้างอิงที่หลากหลาย
เก็บข้อมูลโค้ดเริ่มต้นไว้ซึ่งจะบันทึกชื่อไฟล์ของรูปภาพที่อัปโหลด
ในตอนนี้ โปรดเก็บชื่อของฟังก์ชันที่จะเรียกใช้ Example
ไว้สำหรับการทดสอบ
คลิกที่ Deploy
เพื่อสร้างและทำให้ฟังก์ชันใช้งานได้ เมื่อการติดตั้งใช้งานเสร็จสมบูรณ์ คุณจะเห็นเครื่องหมายถูกวงกลมสีเขียวในรายการฟังก์ชัน
8. ทดสอบฟังก์ชัน
ในขั้นตอนนี้ ให้ทดสอบว่าฟังก์ชันตอบสนองต่อเหตุการณ์พื้นที่เก็บข้อมูลหรือไม่
จาก "แฮมเบอร์เกอร์" (⋮) ให้กลับไปที่หน้า Storage
คลิกที่ที่เก็บข้อมูลรูปภาพ จากนั้นคลิก Upload files
เพื่ออัปโหลดรูปภาพ
โปรดนำทางอีกครั้งภายใน Cloud Console เพื่อไปที่หน้า Logging > Logs Explorer
ในตัวเลือก Log Fields
ให้เลือก Cloud Function
เพื่อดูบันทึกสำหรับฟังก์ชันของคุณโดยเฉพาะ เลื่อนลงตามฟิลด์บันทึกและคุณสามารถเลือกฟังก์ชันที่ต้องการเพื่อดูบันทึกที่เกี่ยวข้องกับฟังก์ชันอย่างละเอียด เลือกฟังก์ชัน picture-uploaded
คุณจะเห็นรายการในบันทึกที่กล่าวถึงการสร้างฟังก์ชัน เวลาเริ่มต้นและสิ้นสุดของฟังก์ชัน และข้อความบันทึกจริงของเรา:
ข้อความบันทึกของเราคือ Processing file: pic-a-daily-architecture-events.png
ซึ่งหมายความว่ามีการทริกเกอร์เหตุการณ์ที่เกี่ยวข้องกับการสร้างและจัดเก็บรูปภาพนี้ตามที่คาดไว้แล้ว
9. เตรียมฐานข้อมูล
คุณจะจัดเก็บข้อมูลเกี่ยวกับรูปภาพที่ได้รับจาก 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
การสร้างดัชนีอาจใช้เวลาสักครู่
10. อัปเดตฟังก์ชัน
กลับไปที่หน้า Functions
เพื่ออัปเดตฟังก์ชันเพื่อเรียกใช้ Vision API เพื่อวิเคราะห์รูปภาพและจัดเก็บข้อมูลเมตาใน Firestore
จาก "แฮมเบอร์เกอร์" (⋮) ไปยังส่วน Cloud Functions
จากนั้นคลิกชื่อฟังก์ชัน จากนั้นเลือกแท็บ Source
แล้วคลิกปุ่ม EDIT
ก่อนอื่น ให้แก้ไขไฟล์ pom.xml
ซึ่งแสดงรายการทรัพยากร Dependency ของฟังก์ชัน Java อัปเดตโค้ดเพื่อเพิ่มทรัพยากร Dependency ของ Cloud Vision API Maven ดังนี้
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cloudfunctions</groupId>
<artifactId>gcs-function</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.0.4</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>
</dependencies>
<!-- Required for Java 11 functions in the inline editor -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<excludes>
<exclude>.google/</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
ตอนนี้ทรัพยากร Dependency เป็นปัจจุบันแล้ว คุณจะทำงานกับโค้ดของฟังก์ชันได้ โดยอัปเดตไฟล์ Example.java
ด้วยโค้ดที่กำหนดเองของเรา
เลื่อนเมาส์เหนือไฟล์ Example.java
แล้วคลิกดินสอ เปลี่ยนชื่อแพ็กเกจและชื่อไฟล์เป็น src/main/java/fn/ImageAnalysis.java
แทนที่รหัสใน ImageAnalysis.java
ด้วยรหัสด้านล่าง ซึ่งจะอธิบายในขั้นตอนถัดไป
package fn;
import com.google.cloud.functions.*;
import com.google.cloud.vision.v1.*;
import com.google.cloud.vision.v1.Feature.Type;
import com.google.cloud.firestore.*;
import com.google.api.core.ApiFuture;
import java.io.*;
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.*;
import java.util.logging.Logger;
import fn.ImageAnalysis.GCSEvent;
public class ImageAnalysis implements BackgroundFunction<GCSEvent> {
private static final Logger logger = Logger.getLogger(ImageAnalysis.class.getName());
@Override
public void accept(GCSEvent event, Context context)
throws IOException, InterruptedException, ExecutionException {
String fileName = event.name;
String bucketName = event.bucket;
logger.info("New picture uploaded " + fileName);
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);
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result = vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
if (responses.size() == 0) {
logger.info("No response received from Vision API.");
return;
}
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return;
}
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);
}
// Saving result to Firestore
if (isSafe) {
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());
ApiFuture<WriteResult> writeResult = doc.set(data, SetOptions.merge());
logger.info("Picture metadata saved in Firestore at " + writeResult.get().getUpdateTime());
}
}
}
private static String rgbHex(float red, float green, float blue) {
return String.format("#%02x%02x%02x", (int)red, (int)green, (int)blue);
}
public static class GCSEvent {
String bucket;
String name;
}
}
11. สำรวจฟังก์ชัน
มาดูรายละเอียดเกี่ยวกับส่วนต่างๆ ที่น่าสนใจกัน
โดยก่อนอื่น เราจะรวมทรัพยากร Dependency เฉพาะในไฟล์ Maven pom.xml
ไลบรารีของไคลเอ็นต์ Google Java จะเผยแพร่ Bill-of-Materials(BOM)
เพื่อกำจัดความขัดแย้งของทรัพยากร Dependency ในการใช้งาน คุณไม่จำเป็นต้องระบุเวอร์ชันใดๆ สำหรับไลบรารีของไคลเอ็นต์ Google แต่ละรายการ
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
จากนั้นเราจะเตรียมไคลเอ็นต์สำหรับ Vision API ดังนี้
...
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
...
ตอนนี้มาถึงโครงสร้างของฟังก์ชัน เราเก็บข้อมูลจากเหตุการณ์ที่เข้ามาใหม่ในฟิลด์ที่เราสนใจ และแมปฟิลด์เหล่านั้นกับโครงสร้าง GCSEvent ที่เรากำหนดไว้ ดังนี้
...
public class ImageAnalysis implements BackgroundFunction<GCSEvent> {
@Override
public void accept(GCSEvent event, Context context)
throws IOException, InterruptedException,
ExecutionException {
...
public static class GCSEvent {
String bucket;
String name;
}
โปรดสังเกตลายเซ็น รวมถึงวิธีที่เราเรียกชื่อไฟล์และที่เก็บข้อมูลซึ่งเรียกใช้ Cloud Function
สำหรับการอ้างอิง เปย์โหลดเหตุการณ์มีลักษณะดังนี้
{
"bucket":"uploaded-pictures",
"contentType":"image/png",
"crc32c":"efhgyA==",
"etag":"CKqB956MmucCEAE=",
"generation":"1579795336773802",
"id":"uploaded-pictures/Screenshot.png/1579795336773802",
"kind":"storage#object",
"md5Hash":"PN8Hukfrt6C7IyhZ8d3gfQ==",
"mediaLink":"https://www.googleapis.com/download/storage/v1/b/uploaded-pictures/o/Screenshot.png?generation=1579795336773802&alt=media",
"metageneration":"1",
"name":"Screenshot.png",
"selfLink":"https://www.googleapis.com/storage/v1/b/uploaded-pictures/o/Screenshot.png",
"size":"173557",
"storageClass":"STANDARD",
"timeCreated":"2020-01-23T16:02:16.773Z",
"timeStorageClassUpdated":"2020-01-23T16:02:16.773Z",
"updated":"2020-01-23T16:02:16.773Z"
}
เราเตรียมคำขอที่จะส่งผ่านไคลเอ็นต์ Vision ดังนี้
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();
เราขอแนะนำความสามารถหลัก 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
}
ถ้าไม่มีการส่งคืนข้อผิดพลาด เราสามารถดำเนินการต่อได้ ด้วยเหตุนี้เราจึงมีข้อผิดพลาดนี้หากบล็อก:
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return;
}
เราจะทำป้ายกำกับของสิ่งต่างๆ หมวดหมู่ หรือธีมที่จำได้ในภาพ
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);
}
นอกจากนี้เรายังใช้ฟังก์ชันยูทิลิตีเพื่อแปลงค่าสีแดง / เขียว / น้ำเงินเป็นรหัสสีเลขฐาน 16 ที่เราใช้ในสไตล์ชีต CSS ได้
ลองดูว่าภาพนั้นปลอดภัยไหม:
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 ได้ ดังนี้
if (isSafe) {
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());
ApiFuture<WriteResult> writeResult = doc.set(data, SetOptions.merge());
logger.info("Picture metadata saved in Firestore at " + writeResult.get().getUpdateTime());
}
12. ทำให้ฟังก์ชันใช้งานได้
ได้เวลาทำให้ฟังก์ชันใช้งานได้แล้ว
กด DEPLOY
และเวอร์ชันใหม่จะถูกทำให้ใช้งานได้ โดยคุณจะเห็นความคืบหน้า:
13. ทดสอบฟังก์ชันอีกครั้ง
เมื่อทำให้ฟังก์ชันใช้งานได้เรียบร้อยแล้ว คุณจะโพสต์รูปภาพไปยัง Cloud Storage ดูว่าฟังก์ชันของเราเรียกใช้หรือไม่ Vision API แสดงผลหรือไม่ และข้อมูลที่เก็บข้อมูลเมตาอยู่ใน Firestore หรือไม่
กลับไปที่ Cloud Storage
แล้วคลิกที่เก็บข้อมูลที่เราสร้างไว้เมื่อเริ่มต้นห้องทดลอง
เมื่ออยู่ในหน้ารายละเอียดที่เก็บข้อมูล ให้คลิกปุ่ม Upload files
เพื่ออัปโหลดรูปภาพ
จาก "แฮมเบอร์เกอร์" (⋮) เพื่อไปที่การสำรวจ Logging > Logs
ในตัวเลือก Log Fields
ให้เลือก Cloud Function
เพื่อดูบันทึกสำหรับฟังก์ชันของคุณโดยเฉพาะ เลื่อนลงตามฟิลด์บันทึกและคุณสามารถเลือกฟังก์ชันที่ต้องการเพื่อดูบันทึกที่เกี่ยวข้องกับฟังก์ชันอย่างละเอียด เลือกฟังก์ชัน picture-uploaded
อันที่จริง ในรายการบันทึก เราจะเห็นว่ามีการเรียกใช้ฟังก์ชันของเรา ดังนี้
บันทึกจะระบุจุดเริ่มต้นและสิ้นสุดของการดำเนินการของฟังก์ชัน และระหว่างนั้น เราจะเห็นบันทึกที่เราใส่ไว้ในฟังก์ชันโดยใช้คำสั่งconsole.log() เราพบว่า
- รายละเอียดของเหตุการณ์ที่ทริกเกอร์ฟังก์ชันของเรา
- ผลลัพธ์ดิบจากการเรียก Vision API
- ป้ายกำกับที่พบในภาพที่เราอัปโหลด
- ข้อมูลสีที่โดดเด่น
- รูปภาพปลอดภัยที่จะแสดงหรือไม่
- และท้ายที่สุดแล้วระบบได้จัดเก็บข้อมูลเมตาเกี่ยวกับรูปภาพไว้ใน Firestore
อีกครั้งจาก "แฮมเบอร์เกอร์" (src) ไปที่ส่วน Firestore
ในส่วนย่อย Data
(แสดงโดยค่าเริ่มต้น) คุณจะเห็นคอลเล็กชัน pictures
ที่มีเอกสารใหม่เพิ่มเข้ามา ซึ่งจะสอดคล้องกับภาพที่คุณเพิ่งอัปโหลด
14. ล้างข้อมูล (ไม่บังคับ)
หากไม่ต้องการใช้ห้องทดลองอื่นๆ ในชุดนี้ต่อ คุณสามารถล้างทรัพยากรเพื่อประหยัดต้นทุนและเป็นพลเมืองระบบคลาวด์ที่ดีโดยรวม คุณสามารถล้างทรัพยากรแต่ละรายการได้ดังนี้
ลบที่เก็บข้อมูล
gsutil rb gs://${BUCKET_PICTURES}
ลบฟังก์ชัน
gcloud functions delete picture-uploaded --region europe-west1 -q
ลบคอลเล็กชัน Firestore โดยเลือก "ลบคอลเล็กชัน" ออกจากคอลเล็กชัน ดังนี้
หรือจะลบทั้งโปรเจ็กต์ก็ได้ โดยทำดังนี้
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
15. ยินดีด้วย
ยินดีด้วย คุณใช้งานบริการจัดการคีย์แรกของโปรเจ็กต์เรียบร้อยแล้ว
หัวข้อที่ครอบคลุม
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore