- vừa được xem lúc

Tìm hiểu thư viện face-api thông qua viết ứng dụng Face Recognition

0 0 207

Người đăng: Lê Quang An

Theo Viblo Asia

Face-api là một thư viện giúp cho chúng ta thực hiện các công việc như phát hiện khuôn mặt và nhận diện khuôn mặt trên trình duyệt và nó được triển khai trên lõi của tensorFlow.js

Face-api.js model

Trước khi bắt đầu với việc làm demo thì mình muốn giới thiệu một chút về các model của thư viện Face-api.js. Hiện tại thì chúng ta có thể sử dụng được 5 model mà face-api cung cấp đó là:

  1. Face Detection:
    • SSD Mobilenet V1: Sử dụng để phát hiện khuôn mặt, bằng cách sử dụng SSD (Single Shot Multibox Detector) dựa trên MobileNetV1. Nó sẽ trả về một ô vuông giới hạn khuôn mặt và xác suất cho mỗi gương mặt mà nó phát hiệnđược. Model này giúp cho việc phát hiện khuôn mặt được nhanh hơn và có độ chính xác cao hơn. Kích cỡ của model khoảng 5.4 MB (ssd_mobilenetv1_model). Model này được "trained" bởi WIDERFACE dataset
    • Tiny Face Detector: hỗ trợ realtime face detector với thời gian nhanh hơn, kích thước nhỏ hơn, tốn ít tài nguyên hơn để so sánh với SSD Mobilenet V1 face detector, đổi lại nó hoạt động kém hơn khi phát hiện các khuôn mặt nhỏ. Model này cực kỳ thân thiện với model và web, phù hợp với các thiết bị hạn chế tài nguyên. Kích cỡ 190KB (tiny_face_detector_model).
  2. Face Landmark Detection: nó giúp phát hiện 68 điểm trên khuôn mặt của bạn một cách cực kỳ nhẹ và nhanh chóng mà lại còn chính xác nữa chứ. Kích cỡ 350KB (face_landmark_68_model) và model nhỏ chỉ 80KB (face_landmark_68_tiny_model). Cả 2 model được "trained" trong bộ dữ liệu ~35k hình ảnh khuôn mặt được gắn nhãn với 68 mốc khuôn mặt.
  3. Face Recognition: Xử dụng cho việc nhận diện khuôn mặt, một kiến trúc gần giống như ResNet-34 được triển khai để tính toán một bộ mô tả khuôn mặt (mộ vectơ đặc trưng có 128 giá trị) từ các hình ảnh khuôn mặt đã được cung cấp, cái mà sử dụng để mô tả các nét đặc trưng của khuôn mặt. Model này không giới hạn ở bộ khuôn mặt được sử dụng để "training", có nghĩa là bạn có thể sử dụng nó để nhận dạng khuôn mặt của bất kỳ người nào, chẳng hạn như khuôn mặt của bạn. Bạn có thể xác định sự giống nhau của hai khuôn mặt tùy ý bằng cách so sánh các mô tả khuôn mặt của chúng. Ví dụ bằng cách tính khoảng cách euclide hoặc sử dụng bất kỳ bộ phân loại nào khác mà bạn chọn. Kích cỡ của model này là 6.2MB (face_recognition_model).
  4. Face Expression Recognition: Model này giúp chúng ta có thể nhận đạng dược những biểu cảm trên khuôn mặt một cách nhanh chóng và cực kỳ nhẹ cùng với độ chính xác cao. Nó được "training" bởi các nguồn được public trên mạng. Lưu ý rằng độ chính xác có thể giảm xuống nếu bạn đeo kính. Kích cỡ 310Kb
  5. Age Estimation and Gender Recognition: Model này giúp chúng ta ước lượng tuổi tác cũng như nhận diện giới tính. Kích thước 420KB

Thực hành viết chương trình nhận diện khuôn mặt

Đầu tiên bạn phải tải hết model của thử viện về thì mới có thể dùng được, bạn có thể tải nó về ở trang chủ của thư viện, ở đấy có một cái example bạn git clone nó về thì ở trong thư mục weights sẽ có tất cả các model của thư viện ở đấy

Bạn hãy tự tạo một thư mục chứa project cho mình ở đây mình tạo là Face-Recognition-JavaScript chưá các file và thư mục như hình dưới

Và bỏ hết các model từ thư mục weight về thư mục models của bạn

File face-api.min.js bạn có thể lấy ở đây. Trong file index.html chúng ta thêm nội dung như sau

<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script defer src="face-api.min.js"></script> <script defer src="script.js"></script> <title>Face Recognition</title> <style> body { margin: 0; padding: 0; width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; flex-direction: column } canvas { position: absolute; top: 0; left: 0; } </style>
</head>
<body> <input type="file" id="imageUpload">
</body>
</html>

Tiếp theo ở trong file script.js chúng ta sẽ viết code ở đây để chương trình có thể chạy được

const imageUpload = document.getElementById('imageUpload')

Dòng đầu tiên trong file sẽ dùng để lấy tấm ảnh mà chúng ta tải lên bằng cách id của thẻ input chứa nó

Promise.all([
faceapi.nets.faceRecognitionNet.loadFromUri('/models'),
faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
faceapi.nets.ssdMobilenetv1.loadFromUri('/models')
]).then(start)

Tiếp theo chúng ta sẽ phải load các models của thư viện cần dùng như ở đây chúng ta cần model faceRecognitionNet, faceLandmark68Net và ssdMobilenetv1. Ở đây chúng ta dùng Promise là để đợi cho đến khi load xong cả 3 models kia thì mới thực hiện tiếp (chạy hàm start).

async function start() { const container = document.createElement('div') container.style.position = 'relative' document.body.append(container) const labeledFaceDescriptors = await loadLabeledImages() const faceMatcher = new faceapi.FaceMatcher(labeledFaceDescriptors, 0.6) let image let canvas document.body.append('Loaded') imageUpload.addEventListener('change', async () => { if (image) image.remove() if (canvas) canvas.remove() image = await faceapi.bufferToImage(imageUpload.files[0]) container.append(image) canvas = faceapi.createCanvasFromMedia(image) container.append(canvas) const displaySize = { width: image.width, height: image.height } faceapi.matchDimensions(canvas, displaySize) const detections = await faceapi.detectAllFaces(image).withFaceLandmarks().withFaceDescriptors() const resizedDetections = faceapi.resizeResults(detections, displaySize) const results = resizedDetections.map(d => faceMatcher.findBestMatch(d.descriptor)) results.forEach((result, i) => { const box = resizedDetections[i].detection.box const drawBox = new faceapi.draw.DrawBox(box, { label: result.toString() }) drawBox.draw(canvas) }) })
} function loadLabeledImages() { const labels = ['Black Widow', 'Captain America', 'Captain Marvel', 'Hawkeye', 'Jim Rhodes', 'Thor', 'Tony Stark'] return Promise.all( labels.map(async label => { const descriptions = [] for (let i = 1; i <= 2; i++) { const img = await faceapi.fetchImage(`https://raw.githubusercontent.com/WebDevSimplified/Face-Recognition-JavaScript/master/labeled_images/${label}/${i}.jpg`) const detections = await faceapi.detectSingleFace(img).withFaceLandmarks().withFaceDescriptor() descriptions.push(detections.descriptor) } return new faceapi.LabeledFaceDescriptors(label, descriptions) }) )
}

Tiếp theo sẽ là 2 function này mình sẽ bắt đầu giải thíc với function loadLabeledImages trước sau đó sẽ là function start

Với function loadLabeledImages

function loadLabeledImages() { const labels = ['Black Widow', 'Captain America', 'Captain Marvel', 'Hawkeye', 'Jim Rhodes', 'Thor', 'Tony Stark'] return Promise.all( labels.map(async label => { const descriptions = [] for (let i = 1; i <= 2; i++) { const img = await faceapi.fetchImage(`https://raw.githubusercontent.com/WebDevSimplified/Face-Recognition-JavaScript/master/labeled_images/${label}/${i}.jpg`) const detections = await faceapi.detectSingleFace(img).withFaceLandmarks().withFaceDescriptor() descriptions.push(detections.descriptor) } return new faceapi.LabeledFaceDescriptors(label, descriptions) }) )
}

Bắt đầu chúng ta sẽ tạo một hằng chứa tên các nhân vật trong avenger ví dụ như Black Widow, Captain America, ... Đây sẽ là tên được hiện ra nếu hệ thống nhận diện được hình ảnh của người đó có tên là gì? Những hình ảnh của người này sẽ được lưu trong thư mục labeled_images, và với mỗi thứ mục tên người dùng bên trong sẽ chứa những ảnh của người đó để hệ thống có thể xác nhận người đấy. Càng nhiều ảnh của người đấy thì hệ thống nhận diện càng chính xác.

Ở đây chúng ta dùng Promise sẽ giúp chúng ta không rơi vào callback hell vì chúng ta sẽ load tất cả những hình ảnh khác nhau và thực hiện việc nhận diện khuôn mặt trong những bức ảnh đấy. bên trong chúng ta tạo một vòng lặp for để kiểm tra từng ảnh một const img = await faceapi.fetchImage(https://raw.githubusercontent.com/WebDevSimplified/Face-Recognition-JavaScript/master/labeled_images/label/{label}/{i}.jpg) Ở đây ở đây mình dùng một link chứa thư mục ảnh vì dùng thông qua live server. Bạn có thể chuyền vào thư mục labeled_images để sử dụng cũng được không sao.

Và tiếp theo chúng ta cũng phải xác định được khuôn mặt của người đó trong ảnh và lưu facedecriptor của nó và chúng ta sẽ lưu nó lại ở trong mảng descriptions. và cuối cùng là trả về label và descriptions của nó return new faceapi.LabeledFaceDescriptors(label, descriptions)

Tiếp theo sẽ là hàm start

async function start() { const container = document.createElement('div') container.style.position = 'relative' document.body.append(container) const labeledFaceDescriptors = await loadLabeledImages() const faceMatcher = new faceapi.FaceMatcher(labeledFaceDescriptors, 0.6) let image let canvas document.body.append('Loaded') imageUpload.addEventListener('change', async () => { if (image) image.remove() if (canvas) canvas.remove() image = await faceapi.bufferToImage(imageUpload.files[0]) container.append(image) canvas = faceapi.createCanvasFromMedia(image) container.append(canvas) const displaySize = { width: image.width, height: image.height } faceapi.matchDimensions(canvas, displaySize) const detections = await faceapi.detectAllFaces(image).withFaceLandmarks().withFaceDescriptors() const resizedDetections = faceapi.resizeResults(detections, displaySize) const results = resizedDetections.map(d => faceMatcher.findBestMatch(d.descriptor)) results.forEach((result, i) => { const box = resizedDetections[i].detection.box const drawBox = new faceapi.draw.DrawBox(box, { label: result.toString() }) drawBox.draw(canvas) }) })
}

Chúng ta sẽ gọi hàm image ở đây const labeledFaceDescriptors = await loadLabeledImages() và gán giá trị nhận được vào biến labeledFaceDescriptors và tiếp theo chúng ta sẽ sử dụng hàm FaceMatcher ở đây tham số đầu tiên là labeledFaceDescriptors sẽ bao gồm tên mà mô tả gương mặt của người ấy, tham số thứ 2 là phần trăm gương mặt giống với mô tả ở đây đang để là 0,6 tương ứng với 60%.

image = await faceapi.bufferToImage(imageUpload.files[0]) Ở đây chúng ta sẽ chỉ nhận 1 file ảnh được up lên và convers nó bằng hàm bufferToImage để thư viện có thể đọc được.

 container.append(image) canvas = faceapi.createCanvasFromMedia(image) container.append(canvas) const displaySize = { width: image.width, height: image.height } faceapi.matchDimensions(canvas, displaySize)

Đoạn này sẽ giúp chúng ta có thể vẽ được một hình vuông xung quanh khuôn mặt mà ta có thể thấy được trong bức ảnh const detections = await faceapi.detectAllFaces(image).withFaceLandmarks().withFaceDescriptors() Ở đây chúng ta sẽ gọi hàm detectAllFaces(image) và truyền vào đó bức ảnh mà chúng ta upload lên để xem bên trong có tất cả toàn bộ bao nhiêu gương mặt và chúng ta làm điều này với withFaceLandmarks() Điều này sẽ giúp cho thuật toán có thể phân biệt được các khuôn mặt khác nhau và withFaceDescriptors() sẽ giúp chúng ta vẽ một hình vuông xung quanh khuôn mặt nhận dạng được

const resizedDetections = faceapi.resizeResults(detections, displaySize) Hàm này sẽ giúp chúng ta resize lại hình vuống với kích cỡ mà mình mong muốn

const results = resizedDetections.map(d => faceMatcher.findBestMatch(d.descriptor)) ở đây chúng ta sẽ goi faceMactcher sẽ so sánh từng khuôn mặt trong ảnh và mẫu với kết quả tốt nhất (findBestMatch(d.descriptor)) để ra kết quả nhận diện

 results.forEach((result, i) => { const box = resizedDetections[i].detection.box const drawBox = new faceapi.draw.DrawBox(box, { label: result.toString() }) drawBox.draw(canvas) })

Cuối cùng thì chỉ cần loop qua từng kết quả và in ra màn hình thôi

Nguồn tham khảo

https://github.com/justadudewhohacks/face-api.js https://www.youtube.com/watch?v=AZ4PdALMqx0

Bình luận

Bài viết tương tự

- vừa được xem lúc

2021, chúng ta cần tối ưu hóa việc tải hình ảnh trên web như nào?

Rất chào các bạn,. Như các bạn đã biết, trong kỉ nguyên công nghệ, song song với sự sinh ra dày đặc của các trang web mới cũng là sự biến mất của những trang web "lạc hậu" hay hoạt động kém hiệu quả.

0 0 56

- vừa được xem lúc

Cách mình "hack" được vào hẹ thống của SMAS để xem điểm.

Cách mà mình "hack" được vào hệ thống của SMAS. Thật ra dùng từ hack cũng không đúng lắm, chỉ là một vài trick để lừa hệ thống mà thôi.

0 0 145

- vừa được xem lúc

Giải thích một số JAVASCRIPT ARRAY METHOD với EMOJIS

Như chúng ta đã biết, Array trong JS có rất nhiều method tiện dụng có thể hỗ trợ chúng ta. Sau đây là một số method thông dụng được giải thích bằng các emoji. Thêm một hoặc nhiều phần tử vào sau mảng. livestock.

0 0 46

- vừa được xem lúc

Testing trong Javascript với Jest (Phần 1)

Hello 500 anh em, lại là mình đây. Chú bé coder yêu màu tím thích màu hồng và ghét sự giả dối đây .

0 0 270

- vừa được xem lúc

Áp dụng kiến trúc 3 Layer Architecture vào project NodeJS

The problem encountered. Các framework nodejs phổ biết như Express cho phép chúng ta dễ dàng tạo ra Resful API xử lí các request từ phía client một cách nhanh chóng và linh hoạt.

0 0 80

- vừa được xem lúc

Một số String methods cần biết trong javascript

String là một trong những phần quan trọng nhất trong javascript, ngoài những methods hay dùng như trim, concat, subString, toUpperCase, toLowerCase; Javascript còn cung cấp cho chúng ta rất nhiều methods hữu ích khác để thao tác và giải quyết các vấn đề dễ dàng hơn khi làm việc với String. Mặc dù nh

0 0 39