Computer Vision

[Face Detection] Face Detection with Haar Cascades

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ố.

Hôm nay chúng ta sẽ tìm hiểu cách áp dụng Face Detection với OpenCV 3.


VIOLA-JOHN ALGORITHM

Ở phần này chúng ta sẽ tìm hiểu khái quát về Viola-John algorithm, một giải thuật được sử dụng trong Object Detection.

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, Figure 1.

face_detection_sliding_window
Figure 1 – Sliding window

Tại mỗi “window”, các Haar-like features (Figure 2) có trong vùng hình ảnh đó sẽ được tính toán bằng cách áp dụng Summed Area Tables và xác định xem nó có chứa khuôn mặt nào không. Để làm được điều này chúng ta cần một bộ classifier được train bởi rất nhiều positive image (ảnh có khuôn mặt) và negative image (ảnh không có khuôn mặt). OpenCV đã cung cấp cho chúng ta một số file Haarcascade được train sẵn.

face_detection_haar_features
Figure 2 – Haar-like featues

Từ 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) nhưng đa số chúng sẽ không mang đặc trưng của khuôn mặt nên AdaBoost algorithm sẽ được sử dụng để chọn ra các Haar-like features phù hợp nhất (chọn ra khoảng 6000 features phù hợp nhất từ 180000 features ban đầu).

Việc so sánh 6000 features đối với mỗi “window” cũng yêu cầu tài nguyên tính toán rất lớn, vì vậy Viola-John đã đưa ra khái niệm Cascade of Classifiers. Thay vì so sánh 6000 features ngay lập tức, chúng được chia vào các nhóm và so sánh theo 38 stages. Chỉ “window” nào vượt qua tất cả 38 stages thì mới được công nhận là có chứa khuôn mặt. Tất cả các “window” bị fail ở một stage nào đó đều bị loại bỏ.

Nhờ áp dụng Summed Area Tables, AdaBoost algorithm và Cascade of Classifiers 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:

  • Thời gian training cho cascade rất lớn;
  • Tỉ lệ False-positive và False-negative cao do khó tìm được các tham số tối ưu;
  • Cần tinh chỉnh các tham số cho phù hợp với từng bức ảnh để đạt được kết quả tốt nhất.

FACE DETECTION IN OPENCV

Cùng tìm hiểu một số function của OpenCV 3 sẽ được sử dụng trong Face Detection nào.

  • CascadeClassifier = cv2.CascadeClassifier(cascadeFile) – Load a classifier from a file.

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.

  • detectedObjects = CascadeClassifier.detectMultiScale(image, scaleFactor,  minNeighbors, flags, minSize) – Phát hiện các đối tượng (được qui định trong Classifier) với các kích thước khác nhau có trong bức ảnh. Các đối tượng được phát hiện sẽ được trả về ở dạng “a list of rectangles“.

Trong đó:

image – input image.
scaleFactor – được sử dụng để tạo “scale pyramid” cho phép phát hiện đối tượng ở multiple scales.
minNeighbors – số lượng overlap window ít nhất trên một vùng hình ảnh để vùng đó được công nhận là có chứa khuôn mặt, thường có giá trị trong khoảng [3, 4, 5, 6]. Tham số này giúp giảm “false detection”.
+ minSize – kích thước nhỏ nhất của đối tượng có thể được phát hiện, thường là (30, 30). Các đối tượng có kích thước nhỏ hơn minSize sẽ bị bỏ qua.


FACE DETECTION IN IMAGES

Example 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:
$ python faceDetection.py -i images -f ..\haarcascades\haarcascade_frontalface_default.xml

Kết quả:

Figure 3 – Footballists

Figure 4 – Arsenal FC

Figure 5 – Cesc Fabregas

Với các tham số không đổi, ở Figure 3, cả 5 khuôn mặt đều được phát hiện, tuy nhiên ở Figure 4, chúng ta lại phát hiện sót khuôn mặt của anh da đen, còn ở Figure 5 có đến 2 khuôn mặt được phát hiện!!!.

Thử thay đổi scaleFactor = 1.05 xem nào:

Figure 6 – Footballists

Figure 7 – Arsenal FC

Figure 8 – 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:

Figure 9 – Footballists

Figure 10 – Arsenal FC

Figure 11 – 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à:

  1. Điểm yếu đầu tiên, để có được kết quả tốt nhất chúng ta buộc phải điều chỉnh các tham số như scaleFactor, minNeighbors cho phù hợp với riêng từng bức ảnh. Điều này là bất khả thi nếu chúng ta cần phải xử lý hàng ngàn tấm ảnh.
  2. Điểm yếu tiếp theo chính là “False positive“, có nghĩa là Haarcascade phát hiện ra các khuôn mặt trong bức ảnh mặc dù thực tế là chẳng có khuôn mặt nào ở vị trí đó. Figure 6 là một ví dụ. Tất nhiên các khuyết điểm này có thể được loại bỏ bằng cách “điều chỉnh các tham số như scaleFactor, minNeighbors cho phù hợp với riêng từng bức ảnh“.

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:
$ python faceDetection.py -f ..\haarcascades\haarcascade_frontalface_default.xml

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 đó:

  • image – hình ảnh cần chèn ký tự.
  • text – nội dung cần chèn vào bức ảnh.
  • (x,y) – tọa độ đoạn text.
  • font – thường sử dụng font cv2.FONT_HERSHEY_SIMPLEX.
  • color – màu chữ ở hệ (B,G,R).
  • thickness – độ dày của nét chữ.
  • lineType – thường dùng cv2.LINE_AA (đối với OpenCV3), cv2.CV_AA (đối với OpenCV 2.4)

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:
[1] StackOverFlow – Recommended values for OpenCV detectMultiScale() parameters.
[2] Increasing webcam FPS with Python and OpenCV.
[3] Drawing functions OpenCV 3.
[4] Face Detection using Haar Cascades

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 )

Google+ photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s