Trong một thử nghiệm vào năm 2017, một hệ thống camera giám sát ở Quế Dương, Trung Quốc chỉ mất 7 phút để xác định được vị trí một phóng viên dựa vào ảnh khuôn mặt của họ. Đây là kết quả của một mạng lưới camera quy mô lớn cùng với công nghệ nhận dạng khuôn mặt (Face Recognition). Nếu để ý một chút thì chúng ta sẽ thấy Face Recognition ngày càng được sử dụng rộng rãi trong đời sống hàng ngày như tự động tag ảnh trên facebook, mở điện thoại bằng FaceID trên Iphone, …
Hãy cùng mình tìm hiểu xem công nghệ này là gì, hoạt động như thế nào và thực hành với một ví dụ nho nhỏ ở cuối bài nha.
Face recognition hoạt động như thế nào

Nhận dạng khuôn mặt là công nghệ xác định danh tính của một cá nhân dựa trên khuôn mặt của họ. Một hệ thống hay phần mềm nhận dạng khuôn mặt thường bao gồm 3 bước chính:
- Xác định vị trí khuôn mặt (Face Detection)
- Trích xuất đặc trưng ảnh (Feature Extraction)
- Nhận dạng khuôn mặt (Recognition)
Xác định vị trí khuôn mặt (Face Detection)

Đây là bước xác định khuôn mặt người từ những bức ảnh hoặc băng hình (images or videos). Những khuôn mặt này sẽ được “cắt” ra để dùng cho bước tiếp theo.
Đã có một vài bài viết liên quan đến Face Detection nên mình không đi sâu vào nữa, các bạn có thể tham khảo thêm những bài sau:
Trích xuất đặc trưng ảnh (Feature Extraction)

Ở bước này, chúng ta sẽ cần một mô hình để trích xuất đặc trưng từ ảnh khuôn mặt (Feature Extractor). Những đặc trưng được trích xuất từ mô hình này còn được gọi là Face Embeddings hay Face Encodings, chúng ta có thể hiểu là cách biểu diễn khác của ảnh khuôn mặt giúp cho việc nhận dạng khuôn mặt dễ dàng hơn (thay vì những điểm ảnh đơn thuần). Face Embeddings có một đặc tính rất thú vị: những khuôn mặt giống nhau sẽ có Face Embeddings “gần” nhau hơn (dựa trên khoảng cách Euclid).

Điều này đạt được khi mô hình trích xuất đặc trưng được thiết kế riêng cho việc phân biệt / nhận dạng khuôn mặt. Bài hôm này không đi sâu vào cách xây dựng dạng mô hình này, các bạn có thể tìm hiểu thêm về mạng Siamese, mô hình FaceNet hay mô hình dlib ResNet.
Nhận dạng khuôn mặt (Recognition)

Với đặc tính của Face Embeddings đề cập ở trên, một phương pháp đơn giản để nhận dạng khuôn mặt là dựa trên khoảng cách giữa các Face Embeddings với nhau. Hãy tưởng tượng mỗi khuôn mặt có thể được biểu diễn bằng một điểm toạ độ trên trục xy, nếu khoảng cách giữa 2 điểm càng gần nhau, khả năng 2 khuôn mặt thuộc về cùng 1 người càng cao. Ngược lại, nếu khoảng cách này quá lớn (vượt qua một ngưỡng nhất định), chúng ta có thể “tự tin” rằng 2 khuôn mặt thuộc về 2 người khác nhau.
Thực hành
Mục tiêu của bài thực hành hôm nay là viết một đoạn code có khả năng xác định được tên cầu thủ bóng đá. Chúng ta sẽ dùng thư viện face_recognition với các mô hình sau:
- Xác định vị trí khuôn mặt với dlib HOG
- Trích xuất đặc trưng ảnh với mô hình dlib ResNet
Nếu các bạn để ý thì đây đều là các mô hình từ thư viện dlib. Thực ra
face_recognition
được xây dựng dựa trêndlib
nhưng lại dễ sử dụng hơn nhiều cho “nhiệm vụ” nhận dạng khuôn mặt.
Cấu trúc code
.
├── images
│ ├── ronaldinho
│ │ ├── ronaldinho1.jpeg
│ │ └── ronaldinho2.jpeg
│ ├── zidane
│ │ ├── zidane1.jpeg
│ │ └── zidane2.jpeg
│ └── test
│ ├── test1.jpeg
│ └── test2.jpeg
├── recognize.py
└── utils.py
- File
recognize.py
là file code chính được sử dụng - File
utils
chứa các hàm được dùng trongrecognize.py
- Thư mục
images
chứa ảnh của các cầu thủ đã biết (thư mụcronaldinho
vàzidane
) và ảnh mới để kiểm tra (trong thư mụctest
)
Để áp dụng nhận dạng khuôn mặt, chúng ta có thể dùng đoạn code sau
python recognize.py --image [đường dẫn đến ảnh]
Các bạn hãy thử áp dụng với ảnh trong thư mục test
có sẵn trong bài hoặc thử nghiệm với ảnh của bạn nhé.
Nội dung code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# trích xuất face embeddings / encodings của ảnh khuôn mặt đã biết
known_faces = [
("ronaldinho", "images/ronaldinho/ronaldinho1.jpeg"),
("ronaldinho", "images/ronaldinho/ronaldinho2.jpeg"),
("zidane", "images/zidane/zidane1.jpeg"),
("zidane", "images/zidane/zidane2.jpeg"),
]
known_names = []
known_encodings = []
for name, image_path in known_faces:
image = load_image(image_path)
encoding = face_image_to_encoding(image)
known_names.append(name)
known_encodings.append(encoding)
# trích xuất face embeddings / encodings của ảnh mới
new_image = load_image(new_image_path)
new_encoding = face_image_to_encoding(new_image)
# tính khoảng cách từ embeddings của ảnh mới đến tất cả embeddings
# của ảnh đã biết và tìm ảnh có khoảng cách gần nhất
face_distances = compute_face_distances(known_encodings, new_encoding)
name, distance = get_most_similar_face(face_distances, known_names)
# xác định tên trong ảnh mới
if distance > 0.6:
print("Unknown face")
else:
print("New image was recognized as: {}".format(name))
- Dòng 2 - 15: trích xuất đặc trưng khuôn mặt của những ảnh đã biết với hàm
face_image_to_encoding
. Ở đây chúng ta chỉ dùng ảnh của 2 cầu thủ bóng đá, Ronaldinho và Zidane, còn trong thực tế thì số lượng này lớn hơn nhiều (có thể lên tới vài nghìn đến vài triệu tuỳ ứng dụng). Do đó, thường thì các đặc trưng này được trích xuất trước để tăng tốc độ chạy code. - Dòng 17 - 18 : trích xuất Face Embeddings của ảnh mới
- Dòng 21 - 22: tính khoảng cách giữa Face Embeddings của ảnh mới với những ảnh đã biết và tìm ảnh có khoảng cách gần nhất
- Dòng 40 - 43: xác định tên trong ảnh mới dựa trên khoảng cách đến ảnh gần nhất. Nếu khoảng cách này nhỏ hơn 0.6 thì lấy tên của ảnh gần nhất, còn không thì xác định đây là một người mới, chưa có trong cơ sở dữ liệu của chúng ta. Ngưỡng 0.6 ở đây được chọn theo mô hình dlib ResNet, tuy nhiên khi áp dụng với một tập dữ liệu mới, chúng ta cần tinh chỉnh lại để đạt được độ chính xác cao nhất.
Một số lưu ý với hàm face_image_to_encoding
1
2
3
4
5
6
7
8
9
10
11
def face_image_to_encoding(rgb_image):
""" Find the face in an image and extract its encoding """
face_locations = face_recognition.face_locations(rgb_image, number_of_times_to_upsample=0, model='hog')
assert len(face_locations) > 0, "Found no face, please use a different image"
encoding = face_recognition.face_encodings(
rgb_image, known_face_locations=face_locations, num_jitters=0, model='small'
)
if len(encoding) > 1:
print('Warning: Found more than 1 face, the first one will be used.')
return encoding[0]
- Dòng 3: xác định vị trí khuôn mặt với hàm
face_locations
, trong đó:number_of_times_to_upsample
là số lần tăng cỡ ảnh trước khi áp dụng mô hình, cỡ ảnh càng lớn thì mô hình chạy càng lâu nên ở đây mình để giá trị là0
model
là mô hình được áp dụng, chúng ta có thể dùnghog
(Hisrogram of Oriented Gradients) hoặccnn
(Convolutional Neural Net - mô hình Max-Margin Object Detection). Dùngcnn
tất nhiên sẽ chính xác hơn, nhưng mình ưu tiên code chạy nhanh cho ví dụ nhỏ này nên chọn mô hìnhhog
.
- Dòng 5: trích xuất đặc trưng ảnh với hàm
face_encodings
, trong đó:num_jitters
là số lần dịch chuyển ảnh ngẫu nhiên trước khi chạy mô hình rồi lấy kết quả trung bình.num_jitters
càng lớn thì chạy càng chậm nên mình chọn giá trị là0
.model
là mô hình được áp dụng, chúng ta có thể dùngsmall
hoặclarge
, ở đây mình chọnsmall
để code chạy cho nhanh.known_face_locations
là vị trí đã biết của khuôn mặt, chúng ta có thể dùngface_detections
đã tìm được ở bước trên. Thực ra nếu chúng dùng giá trịNone
ở đây, hàmface_encodings
vẫn chạyface_locations
nên chúng ta không cần phải có bước riêng ở dòng 3, mình muốn các bạn hiểu rõ hơn một chút nên chia ra 2 bước riêng lẻ như vậy.
Mã nguồn (source code)
Tạm kết
Bài hôm nay giới thiệu đến các bạn về cách xây dựng một phần mềm nhận dạng khuôn mặt dựa trên thư viện face_recognition
với hai mô hình được sử dụng là HOG và dlib ResNet. Mình chọn 2 mô hình này chủ yếu là để chạy cho nhanh chứ thực ra có rất nhiều mô hình khác có thể được sử dụng như Haar Cascade, MTCNN, RetinaNet, …
Còn bạn dùng mô hình nào? Bạn có kinh nghiệm gì thú vị với Face Recognition thì chia sẻ để mọi người cùng tham khảo với nhé.
Leave a comment