हर दिन फ़ोटो लेना: Lab 1—तस्वीरों को स्टोर करना और उनका विश्लेषण करना (Java)
इस कोडलैब (कोड बनाना सीखने के लिए ट्यूटोरियल) के बारे में जानकारी
1. खास जानकारी
पहली कोड लैब में, आपको एक बकेट में तस्वीरें अपलोड करनी होंगी. इससे फ़ाइल बनाने का इवेंट जनरेट होगा, जिसे किसी फ़ंक्शन से हैंडल किया जाएगा. यह फ़ंक्शन, इमेज का विश्लेषण करने और नतीजों को डेटास्टोर में सेव करने के लिए Vision API को कॉल करेगा.
आपको क्या सीखने को मिलेगा
- Cloud Storage
- Cloud Functions
- क्लाउड विज़न एपीआई
- Cloud Firestore
2. सेटअप और ज़रूरी शर्तें
अपने हिसाब से एनवायरमेंट सेटअप करना
- Google Cloud Console में साइन इन करें और नया प्रोजेक्ट बनाएं या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल करें. अगर आपके पास पहले से Gmail या Google Workspace खाता नहीं है, तो आपको नया खाता बनाना होगा.
- प्रोजेक्ट का नाम, इस प्रोजेक्ट में हिस्सा लेने वाले लोगों का डिसप्ले नेम होता है. यह एक वर्ण स्ट्रिंग है, जिसका इस्तेमाल Google API नहीं करता. इसे कभी भी अपडेट किया जा सकता है.
- प्रोजेक्ट आईडी, Google Cloud के सभी प्रोजेक्ट के लिए यूनीक होना चाहिए. साथ ही, आईडी को बदला नहीं जा सकता. सेट अप के बाद इसे बदला नहीं जा सकता. Cloud Console, एक यूनीक स्ट्रिंग अपने-आप जनरेट करता है; आम तौर पर, आपको उसके होने की कोई परवाह नहीं होती. ज़्यादातर कोडलैब में, आपको प्रोजेक्ट आईडी का रेफ़रंस देना होगा. आम तौर पर, इसे
PROJECT_ID
के तौर पर पहचाना जाता है. अगर आपको जनरेट किया गया आईडी पसंद नहीं है, तो आप कोई भी और रैंडम आईडी जनरेट कर सकते हैं. इसके अलावा, खुद भी आज़माया जा सकता है और देखें कि वह उपलब्ध है या नहीं. इस चरण के बाद इसे बदला नहीं जा सकता और प्रोजेक्ट के कुल समय तक बना रहेगा. - आपकी जानकारी के लिए, एक तीसरी वैल्यू यानी प्रोजेक्ट नंबर है. इसका इस्तेमाल कुछ एपीआई करते हैं. दस्तावेज़ में इन तीनों वैल्यू के बारे में ज़्यादा जानें.
- इसके बाद, आपको क्लाउड संसाधनों/एपीआई का इस्तेमाल करने के लिए, Cloud Console में बिलिंग चालू करनी होगी. इस कोडलैब का इस्तेमाल करने पर, आपको ज़्यादा पैसे नहीं चुकाने होंगे. इस ट्यूटोरियल के अलावा, संसाधनों को बंद करने के लिए कि आपको बिलिंग न करनी पड़े. इसके लिए, अपने बनाए गए संसाधनों को मिटाएं या पूरा प्रोजेक्ट मिटाएं. Google Cloud के नए उपयोगकर्ता, 300 डॉलर के मुफ़्त ट्रायल वाले प्रोग्राम में हिस्सा ले सकते हैं.
Cloud Shell शुरू करना
Google Cloud को आपके लैपटॉप से, कहीं से भी ऑपरेट किया जा सकता है. हालांकि, इस कोडलैब में Google Cloud Shell का इस्तेमाल किया जा रहा है. यह क्लाउड में चलने वाला कमांड लाइन एनवायरमेंट है.
Google Cloud Console में जाकर, सबसे ऊपर दाईं ओर मौजूद टूलबार पर क्लाउड शेल आइकॉन पर क्लिक करें:
प्रावधान करने और एनवायरमेंट से कनेक्ट होने में कुछ ही समय लगेगा. उसके पूरा हो जाने पर, आपको कुछ ऐसा दिखाई देगा:
इस वर्चुअल मशीन में ऐसे सभी डेवलपमेंट टूल मौजूद हैं जिनकी आपको ज़रूरत पड़ेगी. यह पांच जीबी की स्थायी होम डायरेक्ट्री उपलब्ध कराता है और Google Cloud पर चलता है. यह नेटवर्क की परफ़ॉर्मेंस और पुष्टि करने की प्रक्रिया को बेहतर बनाता है. इस कोडलैब (कोड बनाना सीखना) में आपका सारा काम ब्राउज़र में किया जा सकता है. आपको कुछ भी इंस्टॉल करने की ज़रूरत नहीं है.
3. एपीआई चालू करें
इस लैब के लिए, आपको Cloud Functions और Vision API का इस्तेमाल करना होगा, लेकिन पहले उन्हें Cloud Console में या gcloud
में चालू करना होगा.
Cloud Console में Vision API को चालू करने के लिए, खोज बार में 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) से या Cloud Shell से gsutil कमांड लाइन टूल या अपने लोकल डेवलपमेंट एनवायरमेंट की मदद से, ऐसा किया जा सकता है.
स्टोरेज पर जाएं
"हैमबर्गर" से (RECORD) मेन्यू, Storage
पेज पर जाएं.
अपने बकेट को नाम दें
CREATE BUCKET
बटन पर क्लिक करें.
CONTINUE
पर क्लिक करें.
जगह चुनें
अपनी पसंद के क्षेत्र में, एक से ज़्यादा इलाकों के लिए बकेट बनाएं (यहां Europe
).
CONTINUE
पर क्लिक करें.
डिफ़ॉल्ट स्टोरेज क्लास चुनना
अपने डेटा के लिए Standard
स्टोरेज क्लास चुनें.
CONTINUE
पर क्लिक करें.
ऐक्सेस कंट्रोल सेट करना
साथ ही, आपने सार्वजनिक तौर पर उपलब्ध इमेज पर काम किया है. इसलिए, इस बकेट में स्टोर की गई हमारी सभी तस्वीरों का ऐक्सेस कंट्रोल एक जैसा होना चाहिए.
Uniform
ऐक्सेस कंट्रोल का विकल्प चुनें.
CONTINUE
पर क्लिक करें.
सुरक्षा/एन्क्रिप्ट (सुरक्षित) करने का तरीका सेट करना
डिफ़ॉल्ट (Google-managed key)
, क्योंकि आप अपनी एन्क्रिप्शन कुंजियों का इस्तेमाल नहीं करेंगे.) को डिफ़ॉल्ट रखें.
हमारी बकेट बनाने की प्रक्रिया को पूरा करने के लिए, CREATE
पर क्लिक करें.
सभी उपयोगकर्ताओं को स्टोरेज व्यूअर के तौर पर जोड़ना
Permissions
टैब पर जाएं:
बकेट में allUsers
सदस्य को, Storage > Storage Object Viewer
की भूमिका के साथ जोड़ें, ऐसा करने का तरीका:
SAVE
पर क्लिक करें.
5. बकेट बनाएं (gsutil)
बकेट बनाने के लिए, Cloud Shell में gsutil
कमांड-लाइन टूल का इस्तेमाल भी किया जा सकता है.
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
ऐक्सेस लेबल के साथ, आपको लिंक का एक छोटा आइकॉन भी दिखेगा. इस पर क्लिक करने पर, आपका ब्राउज़र उस इमेज के सार्वजनिक यूआरएल पर जाएगा, जो इस तरह का होगा:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
जहां आपने अपने बकेट के लिए चुना है वह दुनिया भर में इस्तेमाल होने वाला यूनीक नाम है. इसके बाद, आपकी फ़ोटो का फ़ाइल नाम BUCKET_NAME
है.
चित्र नाम के साथ चेक बॉक्स पर क्लिक करने से, DELETE
बटन सक्षम हो जाएगा और आप इस पहली चित्र को हटा सकते हैं.
7. फ़ंक्शन बनाएं
इस चरण में, एक ऐसा फ़ंक्शन बनाया जाता है जो फ़ोटो के अपलोड किए गए इवेंट पर प्रतिक्रिया देता है.
Google Cloud Console के Cloud Functions
सेक्शन पर जाएं. इस पेज पर जाने पर, Cloud Functions सेवा अपने-आप चालू हो जाएगी.
Create function
पर क्लिक करें.
कोई नाम चुनें (जैसे, picture-uploaded
) और इलाका (बकेट के लिए क्षेत्र के विकल्प को ध्यान में रखें):
फ़ंक्शन दो तरह के होते हैं:
- ऐसे एचटीटीपी फ़ंक्शन जिन्हें यूआरएल (जैसे, वेब एपीआई) के ज़रिए शुरू किया जा सकता है.
- बैकग्राउंड फ़ंक्शन, जो किसी इवेंट से ट्रिगर हो सकते हैं.
आपको ऐसा बैकग्राउंड फ़ंक्शन बनाना है जो हमारे Cloud Storage
बकेट में किसी नई फ़ाइल के अपलोड होने पर ट्रिगर होता है:
आपको Finalize/Create
इवेंट टाइप में दिलचस्पी है. यह इवेंट तब ट्रिगर होता है, जब बकेट में कोई फ़ाइल बनाई जाती है या अपडेट की जाती है:
किसी फ़ाइल के बनाए जाने या अपडेट किए जाने पर Cloud Functions को सूचना देने के लिए, पहले बनाया गया बकेट चुनें:
पहले बनाया गया बकेट चुनने के लिए, Select
पर क्लिक करें. इसके बाद, Save
पर क्लिक करें
'आगे बढ़ें' पर क्लिक करने से पहले, रनटाइम, बिल्ड, कनेक्शन, और सुरक्षा सेटिंग में जाकर, डिफ़ॉल्ट स्टोरेज (256 एमबी) को बड़ा करके उसमें बदलाव किया जा सकता है. साथ ही, उसे 1 जीबी पर अपडेट किया जा सकता है.
Next
पर क्लिक करने के बाद, रनटाइम, सोर्स कोड, और एंट्री पॉइंट को ट्यून किया जा सकता है.
इस फ़ंक्शन के लिए Inline editor
को बनाए रखें:
कोई एक Java रनटाइम चुनें, उदाहरण के लिए, Java 11:
सोर्स कोड में एक Java
फ़ाइल और अलग-अलग तरह का मेटाडेटा और डिपेंडेंसी देने वाली pom.xml
Maven फ़ाइल शामिल होती है.
कोड का डिफ़ॉल्ट स्निपेट छोड़ें: यह अपलोड की गई फ़ोटो का फ़ाइल नाम लॉग करता है:
फ़िलहाल, टेस्टिंग के लिए, फ़ंक्शन के नाम को Example
में एक्ज़ीक्यूट करें.
फ़ंक्शन बनाने और डिप्लॉय करने के लिए, Deploy
पर क्लिक करें. डिप्लॉयमेंट पूरा हो जाने के बाद, आपको फ़ंक्शन की सूची में हरे रंग के सर्कल वाला सही का निशान दिखेगा:
8. फ़ंक्शन की जांच करें
इस चरण में, देखें कि फ़ंक्शन, स्टोरेज इवेंट का जवाब देता है या नहीं.
"हैमबर्गर" से (RECORD) मेन्यू, Storage
पेज पर वापस जाएं.
इमेज अपलोड करने के लिए, इमेज बकेट पर क्लिक करें. इसके बाद, Upload files
पर क्लिक करें.
Logging > Logs Explorer
पेज पर जाने के लिए, Cloud Console में फिर से नेविगेट करें.
Log Fields
सिलेक्टर में, अपने फ़ंक्शन के लिए खास तौर पर बनाए गए लॉग देखने के लिए Cloud Function
चुनें. लॉग फ़ील्ड में नीचे की ओर स्क्रोल करें. इसके बाद, किसी फ़ंक्शन को चुनकर, फ़ंक्शन से जुड़े लॉग को बारीकी से देखें. picture-uploaded
फ़ंक्शन चुनें.
आपको लॉग आइटम दिखेंगे. इनमें फ़ंक्शन के बनने की जानकारी, फ़ंक्शन के शुरू और खत्म होने का समय, और हमारा असल लॉग स्टेटमेंट दिखना चाहिए:
हमारे लॉग स्टेटमेंट में यह बताया गया है: Processing file: pic-a-daily-architecture-events.png
. इसका मतलब है कि इस फ़ोटो को बनाए जाने और सेव करने से जुड़ा इवेंट, उम्मीद के मुताबिक ट्रिगर हुआ है.
9. डेटाबेस तैयार करना
आपको Vision API से मिली तस्वीर की जानकारी को Cloud Firestore के डेटाबेस में सेव करना होगा. यह डेटाबेस, तेज़, पूरी तरह से मैनेज किया गया, सर्वरलेस, क्लाउड-नेटिव NoSQL दस्तावेज़ डेटाबेस है. Cloud Console के Firestore
सेक्शन में जाकर अपना डेटाबेस तैयार करें:
दो विकल्प दिए जाते हैं: Native mode
या Datastore mode
. नेटिव मोड का इस्तेमाल करें. इसमें, ऑफ़लाइन सहायता और रीयल-टाइम सिंक करने जैसी अतिरिक्त सुविधाएं मिलती हैं.
SELECT NATIVE MODE
पर क्लिक करें.
एक से ज़्यादा क्षेत्र चुनें (यहां यूरोप में, लेकिन कम से कम वह क्षेत्र चुनें जो आपका फ़ंक्शन और स्टोरेज बकेट है).
CREATE DATABASE
बटन पर क्लिक करें.
डेटाबेस बन जाने के बाद, आपको यह जानकारी दिखेगी:
+ START COLLECTION
बटन पर क्लिक करके, नया कलेक्शन बनाएं.
नाम संग्रह pictures
.
आपको कोई दस्तावेज़ बनाने की ज़रूरत नहीं है. नई तस्वीरें, Cloud Storage में सेव हो जाती हैं और Vision API इनका विश्लेषण कर सकता है. इसलिए, इन्हें प्रोग्राम के हिसाब से अपने-आप जोड़ा जा सकता है.
Save
पर क्लिक करें.
Firestore, नए कलेक्शन में पहला डिफ़ॉल्ट दस्तावेज़ बनाता है. आपके पास उस दस्तावेज़ को सुरक्षित तरीके से मिटाने का विकल्प होता है, क्योंकि इसमें कोई काम की जानकारी नहीं होती:
हमारे कलेक्शन में, प्रोग्राम के हिसाब से अपने-आप बनने वाले दस्तावेज़ों में चार फ़ील्ड होंगे:
- name (string): अपलोड की गई फ़ोटो की फ़ाइल का नाम, जो दस्तावेज़ की कुंजी भी है
- लेबल (स्ट्रिंग की कैटगरी): Vision API की मदद से पहचाने गए आइटम के लेबल
- color (string): मुख्य रंग का हेक्साडेसिमल रंग कोड (जैसे, #ab12ef)
- बनाया गया (तारीख): इस इमेज के मेटाडेटा को सेव किए जाने के समय का टाइमस्टैंप
- थंबनेल (बूलियन): एक वैकल्पिक फ़ील्ड, जो मौजूद होगी. अगर इस तस्वीर के लिए थंबनेल इमेज जनरेट की गई है, तो यह सही हो जाएगी
हम Firestore में ऐसी तस्वीरें ढूंढने के लिए 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 में मेटाडेटा स्टोर किया जा सके.
"हैमबर्गर" से (IAB) मेन्यू में जाकर, Cloud Functions
सेक्शन पर जाएं. इसके बाद, फ़ंक्शन के नाम पर क्लिक करें और Source
टैब चुनें. इसके बाद, EDIT
बटन पर क्लिक करें.
सबसे पहले, pom.xml
फ़ाइल में बदलाव करें, जो हमारे Java फ़ंक्शन की डिपेंडेंसी की सूची में शामिल है. 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>
डिपेंडेंसी अप-टू-डेट होने के बाद, अब कस्टम कोड के साथ 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. फ़ंक्शन को एक्सप्लोर करें
आइए, इन दिलचस्प हिस्सों के बारे में गहराई से जानते हैं.
सबसे पहले, हम Maven की pom.xml
फ़ाइल में, कुछ खास डिपेंडेंसी शामिल कर रहे हैं. Google Java क्लाइंट लाइब्रेरी, डिपेंडेंसी से जुड़े किसी भी विवाद को खत्म करने के लिए, Bill-of-Materials(BOM)
को पब्लिश करती है. इसका इस्तेमाल करने पर, आपको अलग-अलग 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 फ़ंक्शन को ट्रिगर करने वाली फ़ाइल और बकेट का नाम कैसे पता करते हैं.
यहां बताया गया है कि इवेंट पेलोड कैसा दिखता है:
{
"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();
हम 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);
}
हम लाल / हरे / नीले मानों को हेक्साडेसिमल कलर कोड में बदलने के लिए एक यूटिलिटी फ़ंक्शन का इस्तेमाल कर रहे हैं जिसका इस्तेमाल हम सीएसएस स्टाइलशीट में कर सकते हैं.
आइए, देखते हैं कि तस्वीर सुरक्षित है या नहीं:
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
बटन पर क्लिक करें.
"हैमबर्गर" से (RECORD) मेन्यू, Logging > Logs
एक्सप्लोरर पर जाएं.
Log Fields
सिलेक्टर में, अपने फ़ंक्शन के लिए खास तौर पर बनाए गए लॉग देखने के लिए Cloud Function
चुनें. लॉग फ़ील्ड में नीचे की ओर स्क्रोल करें. इसके बाद, किसी फ़ंक्शन को चुनकर, फ़ंक्शन से जुड़े लॉग को बारीकी से देखें. picture-uploaded
फ़ंक्शन चुनें.
लॉग की सूची में, यह देखा जा सकता है कि हमारे फ़ंक्शन को शुरू किया गया था:
लॉग, फ़ंक्शन के चलने के शुरू और खत्म होने के बारे में बताते हैं. साथ ही, इन दोनों के बीच में हम console.log() स्टेटमेंट की मदद से अपने फ़ंक्शन में डाले गए लॉग देख सकते हैं. हम देखते हैं:
- हमारे फ़ंक्शन को ट्रिगर करने वाले इवेंट का ब्यौरा,
- Vision API कॉल से मिले रॉ नतीजे,
- हमारी अपलोड की गई तस्वीर में मिले लेबल,
- रंगों की मुख्य जानकारी,
- क्या तस्वीर सुरक्षित है या नहीं,
- आखिर में, तस्वीर से जुड़ा वह मेटाडेटा Firestore में सेव कर दिया जाता है.
"हैमबर्गर" से फिर से (RECORD) मेन्यू, 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 Firestore