1. Przegląd
W pierwszym module prześlesz zdjęcia do zasobnika. Spowoduje to wygenerowanie zdarzenia utworzenia pliku, które zostanie obsłużone przez funkcję. Funkcja wywoła interfejs Vision API, aby przeprowadzić analizę obrazu i zapisać wyniki w magazynie danych.

Czego się nauczysz
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
- Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. Jeśli nie masz jeszcze konta Gmail lub Google Workspace, musisz je utworzyć.



- Nazwa projektu to wyświetlana nazwa dla uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google. Możesz ją zaktualizować w dowolnym momencie.
- Identyfikator projektu musi być unikalny we wszystkich projektach Google Cloud i jest niezmienny (nie można go zmienić po ustawieniu). Konsola Cloud automatycznie generuje unikalny ciąg znaków. Zwykle nie musisz się nim przejmować. W większości przewodników programowania musisz odwoływać się do identyfikatora projektu (zwykle jest on oznaczony jako
PROJECT_ID). Jeśli wygenerowany identyfikator Ci się nie podoba, możesz wygenerować inny losowy identyfikator. Możesz też spróbować utworzyć własny adres i sprawdzić, czy jest dostępny. Po tym kroku nie można go zmienić i będzie obowiązywać przez cały czas trwania projektu. - Warto wiedzieć, że istnieje trzecia wartość, numer projektu, której używają niektóre interfejsy API. Więcej informacji o tych 3 wartościach znajdziesz w dokumentacji.
- Następnie musisz włączyć płatności w konsoli Cloud, aby korzystać z zasobów i interfejsów API Google Cloud. Ukończenie tego laboratorium nie powinno wiązać się z dużymi kosztami, a nawet z żadnymi. Aby wyłączyć zasoby i uniknąć naliczania opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub cały projekt. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.
Uruchom Cloud Shell
Z Google Cloud można korzystać zdalnie na laptopie, ale w tym ćwiczeniu użyjesz Google Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.
W konsoli Google Cloud kliknij ikonę Cloud Shell na pasku narzędzi w prawym górnym rogu:

Uzyskanie dostępu do środowiska i połączenie się z nim powinno zająć tylko kilka chwil. Po zakończeniu powinno wyświetlić się coś takiego:

Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Wszystkie zadania w tym laboratorium możesz wykonać w przeglądarce. Nie musisz niczego instalować.
3. Włącz interfejsy API
W tym module będziesz korzystać z Cloud Functions i interfejsu Vision API, ale najpierw musisz je włączyć w konsoli Google Cloud lub za pomocą gcloud.
Aby włączyć interfejs Vision API w konsoli Cloud, na pasku wyszukiwania wpisz Cloud Vision API:

Wyświetli się strona Cloud Vision API:

Kliknij przycisk ENABLE.
Możesz też włączyć go w Cloud Shell za pomocą narzędzia wiersza poleceń gcloud.
W Cloud Shell uruchom to polecenie:
gcloud services enable vision.googleapis.com
Operacja powinna się zakończyć:
Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.
Włącz też Cloud Functions:
gcloud services enable cloudfunctions.googleapis.com
4. Tworzenie zasobnika (konsola)
Utwórz zasobnik na zdjęcia. Możesz to zrobić w konsoli Google Cloud Platform ( console.cloud.google.com) lub za pomocą narzędzia wiersza poleceń gsutil w Cloud Shell lub w lokalnym środowisku programistycznym.
Otwórz Miejsce na dane
W menu „hamburger” (☰) otwórz stronę Storage.

Nazwij zasobnik
Kliknij przycisk CREATE BUCKET.

Kliknij CONTINUE.
Wybierz lokalizację

Utwórz zasobnik z wieloma regionami w wybranym regionie (w tym przypadku Europe).
Kliknij CONTINUE.
Wybierz domyślną klasę pamięci masowej

Wybierz klasę pamięci masowej Standard dla swoich danych.
Kliknij CONTINUE.
Konfigurowanie kontroli dostępu

Będziesz pracować z publicznie dostępnymi obrazami, więc chcesz, aby wszystkie zdjęcia przechowywane w tym zasobniku miały takie same, jednolite ustawienia kontroli dostępu.
Wybierz opcję kontroli dostępu Uniform.
Kliknij CONTINUE.
Ustawianie zabezpieczenia/szyfrowania

Zachowaj ustawienie domyślne (Google-managed key)), ponieważ nie będziesz używać własnych kluczy szyfrowania.
Kliknij CREATE, aby zakończyć tworzenie zasobnika.
Dodawanie grupy allUsers jako przeglądającego miejsce na dane
Otwórz kartę Permissions:

Dodaj użytkownika allUsers do zasobnika z rolą Storage > Storage Object Viewer w ten sposób:

Kliknij SAVE.
5. Tworzenie zasobnika (gsutil)
Do tworzenia zasobników możesz też używać narzędzia wiersza poleceń gsutil w Cloud Shell.
W Cloud Shell ustaw zmienną dla unikalnej nazwy zasobnika. W Cloud Shell zmienna GOOGLE_CLOUD_PROJECT jest już ustawiona na Twój unikalny identyfikator projektu. Możesz dodać go do nazwy zasobnika.
Na przykład:
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
Utwórz standardową strefę wieloregionową w Europie:
gsutil mb -l EU gs://${BUCKET_PICTURES}
Sprawdź, czy jest włączony jednolity dostęp na poziomie zasobnika:
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
Ustaw zasobnik jako publiczny:
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
Jeśli przejdziesz do sekcji Cloud Storage w konsoli, powinien być tam publiczny uploaded-pictures:

Sprawdź, czy możesz przesyłać zdjęcia do zasobnika i czy przesłane zdjęcia są dostępne publicznie, jak opisano w poprzednim kroku.
6. Testowanie dostępu publicznego do zasobnika
Wracając do przeglądarki pamięci, zobaczysz na liście swój zasobnik z dostępem „Publiczny” (wraz z ostrzeżeniem przypominającym, że każdy ma dostęp do zawartości tego zasobnika).

Zasobnik jest gotowy do odbierania zdjęć.
Jeśli klikniesz nazwę zasobnika, zobaczysz jego szczegóły.

Możesz tam kliknąć przycisk Upload files, aby sprawdzić, czy możesz dodać obraz do zasobnika. Pojawi się okienko wyboru pliku, w którym musisz wybrać plik. Po wybraniu plik zostanie przesłany do zasobnika i ponownie zobaczysz public dostęp, który został automatycznie przypisany do tego nowego pliku.

Obok etykiety dostępu Public zobaczysz też małą ikonę linku. Po kliknięciu przeglądarka przejdzie do publicznego adresu URL tego obrazu, który będzie miał postać:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
gdzie BUCKET_NAME to niepowtarzalna globalnie nazwa wybranego zasobnika, a następnie nazwa pliku obrazu.
Kliknięcie pola wyboru obok nazwy obrazu spowoduje włączenie przycisku DELETE, dzięki czemu możesz usunąć pierwszy obraz.
7. Tworzenie funkcji
W tym kroku utworzysz funkcję, która reaguje na zdarzenia przesyłania obrazów.
Otwórz sekcję Cloud Functions w konsoli Google Cloud. Po otwarciu tej strony usługa Cloud Functions zostanie automatycznie włączona.

Kliknij Create function.
Wybierz nazwę (np. picture-uploaded) i regionu (pamiętaj, aby zachować spójność z regionem wybranym dla zasobnika):

Istnieją 2 rodzaje funkcji:
- funkcje HTTP, które można wywoływać za pomocą adresu URL (np. interfejs API sieci),
- Funkcje w tle, które mogą być wywoływane przez określone zdarzenie.
Chcesz utworzyć funkcję w tle, która będzie wywoływana, gdy do zasobnika Cloud Storage zostanie przesłany nowy plik:

Interesuje Cię typ zdarzenia Finalize/Create, czyli zdarzenie wywoływane, gdy plik jest tworzony lub aktualizowany w zasobniku:

Wybierz utworzony wcześniej zasobnik, aby poinformować Cloud Functions, że ma otrzymywać powiadomienia o utworzeniu lub zaktualizowaniu pliku w tym zasobniku:

Kliknij Select, aby wybrać utworzony wcześniej zasobnik, a następnie Save.

Zanim klikniesz Dalej, możesz rozwinąć i zmodyfikować ustawienia domyślne (256 MB pamięci) w sekcji Ustawienia środowiska wykonawczego, kompilacji, połączeń i zabezpieczeń i zmienić je na 1 GB.

Po kliknięciu Next możesz dostosować środowisko wykonawcze, kod źródłowy i punkt wejścia.
Zachowaj Inline editor dla tej funkcji:

Wybierz jedno ze środowisk wykonawczych Node.js:

Kod źródłowy składa się z index.jspliku JavaScript i package.jsonpliku, który zawiera różne metadane i zależności.
Pozostaw domyślny fragment kodu: rejestruje on nazwę pliku przesłanego obrazu:

Na potrzeby testów pozostaw nazwę funkcji do wykonania ustawioną na helloGCS.
Aby utworzyć i wdrożyć funkcję, kliknij Deploy. Po pomyślnym wdrożeniu na liście funkcji powinien pojawić się zielony znacznik wyboru:

8. Testowanie funkcji
W tym kroku sprawdzisz, czy funkcja reaguje na zdarzenia w Cloud Storage.
W menu (☰) wróć na stronę Storage.
Kliknij zasobnik obrazów, a potem Upload files, aby przesłać obraz.

W konsoli w chmurze ponownie otwórz stronę Logging > Logs Explorer.
W selektorze Log Fields wybierz Cloud Function, aby wyświetlić logi dotyczące Twoich funkcji. Przewiń w dół pola logów. Możesz nawet wybrać konkretną funkcję, aby uzyskać bardziej szczegółowy widok logów związanych z funkcjami. Wybierz funkcję picture-uploaded.
Powinny być widoczne wpisy w logu dotyczące utworzenia funkcji, czasu jej rozpoczęcia i zakończenia oraz naszej rzeczywistej instrukcji logowania:

W naszym dzienniku widnieje wpis: Processing file: pic-a-daily-architecture-events.png, co oznacza, że zdarzenie związane z utworzeniem i zapisaniem tego zdjęcia zostało wywołane zgodnie z oczekiwaniami.
9. Przygotowywanie bazy danych
Informacje o zdjęciu podane przez interfejs Vision API zapiszesz w bazie danych Cloud Firestore, czyli szybkiej, w pełni zarządzanej, bezserwerowej, chmurowej bazie danych dokumentów NoSQL. Przygotuj bazę danych, przechodząc do sekcji Firestore w konsoli Cloud:

Dostępne są 2 opcje: Native mode lub Datastore mode. Używaj trybu natywnego, który oferuje dodatkowe funkcje, takie jak obsługa offline i synchronizacja w czasie rzeczywistym.
Kliknij SELECT NATIVE MODE.

Wybierz region obejmujący wiele lokalizacji (w tym przypadku w Europie, ale najlepiej co najmniej ten sam region, w którym znajdują się funkcja i zasobnik pamięci).
Kliknij przycisk CREATE DATABASE.
Po utworzeniu bazy danych zobaczysz te informacje:

Utwórz nową kolekcję, klikając przycisk + START COLLECTION.
Nazwij kolekcję pictures.

Nie musisz tworzyć dokumentu. Dodasz je programowo, gdy nowe zdjęcia będą przechowywane w Cloud Storage i analizowane przez interfejs Vision API.
Kliknij Save.
Firestore tworzy pierwszy domyślny dokument w nowo utworzonej kolekcji. Możesz go bezpiecznie usunąć, ponieważ nie zawiera żadnych przydatnych informacji:

Dokumenty, które zostaną utworzone programowo w naszej kolekcji, będą zawierać 4 pola:
- name (string): nazwa pliku przesłanego zdjęcia, która jest też kluczem dokumentu.
- labels (tablica ciągów znaków): etykiety rozpoznanych elementów przez Vision API.
- color (string): szesnastkowy kod koloru dominującego (np. #ab12ef)
- created (data): sygnatura czasowa wskazująca, kiedy metadane tego obrazu zostały zapisane.
- thumbnail (wartość logiczna): pole opcjonalne, które będzie obecne i będzie miało wartość „true”, jeśli dla tego zdjęcia została wygenerowana miniatura.
Aby wyszukiwać w Firestore zdjęcia, dla których dostępne są miniatury, i sortować je według daty utworzenia, musimy utworzyć indeks wyszukiwania.
Możesz utworzyć indeks za pomocą tego polecenia w Cloud Shell:
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
Możesz też to zrobić w Cloud Console. W kolumnie nawigacyjnej po lewej stronie kliknij Indexes, a potem utwórz indeks złożony, jak pokazano poniżej:

Kliknij Create. Tworzenie indeksu może potrwać kilka minut.
10. Aktualizowanie funkcji
Wróć na stronę Functions, aby zaktualizować funkcję, która będzie wywoływać interfejs Vision API w celu analizowania zdjęć i przechowywania metadanych w Firestore.
W menu (☰) przejdź do sekcji Cloud Functions, kliknij nazwę funkcji, wybierz kartę Source, a następnie kliknij przycisk EDIT.
Najpierw zmień plik package.json, który zawiera listę zależności naszej funkcji Node.JS. Zaktualizuj kod, aby dodać zależność NPM interfejsu Cloud Vision API:
{
"name": "picture-analysis-function",
"version": "0.0.1",
"dependencies": {
"@google-cloud/storage": "^1.6.0",
"@google-cloud/vision": "^1.8.0",
"@google-cloud/firestore": "^3.4.1"
}
}
Po zaktualizowaniu zależności możesz pracować nad kodem funkcji, aktualizując plik index.js.
Zastąp kod w pliku index.js kodem poniżej. Wyjaśnimy to w następnym kroku.
const vision = require('@google-cloud/vision');
const Storage = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');
const client = new vision.ImageAnnotatorClient();
exports.vision_analysis = async (event, context) => {
console.log(`Event: ${JSON.stringify(event)}`);
const filename = event.name;
const filebucket = event.bucket;
console.log(`New picture uploaded ${filename} in ${filebucket}`);
const request = {
image: { source: { imageUri: `gs://${filebucket}/${filename}` } },
features: [
{ type: 'LABEL_DETECTION' },
{ type: 'IMAGE_PROPERTIES' },
{ type: 'SAFE_SEARCH_DETECTION' }
]
};
// invoking the Vision API
const [response] = await client.annotateImage(request);
console.log(`Raw vision output for: ${filename}: ${JSON.stringify(response)}`);
if (response.error === null) {
// listing the labels found in the picture
const labels = response.labelAnnotations
.sort((ann1, ann2) => ann2.score - ann1.score)
.map(ann => ann.description)
console.log(`Labels: ${labels.join(', ')}`);
// retrieving the dominant color of the picture
const color = response.imagePropertiesAnnotation.dominantColors.colors
.sort((c1, c2) => c2.score - c1.score)[0].color;
const colorHex = decColorToHex(color.red, color.green, color.blue);
console.log(`Colors: ${colorHex}`);
// determining if the picture is safe to show
const safeSearch = response.safeSearchAnnotation;
const isSafe = ["adult", "spoof", "medical", "violence", "racy"].every(k =>
!['LIKELY', 'VERY_LIKELY'].includes(safeSearch[k]));
console.log(`Safe? ${isSafe}`);
// if the picture is safe to display, store it in Firestore
if (isSafe) {
const pictureStore = new Firestore().collection('pictures');
const doc = pictureStore.doc(filename);
await doc.set({
labels: labels,
color: colorHex,
created: Firestore.Timestamp.now()
}, {merge: true});
console.log("Stored metadata in Firestore");
}
} else {
throw new Error(`Vision API error: code ${response.error.code}, message: "${response.error.message}"`);
}
};
function decColorToHex(r, g, b) {
return '#' + Number(r).toString(16).padStart(2, '0') +
Number(g).toString(16).padStart(2, '0') +
Number(b).toString(16).padStart(2, '0');
}
11. Poznaj funkcję
Przyjrzyjmy się bliżej różnym interesującym częściom.
Najpierw wymagamy potrzebnych modułów: Vision, Storage i Firestore:
const vision = require('@google-cloud/vision');
const Storage = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');
Następnie przygotowujemy klienta do interfejsu Vision API:
const client = new vision.ImageAnnotatorClient();
Teraz przejdźmy do struktury naszej funkcji. Uczynimy ją funkcją asynchroniczną, ponieważ korzystamy z możliwości async / await wprowadzonych w Node.js 8:
exports.vision_analysis = async (event, context) => {
...
const filename = event.name;
const filebucket = event.bucket;
...
}
Zwróć uwagę na sygnaturę, ale też na to, jak pobieramy nazwę pliku i zasobnika, które wywołały funkcję w Cloud Functions.
Oto przykładowy ładunek zdarzenia:
{
"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"
}
Przygotowujemy żądanie do wysłania za pomocą klienta Vision:
const request = {
image: { source: { imageUri: `gs://${filebucket}/${filename}` } },
features: [
{ type: 'LABEL_DETECTION' },
{ type: 'IMAGE_PROPERTIES' },
{ type: 'SAFE_SEARCH_DETECTION' }
]
};
Prosimy o 3 kluczowe funkcje interfejsu Vision API:
- Wykrywanie etykiet: aby zrozumieć, co znajduje się na zdjęciach.
- Właściwości obrazu: aby podać interesujące atrybuty obrazu (interesuje nas dominujący kolor obrazu).
- Bezpieczne wyszukiwanie: aby sprawdzić, czy obraz jest bezpieczny do wyświetlenia (nie powinien zawierać treści dla dorosłych, medycznych, o charakterze erotycznym ani treści przedstawiających przemoc).
W tym momencie możemy wywołać interfejs Vision API:
const [response] = await client.annotateImage(request);
Oto przykład odpowiedzi interfejsu 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
}
Jeśli nie zostanie zwrócony żaden błąd, możemy przejść dalej. Dlatego mamy ten blok if:
if (response.error === null) {
...
} else {
throw new Error(`Vision API error: code ${response.error.code},
message: "${response.error.message}"`);
}
Pobierzemy etykiety rzeczy, kategorii lub motywów rozpoznanych na zdjęciu:
const labels = response.labelAnnotations
.sort((ann1, ann2) => ann2.score - ann1.score)
.map(ann => ann.description)
Etykiety są sortowane według najwyższego wyniku.
Chcemy poznać dominujący kolor zdjęcia:
const color = response.imagePropertiesAnnotation.dominantColors.colors
.sort((c1, c2) => c2.score - c1.score)[0].color;
const colorHex = decColorToHex(color.red, color.green, color.blue);
Ponownie sortujemy kolory według wyniku i wybieramy pierwszy z nich.
Używamy też funkcji narzędziowej do przekształcania wartości czerwonej, zielonej i niebieskiej w szesnastkowy kod koloru, którego możemy używać w arkuszach stylów CSS.
Sprawdźmy, czy zdjęcie jest bezpieczne:
const safeSearch = response.safeSearchAnnotation;
const isSafe = ["adult", "spoof", "medical", "violence", "racy"]
.every(k => !['LIKELY', 'VERY_LIKELY'].includes(safeSearch[k]));
Sprawdzamy atrybuty dotyczące treści dla dorosłych, parodii, treści medycznych, przemocy i treści o charakterze erotycznym, aby określić, czy prawdopodobnie lub bardzo prawdopodobnie nie są one zgodne z zasadami.
Jeśli wynik bezpiecznego wyszukiwania jest prawidłowy, możemy zapisać metadane w Firestore:
if (isSafe) {
const pictureStore = new Firestore().collection('pictures');
const doc = pictureStore.doc(filename);
await doc.set({
labels: labels,
color: colorHex,
created: Firestore.Timestamp.now()
}, {merge: true});
}
12. Wdrażanie funkcji
Czas wdrożyć funkcję.

Kliknij przycisk DEPLOY, a nowa wersja zostanie wdrożona. Możesz śledzić postęp:

13. Ponowne testowanie funkcji
Po pomyślnym wdrożeniu funkcji opublikujesz obraz w Cloud Storage, sprawdzisz, czy funkcja została wywołana, co zwraca interfejs Vision API i czy metadane są przechowywane w Firestore.
Wróć do Cloud Storage i kliknij zasobnik utworzony na początku modułu:

Na stronie z informacjami o zasobniku kliknij przycisk Upload files, aby przesłać zdjęcie.

W menu „hamburger” (☰) otwórz Logging > Logs Eksplorator.
W selektorze Log Fields wybierz Cloud Function, aby wyświetlić logi dotyczące Twoich funkcji. Przewiń w dół pola logów. Możesz nawet wybrać konkretną funkcję, aby uzyskać bardziej szczegółowy widok logów związanych z funkcjami. Wybierz funkcję picture-uploaded.

Na liście logów widać, że nasza funkcja została wywołana:

Logi wskazują początek i koniec wykonania funkcji. Pomiędzy nimi widzimy logi, które umieściliśmy w funkcji za pomocą instrukcji console.log(). Widzimy:
- szczegóły zdarzenia, które wywołało funkcję,
- Nieprzetworzone wyniki wywołania interfejsu Vision API.
- etykiety znalezione na przesłanym przez nas zdjęciu,
- informacje o dominujących kolorach,
- czy zdjęcie jest bezpieczne do wyświetlenia,
- Metadane obrazu zostały ostatecznie zapisane w Firestore.

Ponownie w menu „hamburger” (☰) otwórz sekcję Firestore. W podsekcji Data (wyświetlanej domyślnie) powinna pojawić się kolekcja pictures z dodanym nowym dokumentem odpowiadającym właśnie przesłanemu zdjęciu:

14. Zwalnianie miejsca (opcjonalnie)
Jeśli nie zamierzasz kontynuować pracy z pozostałymi ćwiczeniami z tej serii, możesz zwalniać miejsce, aby zaoszczędzić koszty i być dobrym obywatelem chmury. Możesz zwolnić miejsce, usuwając poszczególne zasoby w ten sposób.
Usuń zasobnik:
gsutil rb gs://${BUCKET_PICTURES}
Usuń funkcję:
gcloud functions delete picture-uploaded --region europe-west1 -q
Usuń kolekcję Firestore, wybierając opcję Usuń kolekcję:

Możesz też usunąć cały projekt:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
15. Gratulacje!
Gratulacje! Udało Ci się wdrożyć pierwszą kluczową usługę projektu.
Omówione zagadnienia
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore