1. Tổng quan
Trong lớp học lập trình đầu tiên, bạn sẽ tải ảnh lên trong một bộ chứa. Một sự kiện tạo tệp sẽ do một hàm xử lý. Hàm này sẽ thực hiện lệnh gọi đến Vision API để phân tích hình ảnh và lưu kết quả trong kho dữ liệu.
Kiến thức bạn sẽ học được
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore
2. Thiết lập và yêu cầu
Thiết lập môi trường theo tiến độ riêng
- Đăng nhập vào Google Cloud Console rồi tạo dự án mới hoặc sử dụng lại dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.
- Tên dự án là tên hiển thị của những người tham gia dự án này. Đây là một chuỗi ký tự không được API của Google sử dụng. Bạn có thể cập nhật thông tin này bất cứ lúc nào.
- Mã dự án phải là duy nhất trong tất cả các dự án Google Cloud và không thể thay đổi (không thể thay đổi sau khi đã đặt). Cloud Console sẽ tự động tạo một chuỗi duy nhất; thường bạn không quan tâm đến sản phẩm đó là gì. Trong hầu hết các lớp học lập trình, bạn sẽ cần tham chiếu đến Mã dự án (mã này thường được xác định là
PROJECT_ID
). Nếu không thích mã đã tạo, bạn có thể tạo một mã nhận dạng ngẫu nhiên khác. Ngoài ra, bạn có thể thử phương pháp của riêng mình và xem có được cung cấp hay không. Bạn không thể thay đổi thông tin này sau bước này và thông báo đó sẽ vẫn tồn tại trong thời gian của dự án. - Đối với thông tin của bạn, có giá trị thứ ba, Project Number (Số dự án) mà một số API sử dụng. Tìm hiểu thêm về cả ba giá trị này trong tài liệu này.
- Tiếp theo, bạn sẽ phải bật tính năng thanh toán trong Cloud Console để sử dụng API/tài nguyên trên đám mây. Việc chạy qua lớp học lập trình này sẽ không tốn nhiều chi phí. Để tắt các tài nguyên nhằm tránh bị tính phí ngoài hướng dẫn này, bạn có thể xoá các tài nguyên bạn đã tạo hoặc xoá toàn bộ dự án. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí 300 USD.
Khởi động Cloud Shell
Mặc dù bạn có thể vận hành Google Cloud từ xa trên máy tính xách tay, nhưng trong lớp học lập trình này, bạn sẽ sử dụng Google Cloud Shell, một môi trường dòng lệnh chạy trong Đám mây.
Trong Google Cloud Console, hãy nhấp vào biểu tượng Cloud Shell ở thanh công cụ trên cùng bên phải:
Sẽ chỉ mất một chút thời gian để cấp phép và kết nối với môi trường. Sau khi hoàn tất, bạn sẽ thấy như sau:
Máy ảo này chứa tất cả các công cụ phát triển mà bạn cần. Phiên bản này cung cấp thư mục gốc có dung lượng ổn định 5 GB và chạy trên Google Cloud, giúp nâng cao đáng kể hiệu suất và khả năng xác thực của mạng. Bạn có thể thực hiện tất cả các công việc trong lớp học lập trình này trong trình duyệt. Bạn không cần cài đặt gì cả.
3. Bật API
Đối với phòng thí nghiệm này, bạn sẽ sử dụng Cloud Functions and Vision API, nhưng trước tiên, bạn cần bật các API này trong Cloud Console hoặc bằng gcloud
.
Để bật Vision API trong Cloud Console, hãy tìm Cloud Vision API
trong thanh tìm kiếm:
Bạn sẽ được chuyển đến trang Cloud Vision API:
Nhấp vào nút ENABLE
.
Ngoài ra, bạn cũng có thể bật dịch vụ Cloud Shell bằng công cụ dòng lệnh gcloud.
Bên trong Cloud Shell, hãy chạy lệnh sau:
gcloud services enable vision.googleapis.com
Bạn sẽ thấy thao tác hoàn tất thành công:
Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.
Đồng thời bật Cloud Functions:
gcloud services enable cloudfunctions.googleapis.com
4. Tạo bộ chứa (bảng điều khiển)
Tạo bộ chứa lưu trữ cho ảnh. Bạn có thể thực hiện việc này từ bảng điều khiển Google Cloud Platform ( console.cloud.google.com) hoặc bằng công cụ dòng lệnh gsutil của Cloud Shell hoặc môi trường phát triển cục bộ của bạn.
Chuyển đến phần Bộ nhớ
Từ "hamburger" (Podcast) hãy chuyển đến trang Storage
.
Đặt tên cho bộ chứa
Nhấp vào nút CREATE BUCKET
.
Nhấp vào CONTINUE
.
Chọn vị trí
Tạo một bộ chứa nhiều khu vực trong khu vực mà bạn chọn (tại đây Europe
).
Nhấp vào CONTINUE
.
Chọn lớp bộ nhớ mặc định
Chọn lớp bộ nhớ Standard
cho dữ liệu của bạn.
Nhấp vào CONTINUE
.
Đặt chế độ kiểm soát quyền truy cập
Khi làm việc với các hình ảnh có thể truy cập công khai, bạn muốn tất cả ảnh của chúng tôi được lưu trữ trong bộ chứa này có cùng quyền kiểm soát truy cập đồng nhất.
Chọn tuỳ chọn kiểm soát quyền truy cập của Uniform
.
Nhấp vào CONTINUE
.
Thiết lập tính năng bảo vệ/mã hoá
Giữ giá trị mặc định (Google-managed key)
, vì bạn sẽ không sử dụng khoá mã hoá của riêng mình.
Nhấp vào CREATE
để hoàn tất việc tạo bộ chứa.
Thêm tất cả người dùng làm người xem bộ nhớ
Chuyển đến thẻ Permissions
:
Thêm một thành phần allUsers
vào bộ chứa với vai trò là Storage > Storage Object Viewer
như sau:
Nhấp vào SAVE
.
5. Tạo bộ chứa (myactivity)
Bạn cũng có thể dùng công cụ dòng lệnh gsutil
trong Cloud Shell để tạo nhóm.
Trong Cloud Shell, hãy đặt một biến cho tên bộ chứa duy nhất. Cloud Shell đã đặt GOOGLE_CLOUD_PROJECT
thành mã dự án duy nhất của bạn. Bạn có thể thêm tên đó vào tên bộ chứa.
Ví dụ:
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
Tạo một vùng đa vùng tiêu chuẩn ở Châu Âu:
gsutil mb -l EU gs://${BUCKET_PICTURES}
Đảm bảo quyền truy cập đồng nhất ở cấp bộ chứa:
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
Chuyển bộ chứa sang chế độ công khai:
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
Nếu chuyển đến phần Cloud Storage
của bảng điều khiển, bạn sẽ có một bộ chứa uploaded-pictures
công khai:
Kiểm tra để đảm bảo rằng bạn có thể tải ảnh lên bộ chứa và ảnh đã tải lên sẽ xuất hiện công khai, như được giải thích trong bước trước.
6. Kiểm thử quyền truy cập công khai vào bộ chứa
Quay lại trình duyệt lưu trữ, bạn sẽ thấy bộ chứa của mình trong danh sách với "Công khai" quyền truy cập (bao gồm cả biển cảnh báo để nhắc bạn rằng bất kỳ ai cũng có quyền truy cập vào nội dung trong bộ chứa đó).
Bộ chứa của bạn hiện đã sẵn sàng để nhận ảnh.
Nếu nhấp vào tên bộ chứa, bạn sẽ thấy thông tin chi tiết về bộ chứa đó.
Ở đó, bạn có thể thử nút Upload files
để kiểm tra xem bạn có thể thêm ảnh vào bộ chứa hay không. Cửa sổ bật lên của trình chọn tệp sẽ yêu cầu bạn chọn một tệp. Sau khi chọn tệp này, tệp sẽ được tải lên bộ chứa của bạn và bạn sẽ thấy lại quyền truy cập public
đã được tự động gán cho tệp mới này.
Dọc theo nhãn truy cập Public
, bạn cũng sẽ thấy một biểu tượng nhỏ về đường liên kết. Khi nhấp vào hình ảnh đó, trình duyệt của bạn sẽ chuyển đến URL công khai của hình ảnh đó, có dạng như sau:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
Với BUCKET_NAME
là tên duy nhất trên toàn cầu mà bạn đã chọn cho bộ chứa của mình, sau đó là tên tệp cho ảnh của bạn.
Khi nhấp vào hộp kiểm dọc theo tên ảnh, nút DELETE
sẽ được bật và bạn có thể xoá hình ảnh đầu tiên này.
7. Tạo hàm
Ở bước này, bạn tạo một hàm phản ứng với các sự kiện tải ảnh lên.
Truy cập vào phần Cloud Functions
trên bảng điều khiển Google Cloud. Bằng cách truy cập vào dịch vụ này, dịch vụ Cloud Functions sẽ được tự động bật.
Nhấp vào Create function
.
Chọn một tên (ví dụ: picture-uploaded
) và Khu vực (nhớ nhất quán với lựa chọn khu vực cho nhóm):
Có hai loại hàm:
- Các hàm HTTP có thể được gọi qua URL (ví dụ: API web),
- Các hàm nền có thể được kích hoạt bởi một số sự kiện.
Bạn muốn tạo một hàm nền được kích hoạt khi tệp mới được tải lên bộ chứa Cloud Storage
:
Bạn quan tâm đến loại sự kiện Finalize/Create
. Đây là sự kiện được kích hoạt khi một tệp được tạo hoặc cập nhật trong bộ chứa:
Chọn nhóm được tạo trước đó để yêu cầu Cloud Functions nhận thông báo khi một tệp được tạo / cập nhật trong bộ chứa cụ thể này:
Nhấp vào Select
để chọn bộ chứa bạn đã tạo trước đó, sau đó nhấp vào Save
Trước khi nhấp vào Tiếp theo, bạn có thể mở rộng và sửa đổi giá trị mặc định (bộ nhớ 256 MB) trong phần Thời gian chạy, bản dựng, kết nối và chế độ cài đặt bảo mật rồi cập nhật thành 1GB.
Sau khi nhấp vào Next
, bạn có thể điều chỉnh Thời gian chạy, Mã nguồn và điểm truy cập.
Giữ nguyên Inline editor
cho hàm này:
Chọn một trong các môi trường thời gian chạy Java, ví dụ: Java 11:
Mã nguồn bao gồm một tệp Java
và một tệp Maven pom.xml
cung cấp nhiều siêu dữ liệu và phần phụ thuộc.
Để nguyên đoạn mã mặc định: đoạn mã này ghi lại tên tệp của bức ảnh đã tải lên:
Bây giờ, hãy giữ nguyên tên hàm cần thực thi là Example
, cho mục đích kiểm thử.
Nhấp vào Deploy
để tạo và triển khai hàm. Sau khi triển khai thành công, bạn sẽ thấy dấu kiểm có vòng tròn màu xanh lục trong danh sách chức năng:
8. Kiểm thử hàm
Ở bước này, hãy kiểm thử để đảm bảo rằng hàm phản hồi các sự kiện lưu trữ.
Từ "hamburger" (Podcast) hãy quay lại trang Storage
.
Nhấp vào nhóm hình ảnh rồi nhấp vào Upload files
để tải hình ảnh lên.
Di chuyển lại trong bảng điều khiển Cloud để chuyển đến trang Logging > Logs Explorer
.
Trong bộ chọn Log Fields
, hãy chọn Cloud Function
để xem nhật ký dành riêng cho các hàm của bạn. Cuộn xuống qua các Trường nhật ký và thậm chí bạn có thể chọn một hàm cụ thể để xem nhật ký liên quan đến các hàm chi tiết hơn. Chọn hàm picture-uploaded
.
Bạn sẽ thấy các mục nhật ký đề cập đến việc tạo hàm, thời gian bắt đầu và thời gian kết thúc của hàm cũng như câu lệnh nhật ký thực tế của chúng ta:
Câu lệnh nhật ký của chúng ta có nội dung: Processing file: pic-a-daily-architecture-events.png
, nghĩa là sự kiện liên quan đến việc tạo và lưu trữ ảnh này đã thực sự được kích hoạt như mong đợi.
9. Chuẩn bị cơ sở dữ liệu
Bạn sẽ lưu trữ thông tin về hình ảnh do Vision API cung cấp vào cơ sở dữ liệu Cloud Firestore, một cơ sở dữ liệu tài liệu NoSQL nhanh, được quản lý hoàn toàn, không máy chủ và dựa trên đám mây. Chuẩn bị cơ sở dữ liệu bằng cách chuyển đến phần Firestore
trong Cloud Console:
Có hai lựa chọn: Native mode
hoặc Datastore mode
. Hãy sử dụng chế độ gốc. Chế độ này cung cấp thêm các tính năng như hỗ trợ ngoại tuyến và đồng bộ hoá theo thời gian thực.
Nhấp vào SELECT NATIVE MODE
.
Hãy chọn một khu vực (ở đây là Châu Âu, nhưng lý tưởng nhất là cùng một khu vực với chức năng và bộ chứa lưu trữ của bạn).
Nhấp vào nút CREATE DATABASE
.
Sau khi tạo cơ sở dữ liệu, bạn sẽ thấy như sau:
Tạo một bộ sưu tập mới bằng cách nhấp vào nút + START COLLECTION
.
Tập hợp tên pictures
.
Bạn không cần tạo tài liệu. Bạn sẽ thêm ảnh theo chương trình khi ảnh mới được lưu trữ trong Cloud Storage và được Vision API phân tích.
Nhấp vào Save
.
Firestore tạo tài liệu mặc định đầu tiên trong tập hợp mới tạo, bạn có thể yên tâm xoá tài liệu đó vì tài liệu đó không chứa thông tin hữu ích nào:
Các tài liệu được tạo theo chương trình trong bộ sưu tập của chúng tôi sẽ chứa 4 trường:
- name (chuỗi): tên tệp của hình ảnh đã tải lên, cũng là khoá của tài liệu
- nhãn (mảng chuỗi): nhãn của các mục được nhận dạng bằng Vision API
- color (chuỗi): mã màu thập lục phân của màu chủ đạo (ví dụ: #ab12ef)
- Đã tạo (ngày): dấu thời gian khi siêu dữ liệu của hình ảnh này được lưu trữ
- hình thu nhỏ (boolean): một trường tùy chọn sẽ hiển thị và đúng nếu đã được tạo một hình thu nhỏ cho ảnh này
Vì chúng ta sẽ tìm kiếm trong Firestore để tìm những bức ảnh có hình thu nhỏ và sắp xếp theo ngày tạo, chúng ta sẽ cần tạo một chỉ mục tìm kiếm.
Bạn có thể tạo chỉ mục bằng lệnh sau trong Cloud Shell:
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
Hoặc bạn cũng có thể thiết lập từ Cloud Console bằng cách nhấp vào Indexes
trong cột điều hướng bên trái, rồi tạo một chỉ mục tổng hợp như minh hoạ dưới đây:
Nhấp vào Create
. Quá trình tạo chỉ mục có thể mất vài phút.
10. Cập nhật hàm
Quay lại trang Functions
để cập nhật hàm gọi Vision API nhằm phân tích hình ảnh và lưu trữ siêu dữ liệu trong Firestore.
Từ "hamburger" (Podcast), hãy chuyển đến phần Cloud Functions
, nhấp vào tên hàm, chọn thẻ Source
rồi nhấp vào nút EDIT
.
Trước tiên, hãy chỉnh sửa tệp pom.xml
liệt kê các phần phụ thuộc của hàm Java. Cập nhật mã để thêm phần phụ thuộc Maven API của Cloud Vision:
<?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>
Giờ đây, các phần phụ thuộc đã được cập nhật, nên bạn sẽ xử lý mã của hàm bằng cách cập nhật tệp Example.java
bằng mã tuỳ chỉnh.
Di chuột qua tệp Example.java
rồi nhấp vào biểu tượng bút chì. Thay thế tên gói và tên tệp thành src/main/java/fn/ImageAnalysis.java
.
Thay thế mã trong ImageAnalysis.java
bằng đoạn mã dưới đây. Nội dung này sẽ được giải thích trong bước tiếp theo.
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. Khám phá hàm
Hãy cùng tìm hiểu kỹ hơn về những phần thú vị.
Trước tiên, chúng ta thêm các phần phụ thuộc cụ thể vào tệp pom.xml
Maven. Thư viện ứng dụng Java của Google phát hành một Bill-of-Materials(BOM)
để loại bỏ mọi xung đột phần phụ thuộc. Bằng cách sử dụng thư viện này, bạn không phải chỉ định bất kỳ phiên bản nào cho từng Thư viện ứng dụng của 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>
Sau đó, chúng ta chuẩn bị ứng dụng cho Vision API:
...
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
...
Bây giờ là cấu trúc của hàm. Từ sự kiện đến, chúng ta sẽ thu thập các trường mà chúng ta quan tâm rồi ánh xạ các trường đó với cấu trúc GCSEvent mà chúng ta xác định:
...
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;
}
Hãy chú ý đến chữ ký, cũng như cách chúng ta truy xuất tên của tệp và bộ chứa đã kích hoạt Hàm đám mây.
Để tham khảo, dưới đây là hình thức của tải trọng sự kiện:
{
"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"
}
Chúng tôi chuẩn bị một yêu cầu để gửi qua ứng dụng 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();
Chúng tôi yêu cầu cung cấp 3 chức năng chính của Vision API:
- Phát hiện nhãn: để tìm hiểu nội dung của những hình ảnh đó
- Thuộc tính hình ảnh: để cung cấp các thuộc tính thú vị của hình ảnh (chúng ta quan tâm đến màu chủ đạo của hình ảnh)
- Tìm kiếm an toàn: biết liệu hình ảnh đó có an toàn để hiển thị hay không (hình ảnh không được chứa nội dung người lớn / nội dung y tế / nội dung không phù hợp cho người xem chưa đến tuổi trưởng thành / nội dung bạo lực)
Lúc này, chúng ta có thể thực hiện lệnh gọi đến Vision API:
...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result =
vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...
Để tham khảo, dưới đây là phản hồi từ 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
}
Nếu không có lỗi được trả về, chúng ta có thể tiếp tục. Đó là lý do chúng ta có khối if:
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return;
}
Chúng ta sẽ lấy nhãn của những sự vật, danh mục hoặc chủ đề được nhận dạng trong ảnh:
List<String> labels = response.getLabelAnnotationsList().stream()
.map(annotation -> annotation.getDescription())
.collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
logger.info("- " + label);
}
Chúng tôi muốn biết màu chủ đạo của hình ảnh:
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);
}
Chúng ta cũng đang sử dụng hàm hiệu dụng để chuyển đổi các giá trị đỏ / xanh lục / xanh lam thành mã màu thập lục phân mà chúng ta có thể sử dụng trong biểu định kiểu CSS.
Hãy kiểm tra xem hình ảnh có an toàn để hiển thị không:
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);
}
Chúng tôi đang kiểm tra các thuộc tính người lớn / giả mạo / y tế / bạo lực / không phù hợp cho người xem chưa đến tuổi trưởng thành để xem liệu các thuộc tính đó có khả năng hoặc rất có thể hay không.
Nếu kết quả tìm kiếm an toàn là phù hợp, chúng ta có thể lưu trữ siêu dữ liệu trong 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. Triển khai hàm
Đã đến lúc triển khai hàm này.
Nhấn nút DEPLOY
để triển khai phiên bản mới, bạn có thể xem tiến trình:
13. Kiểm tra lại hàm
Sau khi triển khai hàm này thành công, bạn sẽ đăng hình ảnh lên Cloud Storage, xem hàm của chúng ta có được gọi hay không, Vision API trả về những nội dung gì và siêu dữ liệu có được lưu trữ trong Firestore hay không.
Quay lại Cloud Storage
và nhấp vào bộ chứa chúng ta đã tạo ở đầu phòng thí nghiệm:
Khi ở trong trang chi tiết về bộ chứa, hãy nhấp vào nút Upload files
để tải ảnh lên.
Từ "hamburger" (Podcast) hãy chuyển đến Trình khám phá Logging > Logs
.
Trong bộ chọn Log Fields
, hãy chọn Cloud Function
để xem nhật ký dành riêng cho các hàm của bạn. Cuộn xuống qua các Trường nhật ký và thậm chí bạn có thể chọn một hàm cụ thể để xem nhật ký liên quan đến các hàm chi tiết hơn. Chọn hàm picture-uploaded
.
Và thực sự, trong danh sách nhật ký, tôi có thể thấy rằng hàm của chúng ta đã được gọi:
Các nhật ký cho biết thời điểm bắt đầu và kết thúc quá trình thực thi hàm. Ở giữa, chúng ta có thể thấy các nhật ký mà chúng ta đã đặt vào hàm bằng câu lệnh console.log(). Chúng ta thấy:
- Thông tin chi tiết về sự kiện kích hoạt chức năng của chúng tôi,
- Kết quả thô từ lệnh gọi Vision API,
- Nhãn được tìm thấy trong hình ảnh do chúng tôi tải lên,
- Thông tin về màu chủ đạo,
- Liệu hình ảnh có an toàn để hiển thị hay không,
- Cuối cùng, những siêu dữ liệu về bức ảnh này đã được lưu trữ trong Firestore.
Lại từ "hamburger" nữa (Podcast), hãy chuyển đến phần Firestore
. Trong tiểu mục Data
(hiển thị theo mặc định), bạn sẽ thấy bộ sưu tập pictures
có tài liệu mới được thêm vào, tương ứng với ảnh bạn vừa tải lên:
14. Dọn dẹp (Không bắt buộc)
Nếu không có ý định tiếp tục sử dụng các phòng thí nghiệm khác trong chuỗi chương trình này, bạn có thể dọn dẹp các tài nguyên để tiết kiệm chi phí và trở thành một công dân tốt về công nghệ đám mây. Bạn có thể dọn dẹp từng tài nguyên như sau.
Xoá bộ chứa:
gsutil rb gs://${BUCKET_PICTURES}
Xoá hàm:
gcloud functions delete picture-uploaded --region europe-west1 -q
Xoá bộ sưu tập trên Firestore bằng cách chọn Xoá bộ sưu tập khỏi bộ sưu tập:
Ngoài ra, bạn có thể xoá toàn bộ dự án theo cách sau:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
15. Xin chúc mừng!
Xin chúc mừng! Bạn đã triển khai thành công dịch vụ mã khoá đầu tiên của dự án!
Nội dung đã đề cập
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore