1. Обзор
В первой лабораторной работе с кодом вы будете загружать изображения в корзину. Это создаст событие создания файла, которое будет обработано функцией. Функция выполнит вызов Vision API для анализа изображения и сохранения результатов в хранилище данных.
Что вы узнаете
- Облачное хранилище
- Облачные функции
- API облачного видения
- Облачный пожарный магазин
2. Настройка и требования
Самостоятельная настройка среды
- Войдите в Google Cloud Console и создайте новый проект или повторно используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .
- Имя проекта — это отображаемое имя для участников этого проекта. Это строка символов, не используемая API Google. Вы можете обновить его в любое время.
- Идентификатор проекта должен быть уникальным для всех проектов Google Cloud и неизменяемым (нельзя изменить после его установки). Cloud Console автоматически генерирует уникальную строку; обычно тебя не волнует, что это такое. В большинстве лабораторий кода вам потребуется указать идентификатор проекта (обычно он обозначается как
PROJECT_ID
). Если вам не нравится сгенерированный идентификатор, вы можете создать другой случайный идентификатор. Кроме того, вы можете попробовать свой собственный и посмотреть, доступен ли он. Его нельзя изменить после этого шага, и он останется в силе на протяжении всего проекта. - К вашему сведению, есть третье значение — номер проекта , который используют некоторые API. Подробнее обо всех трех этих значениях читайте в документации .
- Затем вам необходимо включить выставление счетов в Cloud Console, чтобы использовать облачные ресурсы/API. Прохождение этой лаборатории кода не должно стоить много, если вообще стоит. Чтобы отключить ресурсы и не взимать плату за пределами этого руководства, вы можете удалить созданные вами ресурсы или удалить весь проект. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .
Запустить Cloud Shell
Хотя Google Cloud можно управлять удаленно с вашего ноутбука, в этой лаборатории вы будете использовать Google Cloud Shell , среду командной строки, работающую в облаке.
В Google Cloud Console щелкните значок Cloud Shell на верхней правой панели инструментов:
Подготовка и подключение к среде займет всего несколько минут. Когда все будет готово, вы должны увидеть что-то вроде этого:
Эта виртуальная машина оснащена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Всю работу в этой лаборатории кода можно выполнять в браузере. Вам не нужно ничего устанавливать.
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.
Также включите облачные функции:
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
) и регион (не забывайте, что оно должно соответствовать выбранному региону для корзины):
Существует два вида функций:
- Функции HTTP, которые можно вызывать через URL-адрес (т. е. веб-API),
- Фоновые функции, которые могут быть вызваны каким-либо событием.
Вы хотите создать фоновую функцию, которая срабатывает при загрузке нового файла в нашу корзину Cloud Storage
:
Вас интересует тип события Finalize/Create
, который запускается при создании или обновлении файла в корзине:
Выберите сегмент, созданный ранее, чтобы сообщить Cloud Functions о необходимости получать уведомления о создании/обновлении файла в этом конкретном сегменте:
Нажмите Select
, чтобы выбрать корзину, которую вы создали ранее, а затем Save
Прежде чем нажать «Далее», вы можете расширить и изменить значения по умолчанию (256 МБ памяти) в разделе «Время выполнения, сборка, подключения и параметры безопасности» и обновить их до 1 ГБ.
После нажатия кнопки Next
вы можете настроить среду выполнения , исходный код и точку входа .
Сохраните Inline editor
для этой функции:
Выберите одну из сред выполнения Java, например Java 11:
Исходный код состоит из файла Java
и файла pom.xml
Maven, который предоставляет различные метаданные и зависимости.
Оставьте фрагмент кода по умолчанию: он записывает имя файла загруженного изображения:
На данный момент оставьте имя функции для выполнения в Example
, в целях тестирования.
Нажмите Deploy
, чтобы создать и развернуть функцию. После успешного развертывания в списке функций вы должны увидеть галочку в зеленом кружке:
8. Проверьте функцию
На этом этапе проверьте, реагирует ли функция на события хранилища.
В меню «гамбургер» (☰) вернитесь на страницу « Storage
».
Нажмите на корзину изображений, а затем на Upload files
чтобы загрузить изображение.
Снова перейдите в облачную консоль, чтобы перейти на страницу Logging > Logs Explorer
.
В селекторе Log Fields
выберите Cloud Function
, чтобы просмотреть журналы, посвященные вашим функциям. Прокрутите вниз поля журнала, и вы даже можете выбрать конкретную функцию, чтобы получить более детальное представление журналов, связанных с функциями. Выберите функцию picture-uploaded
.
Вы должны увидеть элементы журнала, в которых упоминается создание функции, время начала и окончания функции, а также наш фактический оператор журнала:
Наш оператор журнала гласит: Processing file: pic-a-daily-architecture-events.png
, что означает, что событие, связанное с созданием и сохранением этого изображения, действительно было инициировано, как и ожидалось.
9. Подготовьте базу данных
Вы сохраните информацию об изображении, предоставленном Vision API, в базе данных Cloud Firestore , быстрой, полностью управляемой, бессерверной, облачной базе данных документов NoSQL. Подготовьте свою базу данных, перейдя в раздел Firestore
облачной консоли:
Предлагаются два варианта: Native mode
или Datastore mode
. Используйте собственный режим, который предлагает дополнительные функции, такие как автономная поддержка и синхронизация в реальном времени.
Нажмите SELECT NATIVE MODE
.
Выберите мультирегион (здесь, в Европе, но в идеале по крайней мере тот же регион, в котором находятся ваша функция и сегмент хранилища).
Нажмите кнопку CREATE DATABASE
.
После создания базы данных вы должны увидеть следующее:
Создайте новую коллекцию , нажав кнопку + START COLLECTION
.
Назовите pictures
из коллекции.
Вам не нужно создавать документ. Вы будете добавлять их программно по мере того, как новые изображения сохраняются в облачном хранилище и анализируются Vision API.
Нажмите Save
.
Firestore создает первый документ по умолчанию во вновь созданной коллекции. Вы можете безопасно удалить этот документ, поскольку он не содержит никакой полезной информации:
Документы, которые будут созданы программно в нашей коллекции, будут содержать 4 поля:
- имя (строка): имя файла загруженного изображения, которое также является ключом документа.
- labels (массив строк): метки элементов, распознанных Vision API.
- цвет (строка): шестнадцатеричный код доминирующего цвета (например, #ab12ef).
- создано (дата): временная отметка, когда метаданные этого изображения были сохранены.
- миниатюра (логическое значение): необязательное поле, которое будет присутствовать и иметь значение true, если для этого изображения было создано миниатюрное изображение.
Поскольку мы будем искать в 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
Или вы также можете сделать это из облачной консоли, нажав Indexes
в столбце навигации слева, а затем создав составной индекс, как показано ниже:
Нажмите Create
. Создание индекса может занять несколько минут.
10. Обновите функцию
Вернитесь на страницу Functions
, чтобы обновить функцию для вызова Vision API для анализа наших изображений и сохранения метаданных в Firestore.
В меню «гамбургер» (☰) перейдите в раздел 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. Изучите функцию
Давайте подробнее рассмотрим различные интересные части.
Сначала мы включаем конкретные зависимости в файл pom.xml
Maven. Клиентские библиотеки 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;
}
Обратите внимание на подпись, а также на то, как мы получаем имя файла и сегмента, которые активировали функцию облака.
Для справки, вот как выглядит полезная нагрузка события:
{
"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
}
Если ошибка не возвращается, мы можем двигаться дальше, поэтому у нас есть этот блок if:
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);
}
Мы также используем служебную функцию для преобразования значений красного/зеленого/синего в шестнадцатеричный код цвета, который мы можем использовать в таблицах стилей 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.
Снова из меню «гамбургер» (☰) перейдите в раздел 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. Поздравляем!
Поздравляем! Вы успешно реализовали первый ключевой сервис проекта!
Что мы рассмотрели
- Облачное хранилище
- Облачные функции
- API облачного видения
- Облачный пожарный магазин