Giới thiệu InsightFace
InsightFace là một dự án mã nguồn mở mạnh mẽ dành cho các tác vụ liên quan đến nhận diện khuôn mặt (Face Recognition), phát hiện khuôn mặt (Face Detection) và căn chỉnh khuôn mặt (Face Alignment). Dự án này được phát triển dựa trên các mô hình học sâu tối ưu, đặc biệt là ArcFace – một trong những phương pháp tiên tiến nhất trong nhận diện khuôn mặt.
Lý do chọn InsightFace:
- Được đánh giá cao bởi người dùng trên GitHub (hiện là 24k star)
- Độ chính xác cao
- Hỗ trợ phát hiện và căn chỉnh khuôn mặt mạnh mẽ
- Khả năng mở rộng và dễ triển khai
Cài đặt InsightFace:
1 |
pip install insightface |
Giới thiệu Face Embedding trong InsightFace
Face Embedding là một vector số có độ dài cố định (thường là 512-D) được trích xuất từ khuôn mặt. Nó giúp biểu diễn khuôn mặt dưới dạng đặc trưng số hóa, giúp so sánh và nhận diện khuôn mặt một cách hiệu quả. Trong InsightFace, Face Embedding được tạo bởi mô hình ArcFace, một phương pháp tiên tiến giúp tăng độ chính xác bằng cách tối ưu hóa khoảng cách giữa các khuôn mặt khác nhau.
Trích xuất embedding khuôn mặt bằng InsightFace:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import insightface import cv2 import numpy as np #load facial recognition model (ArcFace) model = insightface.app.FaceAnalysis(name="buffalo_l", providers=["CPUExecutionProvider"]) model.prepare(ctx_id=0) #Read image img = cv2.imread("face1.jpg") #Face Detection faces = model.get(img) #If there is a face, get the embedding if faces: face_embedding = faces[0].normed_embedding print(f"Embedding vector (512-D): {face_embedding}") |
So sánh giữa landmark và embedding
Demo nhận diện khuôn mặt bằng InsightFace
Mục tiêu
Demo này thực hiện nhận diện khuôn mặt từ các ảnh trích xuất từ video, sử dụng thư viện InsightFace để phát hiện và so sánh khuôn mặt. Mỗi khuôn mặt được lưu trữ trong dataset theo danh tính dựa trên độ tương đồng của embedding.
Kết quả sau khi chạy demo thu được sẽ là:
- Mỗi người trong dataset có một thư mục riêng (P0001, P0002, …).
- Mỗi thư mục (P0001, P0002, …) chứa từ 20 ảnh mẫu trở lên, các file embedding *.bin tương ứng của ảnh (mục đích của việc lưu của việc lưu ảnh cùng với một file embedding tương ứng của ảnh đó là để khi duyệt qua folder chúng ta không cần trích xuất embedding của ảnh một lần nữa) .
- Trong mỗi thư mục của mỗi người có 1 folder appear/ ghi nhận tất cả lần xuất hiện của khuôn mặt. Mỗi lượt được phát hiện sẽ được lưu vào appear/ Tên file ảnh chứa thời gian phát hiện khuôn mặt + chuỗi 5 ký tự ngẫu nhiên để tránh trùng lặp. Mục đích là để theo dõi thời gian và tuần suất xuất hiện của từng người.
Chuẩn bị
Sử dụng Python 3.6 trở lên trên Windows 10 hoặc Ubuntu 18.04 (OS khác chưa test)
Dataset
Dataset được sử dụng trong bài demo này bao gồm các frame hình ảnh trích xuất từ một tập của chương trình truyền hình thực tế “Ơn Giời Cậu Đây Rồi” – một chương trình hài tình huống nổi tiếng của VTV.
- Môi trường thực tế với nhiều điều kiện khác nhau.
- Chứa nhiều người, giúp kiểm tra khả năng phân biệt khuôn mặt.
- Có thể áp dụng vào các bài toán nhận diện khuôn mặt trong video.
Quy trình xử lý
- Khởi tạo mô hình InsightFace: Sử dụng mô hình buffalo_l để phát hiện và trích xuất embedding khuôn mặt.
- Duyệt qua tất cả ảnh trong thư mục input_folder:
- Bỏ qua file không phải ảnh.
- Đọc ảnh, phát hiện khuôn mặt, trích xuất bounding box và embedding.
- Kiểm tra xem khuôn mặt đã tồn tại trong dataset chưa:
- Duyệt qua tất cả thư mục trong dataset.
- So sánh embedding mới với embedding đã lưu bằng cosine similarity (khoảng cách cosine giữa 2 vector).
- Nếu similarity > 0.6, gán vào thư mục tương ứng, nếu nhỏ hơn thì xem đây là người mới và tạo thư mục
- Lưu ảnh và embedding:
- Nếu số ảnh trong thư mục chưa vượt quá 20, lưu khuôn mặt mới.
- Luôn lưu khuôn mặt vào thư mục appear/ để ghi nhận lần xuất hiện.
Code demo
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
import os import cv2 import numpy as np from insightface.app import FaceAnalysis from datetime import datetime import random import string # Initialize FaceAnalysis model app = FaceAnalysis(name="buffalo_l") app.prepare(ctx_id=0, det_size=(640, 640)) # Paths currentDir = os.getcwd() input_folder = currentDir + "\\frame" dataset_folder = currentDir + "\\dataset" os.makedirs(dataset_folder, exist_ok=True) # Create dataset folder if not exists MAX_IMAGES_PER_FOLDER = 20 # Limit of stored images per person # Function to compute cosine similarity def cosine_similarity(embedding1, embedding2): return np.dot(embedding1, embedding2) / (np.linalg.norm(embedding1) * np.linalg.norm(embedding2)) # Function to check if an embedding already exists in dataset def find_matching_folder(new_embedding, threshold=0.6): """Compare new embedding with all embeddings in dataset folders.""" for folder in sorted(os.listdir(dataset_folder)): folder_path = os.path.join(dataset_folder, folder) if not os.path.isdir(folder_path): continue # Check all embeddings inside the embedding folder emb_folder = os.path.join(folder_path, "embedding") if not os.path.exists(emb_folder): continue for file in os.listdir(emb_folder): if file.endswith(".bin"): emb_path = os.path.join(emb_folder, file) stored_embedding = np.fromfile(emb_path, dtype=np.float32) similarity = cosine_similarity(new_embedding, stored_embedding) if similarity > threshold: return folder_path # Return the matching folder path return None # No match found # Function to get the next available folder name (P000X) def get_next_folder_name(): existing_folders = [f for f in os.listdir(dataset_folder) if f.startswith("P")] if not existing_folders: return "P0001" # First folder # Find the highest folder number and increment existing_folders.sort() last_folder = existing_folders[-1] next_number = int(last_folder[1:]) + 1 return f"P{next_number:04d}" # Function to get the next sequential filename (01, 02, ..., 20) def get_next_sequential_filename(folder): existing_files = [f for f in os.listdir(folder) if f.endswith(".jpg")] existing_numbers = sorted( [int(f.split('.')[0]) for f in existing_files if f.split('.')[0].isdigit()] ) if not existing_numbers: return "01" # First file next_number = existing_numbers[-1] + 1 return f"{next_number:02d}" if next_number <= MAX_IMAGES_PER_FOLDER else None # Function to generate a unique filename (same for appear & embedding) def generate_unique_filename(): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") random_str = ''.join(random.choices(string.ascii_letters + string.digits, k=5)) return f"{timestamp}_{random_str}" # Process each image in the input folder for filename in os.listdir(input_folder): if not filename.lower().endswith((".jpg", ".png", ".jpeg")): continue # Skip non-image files img_path = os.path.join(input_folder, filename) img = cv2.imread(img_path) if img is None: print(f"Error loading image: {filename}") continue # Detect faces using insightface faces = app.get(img) for idx, face in enumerate(faces): bbox = face.bbox.astype(int) # Bounding box coordinates embedding = face.normed_embedding # Extracted embedding # Crop the face based on detected bounding box startX, startY, endX, endY = bbox cropped_face = img[startY:endY, startX:endX] # Skip if face is too small if cropped_face.shape[0] == 0 or cropped_face.shape[1] == 0: continue # Check if the embedding exists in dataset matched_folder = find_matching_folder(embedding) if matched_folder: target_folder = matched_folder # Use existing folder else: # Create a new folder new_folder = get_next_folder_name() target_folder = os.path.join(dataset_folder, new_folder) os.makedirs(target_folder, exist_ok=True) # Create "appear" and "embedding" folders inside the target folder appear_folder = os.path.join(target_folder, "appear") embedding_folder = os.path.join(target_folder, "embedding") os.makedirs(appear_folder, exist_ok=True) os.makedirs(embedding_folder, exist_ok=True) # Check if we can save more images in the main folder save_allowed = len([f for f in os.listdir(target_folder) if f.endswith(".jpg")]) < MAX_IMAGES_PER_FOLDER if save_allowed: # Get next filename (01, 02, ... 20) next_filename = get_next_sequential_filename(target_folder) if next_filename is not None: face_save_path = os.path.join(target_folder, f"{next_filename}.jpg") cv2.imwrite(face_save_path, cropped_face) # Generate a unique filename for appear & embedding (same name for both) unique_filename = generate_unique_filename() # Save embedding in the embedding folder emb_save_path = os.path.join(embedding_folder, f"{unique_filename}.bin") embedding.astype(np.float32).tofile(emb_save_path) # Save cropped face in appear folder appear_save_path = os.path.join(appear_folder, f"{unique_filename}.jpg") cv2.imwrite(appear_save_path, cropped_face) print("Processing completed!") |
Đánh giá kết quả
InsightFace xử lý nhanh và tối ưu, giúp nhận diện khuôn mặt trong thời gian ngắn. Hầu như nhận diện được tất cả khuôn mặt xuất hiện trong ảnh, chỉ bỏ sót những khuôn mặt với điều kiện chiếu sáng quá tối. Việc so sánh khuôn mặt dựa vào embedding trích xuất bằng InsightFace cũng đạt hiệu quả cao đối với những khuôn mặt chính diện hoặc có góc nhìn gần chính diện.
Ứng dụng
- Nhận diện người xuất hiện nhiều lần trong video.
- Xây dựng hệ thống theo dõi danh tính trong chuỗi video.
- Có thể mở rộng để nhận diện trong thời gian thực.