Haarcascade_frontalface_default.xml là gì
Face Detection (Phát hiện khuôn mặt) là một ứng dụng của Computer Vision được sử dụng trong nhiều lĩnh vực, với chức năng phát hiện khuôn mặt con người trong một bức ảnh kỹ thuật số. Show Mặc dù Deep Learning ngày càng có nhiều đột phá (thực thế thì OpenCV 3 đã cung cấp bộ Face Detection dựa trên Deep Learning), nhưng Cascade Classifier vẫn là một thuật toán được sử dụng phổ biến cho bài toán Object Detection vì tốc độ cao và độ chính xác khá ổn. Bài viết này sẽ giới thiệu về Cascade Classifier với Haar-like features được cung cấp sẵn bởi OpenCV. VIOLA-JOHN DETECTORỞ phần này chúng ta sẽ tìm hiểu khái quát về Viola-John detector, giải thuật được sử dụng trong Cascade Classifier. Ban đầu giải thuật này chỉ hỗ trợ Haar-like features (Haar Cascade Classifier), nhưng kể từ OpenCV 3.x thì đã hỗ trợ thêm Local Binary Pattern Histogram (LBP Cascade Classifier). Với bài toán Face Detection, mỗi bức ảnh được đưa vào detector sẽ được “scan” bởi một “sliding window” có kích thước cố định ở nhiều tỉ lệ của bức ảnh, hình 1. Hình 1 – Sliding windowTại mỗi “window”, các Haar-like features (hình 2) có trong vùng hình ảnh đó sẽ được tính toán bằng cách áp dụng Summed Area Tables (còn gọi là Integral Image). Hình 2 – Haar-like featuesTừ mỗi “window” có thể tính toán ra rất nhiều Haar-like features (khoảng 180000 features cho “window” có kích thước 24×24). Để có thể xử lý hết số lượng feature khổng lồ này, Viola-John detector sử dụng một phương pháp gọi là Rejection Cascade, hình 3. Hình 3 – Rejection Cascade được sử dụng trong Viola-John detector.Bản chất mỗi node (F1, F2,…, Fn) trong Rejection Cascade là một AdaBoosted Decision Tree classifier được train từ một dataset rất lớn gồm hàng (chục) nghìn bức ảnh chứa và không chứa khuôn mặt (OpenCV đã cung cấp sẵn một số file cascade classifier để phát hiện khuôn mặt nên chúng ta không cần phải tự train nữa!). Thông thường, mỗi node sẽ dựa trên 1 feature (và tối đa không quá 3 features) để đưa ra quyết định xem vùng “window” đó có thể chứa khuôn mặt hay không. Nếu có, nó sẽ được đưa tới node tiếp theo để xử lý; ngược lại thì quá trình xử lý kết thúc. Chỉ những “window” nào vượt qua tất cả n nodes thì mới được công nhận là chứa khuôn mặt. Viola-John detector có tốc độ xử lý rất nhanh, vì đa số “window” không chứa khuôn mặt sẽ bị loại bỏ ngay từ một vài node đầu tiên của Rejection Cascade. Nhờ áp dụng Summed Area Tables, AdaBoost algorithm và Rejection Cascade nên Haar cascade tính toán rất nhanh và có thể phát hiện khuôn mặt ở nhiều vị trí và tỉ lệ, cũng như rất phù hợp cho các ứng dụng real-time. Tuy nhiên Haar cascade có những điểm yếu sau:
FACE DETECTION IN OPENCVCùng tìm hiểu một số function của OpenCV 3 sẽ được sử dụng trong Face Detection nào.
Trong đó: + cascadeFile – Cascade file, thường nằm tại folder ..\opencv\build\share\OpenCV. Trong bài viết này chúng ta sẽ sử dụng Haarcascades.
Trong đó: + image – input image. FACE DETECTION IN IMAGESExample 1 – faceDetection.py import argparse import cv2 class FaceDetector: def __init__(self, faceCascadePath): self.faceCascade = cv2.CascadeClassifier(faceCascadePath) def detect(self, image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)): rects = self.faceCascade.detectMultiScale(image, scaleFactor=scaleFactor, minNeighbors=minNeighbors, minSize=minSize, flags=cv2.CASCADE_SCALE_IMAGE) return rects ap = argparse.ArgumentParser() ap.add_argument("-f", "--face") ap.add_argument("-i", "--image") args = vars(ap.parse_args()) image = cv2.imread(args["image"]) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) fd = FaceDetector(args["face"]) faceRects = fd.detect(gray, scaleFactor=1.1, minNeighbors=6, minSize=(30, 30)) print("I found {} face(s)".format(len(faceRects))) for (x,y,w,h) in faceRects: cv2.rectangle(image, (x,y), (x+w, y+h), (0,255,0), 2) cv2.imshow("Faces", image) cv2.waitKey(0) Execution: Kết quả: Hình 4 – Footballists Hình 5 – Arsenal FC Hình 6 – Cesc Fabregas Với các tham số không đổi, ở Figure 4, cả 5 khuôn mặt đều được phát hiện, tuy nhiên ở Figure 5, chúng ta lại phát hiện sót khuôn mặt của anh da đen, còn ở Figure 6 có đến 2 khuôn mặt được phát hiện!!!. Thử thay đổi scaleFactor = 1.05 xem nào: Hình 7 – Footballists Hình 8 – Arsenal FC Hình 9 – Cesc Fabregas Cuối cùng thì khuôn mặt của anh da đen cũng đã được phát hiện. Tuy nhiên trong bức ảnh của Cesc Fabregas lại phát hiện đến 3 khuôn mặt!!! Chúng ta sẽ thay scaleFactor = 1.15 xem kết quả thay đổi thế nào: Hình 10 – Footballists Hình 11 – Arsenal FC Hình 12 – Cesc Fabregas Lần này thì chúng ta đã phát hiện đúng khuôn mặt trong bức ảnh của Cesc Fabregas, tuy nhiên bức ảnh của Arsenal FC đã bị phát hiện thiếu vài khuôn mặt. Qua các ví dụ trên có thể thấy rằng Haarcascade mặc dù khá nhanh nhưng có 2 điểm yếu nổi bật là:
Mình cũng rút ra một kinh nghiệm, nếu phát hiện thiếu các khuôn mặt thì hãy giảm giá trị scaleFactor, nếu gặp “False Positive” thì tăng giá trị scaleFactor. Giá trị minNeighbors tốt nhất là 5 hoặc 6. Tất nhiên không có gì là hoàn hảo, hãy cứ vui với kết quả chúng ta vừa nhận được nhé! Mọi thứ chỉ mới bắt đầu. FACE DETECTION IN VIDEOĐể phát hiện khuôn mặt trên video (từ webcam), chúng ta sẽ xử lý từng frame của video đó, tương tự như đối với từng bức ảnh riêng biệt ở ví dụ trước. Trong bài viết này mình sử dụng Webcam Logitech C270. Example 2 – webcamFaceDetection.py import argparse import cv2 from threading import Thread class FaceDetector: def __init__(self, faceCascadePath): self.faceCascade = cv2.CascadeClassifier(faceCascadePath) def detect(self, image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)): rects = self.faceCascade.detectMultiScale(image, scaleFactor=scaleFactor, minNeighbors=minNeighbors, minSize=minSize, flags=cv2.CASCADE_SCALE_IMAGE) return rects class WebcamVideoCapture: def __init__(self, src=0): # initialize the video camera stream and read the first frame # from the stream self.stream = cv2.VideoCapture(src) (self.grabbed, self.frame) = self.stream.read() # initialize the variable used to indicate if the thread should # be stopped self.stopped = False def start(self): # start the thread to read frames from the video stream Thread(target=self.update, args=()).start() return self def update(self): # keep looping infinitely until the thread is stopped while True: # if the thread indicator variable is set, stop the thread if self.stopped: self.stream.release() return # otherwise, read the next frame from the stream (self.grabbed, self.frame) = self.stream.read() def read(self): # return the frame most recently read return self.grabbed, self.frame def stop(self): # indicate that the thread should be stopped self.stopped = True ap = argparse.ArgumentParser() ap.add_argument("-f", "--face") args = vars(ap.parse_args()) video = WebcamVideoCapture(src=0).start() fd = FaceDetector(args["face"]) while True: grabbed, frame = video.read() if not grabbed: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faceRects = fd.detect(gray, scaleFactor=1.1, minNeighbors=6, minSize=(30, 30)) count = 0 for (x,y,w,h) in faceRects: count += 1 cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 2) cv2.putText(frame, "Face [{}]".format(count), (x,y-15), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2, cv2.LINE_AA) cv2.imshow("Faces", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break video.stop() cv2.destroyAllWindows() Execution: Như các bạn thấy, các function xử lý hình ảnh đều tương tự ví dụ 1, chỉ khác ở chỗ chúng ta sẽ xử lý liên tiếp các frame nhận được từ webcam. Ngoài ra ở ví dụ này mình còn sử dụng class WebcamVideoCapture để capture hình ảnh từ webcam với một thread riêng biệt nhằm hạn chế độ trễ. Tại dòng 66 mình có sử dụng function cv2.putText() để thêm các ký tự lên bức ảnh: cv2.putText(image, text, (x,y), font, size, color, thickness, lineType) Trong đó:
Như vậy chúng ta đã làm quen với chức năng phát hiện khuôn mặt trên OpenCV, hãy tiếp tục khám phá nhé. Cảm ơn các bạn đã theo dõi bài viết. Thân ái và quyết thắng. Reference: |