[AWS Serverless] - Sử dụng Golang và Amazon Rekognition để xây dựng API tìm kiếm khuôn mặt bằng hình ảnh
Serverless là gì
Hãy xem lại bài viết này của mình để nắm căn bản phần Serverless này.
Mô hình hệ thống chúng ta sẽ xây dựng hôm nay
Giới thiệu Amazon Rekognition (Nhân vân chính của bài viết)
Amazon Rekognition là một dịch vụ Serverless của AWS hỗ trợ chúng ta xây dựng các chương trình cần sử dụng đến AI về phân tích hình ảnh, phân tích video, nhận diện vật thể trong hình mà hoàn toàn không yêu cầu người sử dụng có kiến thức về Machine Learning hoặc AI. Chỉ cần gọi dịch vụ thông qua AWS SDK hoặc API là chúng ta có thể tương tác với AI model mà AWS xây dựng sẵn.
Trong bài viết này mình sử dụng Amazon Rekognition để xây một thư viện hình ảnh các khuôn mặt của người dùng. Chúng ta sẽ có 2 API, một dùng để Index(tức là thêm khuôn mặt vào thư viện) và hai là chức năng tìm kiếm khuôn mặt bằng hình ảnh. API sẽ cho chúng ta biết username của khuôn mặt mà chúng ta truyền vào khi tìm kiếm và % trùng khớp giữa hình truyền vào và hình gốc trong thư viện ảnh.
Ứng dụng này có thể áp dụng cho các tình huống như là máy chấm công bằng khuôn mặt tại công ty, hoặc là cho khách hàng đăng nhập bằng cách mở camera chụp hình khuôn mặt. Vì đây là Serverless nên các bạn không phải lo về tốc độ và cỗ máy xử lý bên dưới. Qua tìm hiểu của mình thì thư viện khuôn mặt có thể xử lý thoải mái trong khoảng 10 triệu khuôn mặt, còn hơn thế nữa thì mình chưa thử hihi.
Chuẩn bị môi trường
- Cài đặt Nodejs(Vì mục 2 cài bằng NPM), chi tiết ở link này
- Cài đặt Serverless Framework, chi tiết ở link này
- Cài đặt AWS CLI, chi tiết ở link này
- Cài đặt Golang, chi tiết ở link này
- Do ví dụ sử dụng lệnh make nên mình sử dụng Ubuntu/MacOS.
Cài đặt AWS CLI để kết nối tài khoản AWS của bạn
aws configure
Các bạn cài đặt Client ID và Client Secret như bên dưới, 2 thông số còn lại optional bạn không cài cũng được.
AWS Access Key ID [****]:
AWS Secret Access Key [****]:
Default region name [us-west-1]:
Default output format [None]:
Tạo Amazon Rekognition và AWS S3 để lưu ảnh khuôn mặt và file hình
- Hãy tạo kho khuôn mặt(face collection) bằng câu lệnh CLI bên dưới. Tên collection này mình đặt là "viblo", nếu các bạn đổi thì nhớ vào file serverless.yml đổi lại cho khớp nhé(dòng 26).
aws rekognition create-collection --collection-id "viblo"
- Tiếp theo chạy câu lệnh CLI bên dưới để tạo S3 Bucket lưu trữ hình ảnh khuôn mặt. Tên S3 bucket này mình đặt là "viblo-facecollection", nếu các bạn đổi thì nhớ vào file serverless.yml đổi lại cho khớp nhé(dòng 27).
aws s3api create-bucket --bucket viblo-facecollection --region ap-southeast-1
Hãy xem cấu trúc chính của sourcecode nào !
Các bạn clone sourcecode mình viết về máy bằng lệnh bên dưới (hãy cho mình một star để ủng hộ mình ra thêm nhiều bài viết và source mẫu nhé):
git clone https://github.com/tanthanhkid/serverless-aws-golang-face-collection-with-aws-rekognition.git
Đầu tiên chúng ta xem file serverless.yml để xem các thành phần mà source Serverless này sẽ tạo trên account AWS của chúng ta.
serverless.yml
Đoạn này nói cho chúng ta biết sourcecode này sẽ deploy lên tài khoản AWS với runtime của Golang và version 1.x ở Region ap-southeast-1(Singapore). Ngoài ra, bên dưới còn cho chúng ta điền thêm một biến môi trường của Lambda, nơi chúng ta sẽ để connection string và để khi code chạy sẽ lấy connection string từ đây ra để kết nối vào DB Instance.
provider:
name: aws
runtime: go1.x
region: ap-southeast-1
environment:
COLLECTION_ID: viblo # put your AWS Rekognition collection name here
FACES_BUCKET: viblo-facecollection # put your S3 bucket for storing image here
iam:
role:
statements:
# Allow functions to have full access to AWS Rekognition (not for production)
- Effect: Allow
Action: 'rekognition:*'
Resource: '*'
# Allow functions to hve full access to AWS S3 and DynamoDB (not for production)
- Effect: Allow
Action:
- 's3:*'
Resource:
- '*'
Đoạn code bên dưới mình có bổ sung thêm phần bảo mật so với bài trước, Bởi vì API của chúng ta public internet, nên việc vô tình lộ URL và Request có thể dẫn tới bị tấn công từ các bên có chủ đích phá hoại. Hậu quả có thể xảy ra là dịch vụ của chúng ta bị dừng hoạt động hoặc quá tải, bill AWS tăng đột biến.
Cách xử lý là chúng ta phải có một x-api-key(được in ra trong console sau khi deploy serverless hoặc các bạn vào AWS Console để lấy) gắn vào header như sau:
plugins:
- serverless-add-api-key
custom:
apiKeys:
dev:
- name: facecollection
usagePlan:
name: "facecollection"
description: "Description of first plan"
quota:
limit: 100000 # ở đây mình cho 10k request/tháng, nếu vô tình client làm lộ key thì chỉ mất 10k request và chúng ta hoàn toàn có thể kiểm soát tình hình bằng nhiều cách phản ứng khác nhau, ví dụ disable và tạo key mới
period: MONTH
throttle: # cấu hình này cho phép chúng ta kiểm soát số lượng request được xử lý cùng lúc
burstLimit: 100
rateLimit: 20
Đến phần cuối cùng của file serverless.yml chúng ta sẽ phân tích đoạn code dùng để tạo AWS Lambda bằng Golang, sau đó code Golang sẽ gọi tiếp tới Amazon Rekognition và AWS S3, chúng ta có 2 API là index và search được mô tả như bên dưới.
postindex: # API này dùng để đưa hình vào thư viện ảnh khuôn mặt
handler: bin/postIndex # các bạn xem file MakeFile sẽ thấy code Go được build ra bin và chỗ này lấy file build và đẩy lên AWS
package:
include:
- ./bin/postIndex
events:
- http:
path: index # các bạn gọi vào thông qua path /insert
method: post # HTTP Method là POST
private: true # true để bật chức năng xác thực x-api-key trước khi xử lý tiếp
postsearch: # API này dùng để tìm kiếm khuôn mặt trong thư viện ảnh bằng hình ảnh do người dùng truyền vào
handler: bin/postSearch
package:
include:
- ./bin/postSearch
events:
- http:
path: search
method: post
private: true
Tiếp theo đến phần code chính, xử lý các tương tác với AWS SDK để sử dụng Amazon Rekognition và AWS S3
postIndex/postIndex.go
Đầu tiên ở API /index, chúng ta sẽ parse hình ảnh được upload lên bằng dạng Base64 và upload file hình này lên S3, vì Rekognition sẽ lấy hình từ S3 về đẩy vào kho hình khi mình phát lệnh cho nó.
# từ dòng 136
//parse image from base 64 and upload to S3 bucket
decodedSignature, err := base64.StdEncoding.DecodeString(image)
if err != nil {
log.Fatalf("decode base64 failed, %v", err)
}
r := bytes.NewReader(decodedSignature)
//create s3 input
s3ObjectName := userName + ".jpg"
s3Input := &s3.PutObjectInput{
Body: r,
Bucket: &facesBucket,
Key: &s3ObjectName,
}
//create new session
sess, err := createSession()
if err != nil {
log.Fatalf("failed to create AWS session, %v", err)
}
s3 := s3.New(sess)
//upload image file to s3
s3output, err := s3.PutObject(s3Input)
Sau đó, chúng ta sẽ phát lệnh cho Amazon Rekognition là index tấm hình khuôn mặt này, lưu ý hình up lên nên là đuôi jpg hoặc jpeg. Thỉnh thoảng up file png hoặc chất lượng quá kém thì bị Error lỗi invalid image format.
# từ dòng 168
//get image from S3 bucket and index with rekognition
input := &rekognition.IndexFacesInput{
Image: &types.Image{
S3Object: &types.S3Object{
Bucket: &facesBucket,
Name: &s3ObjectName,
},
},
CollectionId: &collectionId,
ExternalImageId: &userName,
}
output, err := client.IndexFaces(context.TODO(), input)
Vậy là xong, chúng ta chỉ việc gọi hàm indexFace() trong Handler của Lambda là kết thúc API này.
# từ dòng 85
// call AWS Rekognition to index face
output, err := indexFace(bodyRequest.Data.UserName, bodyRequest.Data.Image)
postSearch/postSearch.go
Tiếp theo, khi khuôn mặt đã được lưu trữ trong collection, mình xây dựng tiếp API /search để tìm kiếm khuôn mặt bằng hình ảnh(bạn nghĩ tới ví dụ máy chấm công hoặc FaceID trên iphone là sẽ dễ hiểu).
Tương tự ở trên, đầu tiên chúng ta phải upload file ảnh lên S3 trước.
# từ dòng 129
//parse image from base 64 and upload to S3 bucket
decodedSignature, err := base64.StdEncoding.DecodeString(image)
if err != nil {
log.Fatalf("decode base64 failed, %v", err)
}
r := bytes.NewReader(decodedSignature)
//create s3 input
s3ObjectName := uuid.NewString() + ".jpg"
s3Input := &s3.PutObjectInput{
Body: r,
Bucket: &facesBucket,
Key: &s3ObjectName,
}
//create new session
sess, err := createSession()
if err != nil {
log.Fatalf("failed to create AWS session, %v", err)
}
s3 := s3.New(sess)
//upload image file to s3
s3output, err := s3.PutObject(s3Input)
Sau đó, chúng ta gọi Amazon Rekognition để truyền khuôn mặt này vào để xem có khuôn mặt nào trong collection trùng khớp không.
# từ dòng 161
client := rekognition.NewFromConfig(cfg)
input := &rekognition.SearchFacesByImageInput{
Image: &types.Image{
S3Object: &types.S3Object{
Bucket: &facesBucket,
Name: &s3ObjectName,
},
},
CollectionId: &collectionId,
}
output, err := client.SearchFacesByImage(context.TODO(), input)
Chúng ta gọi hàm searchface() ở Handler của Lambda.
# từ dòng 80
ouput, err := searchface(bodyRequest.Data.Image)
Tiếp theo đó tới phần build code và đẩy lên AWS. khi chạy lệnh make deploy
thì MakeFile sẽ lần lượt chạy vào các folder và build source Go thành các gói và để trong folder /bin. Cuối cùng, chạy lệnh sls deploy
để bắt đầu quá trình build và deploy lên AWS.
MakeFile
.PHONY: build clean deploy
build:
cd postIndex && env GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -o ../bin/postIndex ./postIndex.go && cd ..
cd postSearch && env GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -o ../bin/postSearch ./postSearch.go && cd ..
clean:
rm -rf ./bin ./vendor Gopkg.lock
deploy: clean build
sls deploy --verbose
Chạy thôi nào!
Nếu các bạn sử dụng Ubuntu và MacOS thì chỉ cần chạy lệnh bên dưới ở ngoài root:
make deploy
Các bạn truy cập vào tài khoản AWS của mình, sau đó vào Cloudformation và chọn stack có tên bắt đầu bằng "goservice".
Vào tab Output để lấy API Endpoint.
Hãy thử nào dùng AI của chúng ta nào !
Đầu tiên mình sẽ lấy ảnh Mỹ Tâm để parse ra base64 bằng web này như bên dưới:
Sau đó gọi Postman như hình dưới:
Tiếp, theo mình lặp lại với ảnh Hồ Ngọc Hà.
Gọi Postman như hình dưới:
OK! Bây giờ thử lấy 1 tấm ảnh khác của Mỹ Tâm để truyền vào tìm kiếm xem AI tìm ra kết quả như thế nào nhé:
Như các bạn thấy, AI đã xác định được khuôn mặt trùng khớp trong thư viện ảnh. Các bạn hãy thử thêm với Hồ Ngọc Hà và kiểm chứng AI này thêm ở máy mình.
Chúc các bạn thành công !
All rights reserved