1. Genel Bakış
İlk kod laboratuvarında resimleri bir paket içinde yükleyeceksiniz. Bu işlemle, bir işlev tarafından işlenecek bir dosya oluşturma etkinliği oluşturulur. İşlev, görüntü analizi yapmak ve sonuçları veri deposuna kaydetmek için Vision API'ye çağrı yapar.
Neler öğreneceksiniz?
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore
2. Kurulum ve Gereksinimler
Kendi hızınızda ortam kurulumu
- Google Cloud Console'da oturum açıp yeni bir proje oluşturun veya mevcut bir projeyi yeniden kullanın. Gmail veya Google Workspace hesabınız yoksa hesap oluşturmanız gerekir.
- Proje adı, bu projenin katılımcıları için görünen addır. Google API'leri tarafından kullanılmayan bir karakter dizesidir. İstediğiniz zaman güncelleyebilirsiniz.
- Proje Kimliği, tüm Google Cloud projelerinde benzersiz olmalıdır ve değiştirilemez (belirlendikten sonra değiştirilemez). Cloud Console, otomatik olarak benzersiz bir dize oluşturur. bunun ne olduğunu umursamıyorsunuz. Çoğu codelab'de proje kimliğini (genellikle
PROJECT_ID
olarak tanımlanır) referans almanız gerekir. Oluşturulan kimliği beğenmezseniz rastgele bir kimlik daha oluşturabilirsiniz. Alternatif olarak, kendi ölçümünüzü deneyip mevcut olup olmadığına bakabilirsiniz. Bu adımdan sonra değiştirilemez ve proje süresince kalır. - Bilginiz için bazı API'lerin kullandığı üçüncü bir değer, yani Proje Numarası daha vardır. Bu değerlerin üçü hakkında daha fazla bilgiyi belgelerde bulabilirsiniz.
- Sonraki adımda, Cloud kaynaklarını/API'lerini kullanmak için Cloud Console'da faturalandırmayı etkinleştirmeniz gerekir. Bu codelab'i çalıştırmanın maliyeti, yüksek değildir. Bu eğitim dışında faturalandırma yapılmaması için kaynakları kapatmak isterseniz oluşturduğunuz kaynakları silebilir veya projenin tamamını silebilirsiniz. Yeni Google Cloud kullanıcıları, 300 ABD doları değerindeki ücretsiz denemeden yararlanabilir.
Cloud Shell'i başlatma
Google Cloud dizüstü bilgisayarınızdan uzaktan çalıştırılabilse de bu codelab'de, Cloud'da çalışan bir komut satırı ortamı olan Google Cloud Shell'i kullanacaksınız.
Google Cloud Console'da, sağ üstteki araç çubuğunda bulunan Cloud Shell simgesini tıklayın:
Ortamı sağlamak ve bağlamak yalnızca birkaç dakika sürer. Tamamlandığında şuna benzer bir sonuç görmeniz gerekir:
İhtiyacınız olan tüm geliştirme araçlarını bu sanal makinede bulabilirsiniz. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud üzerinde çalışarak ağ performansını ve kimlik doğrulamasını büyük ölçüde iyileştirir. Bu codelab'deki tüm çalışmalarınız tarayıcıda yapılabilir. Herhangi bir şey yüklemeniz gerekmez.
3. API'leri etkinleştir
Bu laboratuvarda Cloud Functions ve Vision API'yi kullanacaksınız ancak bunların önce Cloud Console'da veya gcloud
ile etkinleştirilmesi gerekir.
Cloud Console'da Vision API'yi etkinleştirmek için arama çubuğunda Cloud Vision API
ifadesini arayın:
Cloud Vision API sayfasına yönlendirilirsiniz:
ENABLE
düğmesini tıklayın.
Alternatif olarak, gcloud komut satırı aracını kullanarak Cloud Shell'i de etkinleştirebilirsiniz.
Cloud Shell'in içinde aşağıdaki komutu çalıştırın:
gcloud services enable vision.googleapis.com
İşlemin başarıyla tamamlandığını göreceksiniz:
Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.
Cloud Functions'ı da etkinleştirin:
gcloud services enable cloudfunctions.googleapis.com
4. Paketi oluşturma (konsol)
Resimler için bir depolama paketi oluşturun. Bu işlemi Google Cloud Platform konsolundan ( console.cloud.google.com) veya Cloud Shell'den ya da yerel geliştirme ortamınızdan gsutil komut satırı aracıyla yapabilirsiniz.
Depolama alanına git
"Hamburger"den (Gönder) menüsünden Storage
sayfasına gidin.
Paketinizi adlandırma
CREATE BUCKET
düğmesini tıklayın.
CONTINUE
simgesini tıklayın.
Konum Seçin
Seçtiğiniz bölgede çok bölgeli bir paket oluşturun (burada Europe
).
CONTINUE
simgesini tıklayın.
Varsayılan depolama sınıfını seçme
Verileriniz için Standard
depolama sınıfını seçin.
CONTINUE
simgesini tıklayın.
Erişim Denetimini Ayarlama
Herkesin erişebileceği görüntülerle çalışacağınız için bu pakette depolanan tüm resimlerimizin aynı tek tip erişim denetimine sahip olmasını istersiniz.
Uniform
erişim denetimi seçeneğini belirleyin.
CONTINUE
simgesini tıklayın.
Korumayı/Şifrelemeyi Ayarlama
Varsayılan değeri koruyun (Kendi şifreleme anahtarlarınızı kullanmayacağınız için Google-managed key)
.
Paket oluşturma işlemini tamamlamak için CREATE
düğmesini tıklayın.
allUsers'ı depolama alanı görüntüleyici olarak ekle
Permissions
sekmesine gidin:
Aşağıdaki gibi, Storage > Storage Object Viewer
rolüne sahip bir allUsers
üyesini pakete ekleyin:
SAVE
simgesini tıklayın.
5. Paket (gsutil) oluşturma
Paket oluşturmak için Cloud Shell'deki gsutil
komut satırı aracını da kullanabilirsiniz.
Cloud Shell'de benzersiz paket adı için bir değişken ayarlayın. Cloud Shell'de GOOGLE_CLOUD_PROJECT
, benzersiz proje kimliğinize zaten ayarlanmış. Bunu paket adına ekleyebilirsiniz.
Örneğin:
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
Avrupa'da standart bir çok bölgeli alt bölge oluşturun:
gsutil mb -l EU gs://${BUCKET_PICTURES}
Tek tip paket düzeyinde erişim sağlayın:
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
Paketi herkese açık hale getirin:
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
Konsolun Cloud Storage
bölümüne giderseniz herkese açık bir uploaded-pictures
paketiniz olur:
Pakete resim yükleyemediğinizi ve yüklenen resimlerin önceki adımda açıklandığı gibi herkese açık olup olmadığını test edin.
6. Pakete herkese açık erişimi test etme
Depolama tarayıcısına geri döndüğünüzde, paketinizi listede "Herkese açık" olarak görürsünüz. (ilgili grubun içeriğine herhangi birinin erişimi olduğunu hatırlatan bir uyarı işareti dahil).
Paketiniz artık resim almaya hazır.
Paket adını tıklarsanız paket ayrıntılarını görürsünüz.
Buradan, pakete resim ekleyip ekleyebileceğinizi test etmek için Upload files
düğmesini deneyebilirsiniz. Dosya seçici pop-up'ında bir dosya seçmeniz istenir. Seçildikten sonra paketinize yüklenir ve bu yeni dosyayla otomatik olarak ilişkilendirilen public
erişimini tekrar görürsünüz.
Public
erişim etiketinin yanında küçük bir bağlantı simgesi de göreceksiniz. Resmi tıkladığınızda, tarayıcınız ilgili resmin herkese açık URL'sine gider. Bu URL şu biçimde olur:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
BUCKET_NAME
, paketiniz için seçtiğiniz genel olarak benzersiz addır ve ardından resminizin dosya adıdır.
Resim adının yanındaki onay kutusunu işaretleyerek DELETE
düğmesi etkinleşir ve bu ilk resmi silebilirsiniz.
7. İşlevi oluşturma
Bu adımda, resim yükleme etkinliklerine tepki veren bir işlev oluşturacaksınız.
Google Cloud Console'un Cloud Functions
bölümünü ziyaret edin. Bu sayfayı ziyaret ettiğinizde Cloud Functions hizmeti otomatik olarak etkinleştirilecektir.
Create function
seçeneğini tıklayın.
Bir ad seçin (ör. picture-uploaded
) ve Bölge (paket için bölge seçimiyle tutarlı olmayı unutmayın):
İki tür işlev vardır:
- Bir URL (ör. web API'si) aracılığıyla çağrılabilen HTTP işlevleri
- Bazı etkinlikler tarafından tetiklenebilecek arka plan işlevleri.
Cloud Storage
paketimize yeni bir dosya yüklendiğinde tetiklenen bir arka plan işlevi oluşturmak istiyorsunuz:
Pakette bir dosya oluşturulduğunda veya güncellendiğinde tetiklenen etkinlik olan Finalize/Create
etkinlik türüyle ilgileniyorsunuz:
Bu pakette bir dosya oluşturulduğunda / güncellendiğinde Cloud Functions'a bildirim gönderilmesi için daha önce oluşturulan paketi seçin:
Daha önce oluşturduğunuz paketi seçmek için Select
simgesini, ardından Save
düğmesini tıklayın.
İleri'yi tıklamadan önce Çalışma zamanı, derleme, bağlantılar ve güvenlik ayarları bölümündeki varsayılan değerleri (256 MB bellek) genişletip değiştirebilir ve boyutu 1 GB olarak güncelleyebilirsiniz.
Next
düğmesini tıkladıktan sonra Çalışma zamanı, Kaynak kodu ve giriş noktası ayarlarını yapabilirsiniz.
Bu işlev için Inline editor
değerini koruyun:
Java çalışma zamanlarından birini (ör. Java 11) seçin:
Kaynak kodu, bir Java
dosyası ile çeşitli meta veriler ve bağımlılıklar sağlayan bir pom.xml
Maven dosyasından oluşur.
Varsayılan kod snippet'ini değiştirmeyin. Yüklenen resmin dosya adı kaydedilir:
Şimdilik, test amacıyla Example
işlevine yürütülecek fonksiyonun adını kullanın.
İşlevi oluşturmak ve dağıtmak için Deploy
simgesini tıklayın. Dağıtım başarılı olduktan sonra, işlev listesinde yeşil daire içine alınmış bir onay işareti göreceksiniz:
8. İşlevi test etme
Bu adımda, işlevin depolama etkinliklerine yanıt verip vermediğini test edin.
"Hamburger"den (Gönder) menüsünü açın, Storage
sayfasına geri dönün.
Resim yüklemek için resim paketini, ardından Upload files
öğesini tıklayın.
Cloud Console'da tekrar gezinerek Logging > Logs Explorer
sayfasına gidin.
İşlevlerinize özel günlükleri görmek için Log Fields
seçicide Cloud Function
öğesini seçin. Günlük Alanları'nı kaydırarak ilgili günlüklerin daha ayrıntılı bir görünümünü elde etmek için belirli bir işlev bile seçebilirsiniz. picture-uploaded
işlevini seçin.
İşlevin oluşturulmasından bahseden günlük öğeleri, işlevin başlangıç ve bitiş zamanları ile gerçek günlük ifademizi göreceksiniz:
Günlük bildirimimizde şu ifade bulunmaktadır: Processing file: pic-a-daily-architecture-events.png
, bu resmin oluşturulması ve depolanmasıyla ilgili etkinliğin gerçekten de beklendiği gibi tetiklendiği anlamına gelir.
9. Veritabanını hazırlama
Vision API'nin sağladığı resimle ilgili bilgileri hızlı, tümüyle yönetilen, sunucusuz ve bulutta yerel bir NoSQL belge veritabanı olan Cloud Firestore veritabanında saklarsınız. Cloud Console'un Firestore
bölümüne giderek veritabanınızı hazırlayın:
İki seçenek sunulmaktadır: Native mode
veya Datastore mode
. Çevrimdışı destek ve gerçek zamanlı senkronizasyon gibi ek özellikler sunan yerel modu kullanın.
SELECT NATIVE MODE
seçeneğini tıklayın.
Çoklu bölge seçin (Avrupa'da ancak tercihen en azından işlevinizin ve depolama paketinizin bulunduğu bölgeyle aynı bölge).
CREATE DATABASE
düğmesini tıklayın.
Veritabanı oluşturulduktan sonra şunu göreceksiniz:
+ START COLLECTION
düğmesini tıklayarak yeni bir koleksiyon oluşturun.
pictures
ad koleksiyonu.
Doküman oluşturmanız gerekmez. Yeni resimler Cloud Storage'da depolanıp Vision API tarafından analiz edilirken bu resimleri programatik olarak eklersiniz.
Save
simgesini tıklayın.
Firestore, yeni oluşturulan koleksiyonda ilk varsayılan dokümanı oluşturur. Faydalı herhangi bir bilgi içermediğinden bu dokümanı güvenle silebilirsiniz:
Koleksiyonumuzda programlı olarak oluşturulacak dokümanlar 4 alan içerecektir:
- name (dize): yüklenen resmin dosya adı ve aynı zamanda dokümanın anahtarıdır
- labels (dize dizisi): Vision API tarafından tanınan öğelerin etiketleri
- color (dize): baskın rengin onaltılık renk kodu (ör. #ab12ef)
- create (tarih): Bu görsele ait meta verilerin saklandığı zaman damgası
- thumbnail (boole): Bu resim için bir küçük resim oluşturulmuşsa mevcut ve doğru olacak isteğe bağlı bir alan
Küçük resimleri olan resimleri bulmak için Firestore'da arama yapacağımızdan ve oluşturma tarihine göre sıralayacağımız için bir arama dizini oluşturmamız gerekecek.
Dizini Cloud Shell'de aşağıdaki komutla oluşturabilirsiniz:
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
Bu işlemi Cloud Console'dan da yapabilirsiniz. Bunun için soldaki gezinme sütununda Indexes
simgesini tıklayın ve ardından aşağıda gösterildiği gibi bir birleşik dizin oluşturun:
Create
simgesini tıklayın. Dizin oluşturma birkaç dakika sürebilir.
10. İşlevi güncelleme
Functions
sayfasına geri dönün.
"Hamburger"den (Gönder) menüsünü açın, Cloud Functions
bölümüne gidin, işlev adını tıklayın, Source
sekmesini seçin, ardından EDIT
düğmesini tıklayın.
Öncelikle, Java işlevimizin bağımlılıklarını listeleyen pom.xml
dosyasını düzenleyin. Cloud Vision API Maven bağımlılığını eklemek için kodu güncelleyin:
<?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>
Bağımlılıklar güncel olduğuna göre, Example.java
dosyasını özel kodumuzla güncelleyerek işlevimizin kodu üzerinde çalışacaksınız.
Fareyi Example.java
dosyasının üzerine getirip kalem simgesini tıklayın. Paket adını ve dosya adını src/main/java/fn/ImageAnalysis.java
olarak değiştirin.
ImageAnalysis.java
içindeki kodu aşağıdaki kodla değiştirin. Bir sonraki adımda açıklanacaktır.
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. İşlevi keşfedin
Çeşitli ilginç bölümlere daha yakından bakalım.
Öncelikle belirli bağımlılıkları Maven pom.xml
dosyasına ekledik. Google Java İstemci Kitaplıkları bağımlılık çakışmalarını ortadan kaldırmak için bir Bill-of-Materials(BOM)
yayınlar. Bu özelliği kullandığınızda, her bir Google istemci kitaplığı için herhangi bir sürüm belirtmeniz gerekmez.
<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>
Ardından bir müşteriyi Vision API için hazırlıyoruz:
...
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
...
Şimdi işlevimizin yapısı geliyor. Gelen etkinlikten ilgilendiğimiz alanları yakalayıp tanımladığımız GCSEvent yapısıyla eşleriz:
...
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;
}
İmzanın yanı sıra Cloud Functions işlevini tetikleyen dosyanın ve paketin adını nasıl aldığımıza da dikkat edin.
Referans olarak etkinlik yükünün nasıl göründüğüne bakalım:
{
"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 istemcisi üzerinden göndermek üzere bir istek hazırlarız:
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'nin 3 temel özelliğini istiyoruz:
- Etiket algılama: Resimlerdekileri anlamak için
- Resim özellikleri: Resmin ilgi çekici özelliklerini vermek için (resmin baskın rengiyle ilgileniyoruz)
- Güvenli arama: resmin gösterilmesinin güvenli olup olmadığını öğrenmek (yetişkinlere uygun / tıbbi / müstehcen / şiddet barındıran içerik barındırmamalıdır)
Bu noktada Vision API'ye çağrı yapabiliriz:
...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result =
vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...
Referans olarak, Vision API'nin yanıtı şu şekilde görünür:
{
"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
}
Herhangi bir hata döndürülmezse işleme devam edebiliriz. Dolayısıyla, engelleme durumunda neden şu hatayı gördüğümüz:
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return;
}
Resimde algılanan öğelerin, kategorilerin veya temaların etiketlerini alacağız:
List<String> labels = response.getLabelAnnotationsList().stream()
.map(annotation -> annotation.getDescription())
.collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
logger.info("- " + label);
}
Resmin baskın rengini öğrenmek istiyoruz:
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);
}
Ayrıca, kırmızı / yeşil / mavi değerlerini CSS stil sayfalarında kullanabileceğimiz bir onaltılık renk koduna dönüştürmek için bir yardımcı program işlevinden yararlanıyoruz.
Resmin gösterilmesinin güvenli olup olmadığını kontrol edelim:
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);
}
Olası veya çok olası olup olmadıklarını görmek için yetişkinlere uygun / adres sahteciliği / tıbbi / şiddet / müstehcenlik özelliklerini kontrol ediyoruz.
Güvenli aramanın sonucu uygunsa meta verileri Firestore'da depolayabiliriz:
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. İşlevi dağıtma
İşlevi dağıtma zamanı.
DEPLOY
düğmesine bastığınızda yeni sürüm dağıtılır. İlerleme durumunu görebilirsiniz:
13. İşlevi tekrar test etme
İşlev başarıyla dağıtıldıktan sonra Cloud Storage'da bir resim yayınlayarak işlevimizin çağrılıp çağrılmadığını, Vision API'nin ne döndürdüğünü ve meta verilerin Firestore'da depolanıp depolanmadığını görürsünüz.
Cloud Storage
hizmetine geri dönün ve laboratuvarın başında oluşturduğumuz paketi tıklayın:
Paket ayrıntıları sayfasına geldiğinizde, resim yüklemek için Upload files
düğmesini tıklayın.
"Hamburger"den (Gönder) menüsünden Logging > Logs
Gezgini'ne gidin.
İşlevlerinize özel günlükleri görmek için Log Fields
seçicide Cloud Function
öğesini seçin. Günlük Alanları'nı kaydırarak ilgili günlüklerin daha ayrıntılı bir görünümünü elde etmek için belirli bir işlev bile seçebilirsiniz. picture-uploaded
işlevini seçin.
Hatta günlük listesinde işlevimizin çağrıldığını görebiliyorum:
Günlükler, işlevin yürütülmesinin başlangıcını ve bitişini gösterir. Bunların arasında, console.log() ifadeleriyle işlevimize yerleştirdiğimiz günlükleri görebiliriz. Şunları görürüz:
- İşlevimizi tetikleyen olayın ayrıntıları,
- Vision API çağrısından gelen ham sonuçlar,
- Yüklediğimiz resimde bulunan etiketler,
- Baskın renklerle ilgili bilgiler,
- Resmin gösterilmesinin güvenli olup olmadığı,
- Sonunda resimle ilgili bu meta veriler Firestore'da saklanır.
Yine "hamburger"den (Gönder) menüsünden Firestore
bölümüne gidin. Data
alt bölümünde (varsayılan olarak gösterilmektedir), az önce yüklediğiniz resimle ilgili yeni bir doküman eklenmiş pictures
koleksiyonunu görürsünüz:
14. Temizleme (İsteğe bağlı)
Serideki diğer laboratuvarlarla devam etmeyi düşünmüyorsanız maliyet tasarrufu yapmak ve genel olarak iyi bir bulut vatandaşı olmak için kaynakları temizleyebilirsiniz. Kaynakları aşağıda açıklandığı şekilde tek tek temizleyebilirsiniz.
Paketi silin:
gsutil rb gs://${BUCKET_PICTURES}
İşlevi silin:
gcloud functions delete picture-uploaded --region europe-west1 -q
Koleksiyondan koleksiyonu sil'i seçerek Firestore koleksiyonunu silin:
Alternatif olarak projenin tamamını silebilirsiniz:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
15. Tebrikler!
Tebrikler! Projenin ilk anahtar hizmetini başarıyla uyguladınız.
İşlediklerimiz
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore