Gắn râu JAV Idol – Phần 2: Làm quen với các thư viện và API cần sử dụng

Series này gồm 3 phần:

  1. Cơ chế hoạt động của các filter Snapchat và Facebook Messenger
  2. Làm quen với các thư viện và API cần sử dụng
  3. Gắn râu bằng cách kết hợp Face Detection + Image Processing và… Toán Học

Ở phần trước, chúng ta đã tìm hiểu về cơ chế hoạt động của các filter trên Snapchat và Facebook. Sau đó, chúng ta đã setup code gọi API để nhận diện các đặc điểm của khuôn mặt.

Trong phần này, chúng ta sẽ đi sâu vào việc sử dụng kĩ thuật xử lý hình ảnh để… gắn râu vào khuôn mặt nhé!

Ngâm cứu kết quả của API nhận diện

Ở kì trước, sau khi đưa url hình ảnh vào nhận diện, Microsoft Cognitive API sẽ trả cho ta một JSON array.

Mỗi phần tử trong array là một khuôn mặt nhận diện được. Các property và value trong object “faceLandmarks” chính là những đặc điểm nhận dạng trên khuôn mặt.


[
{
"faceId": "2f3e1309-6e6f-4c2c-92a0-c68d898ba571",
"faceRectangle": { // Tọa độ khuôn mặt
"top": 109,
"left": 209,
"width": 109,
"height": 109
},
"faceLandmarks": { // Đặc điểm nhận dạng
"pupilLeft": {
"x": 234.1,
"y": 147.2
},
"pupilRight": {
"x": 276.2,
"y": 128.4
},
"noseTip": {
"x": 264.3,
"y": 169.3
},
"mouthLeft": {
"x": 257.1,
"y": 197.5
},
"mouthRight": {
"x": 290.7,
"y": 178.9
},
"upperLipTop": {
"x": 272.8,
"y": 184.6
},
"upperLipBottom": {
"x": 274.9,
"y": 188.9
},
"underLipTop": {
"x": 275.5,
"y": 190.3
},
"underLipBottom": {
"x": 278,
"y": 195.4
}
}
}
]

Ta dựa theo json này để xác định các điểm trong hình. Giả sử như landmark pupilLeft – đồng tử mắt trái sẽ nằm ở điểm có tọa độ x=234.1, y=147.2 tính từ góc trên bên trái.

Để dễ hiểu, mình có dùng code để vẽ một tấm ảnh chứa toàn bộ những landmark mà Microsoft Cognitive có thể nhận biết được.

Các bạn bấm vào hình để xem ảnh lớn cho rõ nhé

Có thể nói API nhận diện của Microsoft trả về khá nhiều thứ hay ho! Những API khác mình dùng chỉ trả về vùng khuôn mặt hoặc 2 mắt.

API của Microsoft trả về các điểm quan trọng như: hai mắt, môi trên môi dưới, mũi… Những thứ này là quá đủ để chúng ta có thể nghịch ngợm, bày trò gắn filter.

Tìm hiểu thư viện xử lý hình ảnh của NodeJS

Có kết quả rồi, bây giờ chúng ta bắt đầu xử lý hình ảnh nào! Để đỡ tốn công viết code lại từ đầu, chúng ta sử dụng thư viện để xử lý hình ảnh cho tiện.

Sau một hồi Google “NodeJS image processing library”, mình tìm được 3 thư viện khá ổn:

  • gm: ~4800 starts trên Github. Đây là wrapper cho 2 thư viện GraphicsMagick hoặc ImageMagicks, muốn dùng phải cài kèm theo
  • jimp: ~5900 stars trên Github
  • sharp: ~8000 stars trên Github. Thư viện xử lý ảnh nhanh nhất NodeJS, dựa trên libvips

Theo review thì performance của sharp là nhanh nhất, gấp mấy lần gm và mười mấy lần jimp.

Performance của sharp ăn đứt hẳn mấy đứa khác

Tuy vậy, do API của sharp không load được ảnh từ URL nên mình lười, dùng jimp demo luôn. Các bạn nào build hệ thống production thì nên tìm hiểu và dùng sharp nhé!

Các API JIMP chúng ta sẽ dùng

Các bạn có thể xem đầy đủ các API của jimp tại đây: https://github.com/oliver-moran/jimp

Trong bài này, chúng ta chỉ sử dụng vài API cơ bản nhất dưới đây:

  • read: Đọc file ảnh từ local hoặc url
  • write: Ghi ảnh đã xử lý xuống 1 file riêng
  • resize: Thu nhỏ, phóng to một ảnh
  • rotate: Xoay ảnh theo chiểu ngược kim đồng hồ
  • composite: Ghép ảnh này vào ảnh kia

Các bạn có thể viết code nghịch thử các API này cho quen. Tới đây chúng ta bắt đầu trò vui được rồi nhé!

Tất nhiên, để gắn râu vào ảnh, chúng ta phải … có râu để gắn trước đã. Mình đã chuẩn bị sẵn cho các bạn rồi đây! Các bạn click vào file ảnh râu phía dưới, bấm tải và lưu tên mustache.png nhé.

Ảnh râu:

Bộ râu chúng ta sẽ sử dụng để … gắn

Ảnh gốc để gắn râu:

App tên Gắn Râu Sơn Tùng mà ngắm ảnh trai hoài chán quá nên mình đổi thành Gắn Râu Yua Mikami vậy.

Bắt đầu… gắn râu vào ảnh (image processing)

Chúng ta cứ viết code thử đã nhé!

Các bạn sửa file index.js lại như sau (nhớ đọc comment để hiểu code làm gì nha), sau đó chạy “node index.js”


const Jimp = require("jimp");
async function addMustache(url, output) {
let source = await Jimp.read(url); // Đọc ảnh từ URL
const mustache = await Jimp.read('mustache.png'); // Đọc file râu
// Gắn file râu vào vị trí x=0, y=0, lưu ra output
return source.composite(mustache, 0, 0).write(output);
}
(async () => {
const imageUrl = 'https://pbs.twimg.com/media/DWr05hUXcAA9s8n.jpg';
await addMustache(imageUrl, 'jav_rau.jpg');
})();

view raw

index.js

hosted with ❤ by GitHub

Chúng ta sẽ được một tấm hình “dị hợm” như sau:

Cái này là gắn râu vào ảnh chứ đâu phải vào mặt đâu!!

 

Ế, rõ ràng chúng ta đã gắn râu vào ảnh rồi còn gì, chỉ là râu… hơi bự. Chúng ta thử sửa lại hàm addMustache, resize bộ râu lại cho dài bằng ảnh nhé!


async function addMustache(url, output) {
let source = await Jimp.read(url);
// Lấy chiều cao, rộng của ảnh
const { height, width } = source.bitmap;
const mustache = await Jimp.read('mustache.png');
// Thu nhỏ bộ râu lại
mustache.resize(width, Jimp.AUTO);
return source.composite(mustache, 0, 0).write(output);
}

view raw

addMustache.js

hosted with ❤ by GitHub

Kết quả thu được có khả quan hơn một chút.

Bộ râu bây giờ đã dài bằng ảnh, che luôn khuôn mặt

Để gắn bộ râu chính xác vào mặt quả là không dễ!

Ta phải xác định được kích thước râu, vị trí đặt râu… Thông thường những việc này được làm bằng tay, ta phải tự đo đạc, xác định vị trí.

Để máy tính có thể tự xác định những điều này, chúng ta phải dùng tới kĩ thuật … Face Detection. Chi tiết thế nào, các bạn xem phần sau sẽ rõ nhé.

Tạm kết

Định viết tiếp nhưng tự dưng thấy phần này hơi dài rồi, đành phải cắt ra phần khác. Nếu có thắc mắc hay khó khăn gì trong lúc làm theo, các bạn có thể hỏi trong mục comment nhé.

Ở phần 3 của series, chúng ta sẽ hoàn thành việc gắn râu cho Sơn Tùng, lộn, gắn râu cho Yua Mikami nhé!

 

2 thoughts on “Gắn râu JAV Idol – Phần 2: Làm quen với các thư viện và API cần sử dụng”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s