เกี่ยวกับ Codelab นี้
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