Trong quá trình làm việc với thị giác máy tính, mình luôn bị thu hút bởi những bản demo công nghệ mới được áp dụng trên videos. Có lẽ một phần là do videos sống động hơn những bức ảnh tĩnh. Mặt khác, những ứng dụng cho videos thường rất gần với đời sống hàng ngày như chức lấy nét trên máy ảnh, camera giao thông, xe tự lái …

Bài thực hành hôm nay sẽ hướng dẫn các bạn dùng OpenCV để làm việc với videos nói chung. Sau đó chúng ta sẽ cụ thể hoá bằng việc áp dụng mô hình nhận diện khuôn mặt để xem mô hình này hoạt động ra sao với videos.

Đọc và hiển thị videos bằng OpenCV

Ví dụ về video được tạo ra từ 10 bức ảnh tĩnh chạy liên tục (by David DeFino)

Trong quay dựng phim hay videos có khái niệm về tốc độ khung hình, ví dụ như 24 hay 30 hình trên giây, khái niệm này có nghĩa là số khung hình trên 1 giây của phim / video đó. Mỗi khung hình là 1 ảnh tĩnh, nên về cơ bản, làm việc với videos cũng không khác nhiều với ảnh là mấy, chúng ta chỉ cần xử lý nhiều ảnh hơn mà thôi.

Đoạn code sau là cấu trúc thường gặp khi làm việc với videos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# kết nối với video cần xử lý
video = cv2.VideoCapture('[Đường dẫn đến file video hoặc số tương ứng với webcam]')

while(video.isOpened()):
    # đọc từng khung hình / ảnh
    ret, frame = video.read()
    # xử lý khung hình ở đây
    # ...
    
    # hiển thị khung hình đã được xử lý, có thể bỏ qua nếu không cần hiển thị ảnh
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ngừng kết nối với video và dừng các khung hình được hiển thị
video.release()
cv2.destroyAllWindows()
  • Dòng 2: dùng cv2.VideoCapture để kết nối với video cần xử lý (bước này giống như cho đĩa phim vào đầu đĩa mà chưa ấn nút chạy vậy). Đầu vào có thể là đường dẫn đến file video hoặc số 0, 1, ... để kết nối với webcam mà chúng ta dùng.
  • Dòng 6: đọc từng khung hình từ video.
  • Dòng 26 - 27: logic xử lý ảnh (ví dụ như xoá phông, đổi sang ảnh đen trắng, nhận diện khuôn mặt, …).
  • Dòng 30 - 32: hiển thị khung hình đã được xử lý. cv2.waitKey(1) & 0xFF == ord('q') là hiển thị ảnh liên tục cho đến khi ấn phím q trên bàn phím (chữ q thường không có caplock). Lưu ý là chúng ta có thể chọn ấn phím khác phím q, đây chỉ là phím hay được dùng trong các bài hướng dẫn OpenCV trên mạng, ngày xưa mình học theo và cũng quen nên cứ để vậy.
  • Dòng 35 - 36: ngừng kết nối với video (giống như việc bỏ đĩa phim ra khỏi đầu đĩa).

Thực hành

Áp dụng mô hình Haar Cascade

Với cấu trúc ở trên, chúng ta có thể ghép phần nhận diện khuôn mặt vào bước xử lý khung hình. Bài hôm nay chúng ta sẽ dùng mô hình Haar Cascase ở bước này.

    # chuyển sang ảnh đen trắng
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # áp dụng nhận diện khuôn mặt trong ảnh
    faces = detector.detectMultiScale(gray, scaleFactor=1.03, minNeighbors=5, minSize=(10, 10), maxSize=(50, 50))
    # vẽ đường bao cho từng khuôn mặt
    green_color = (0, 255, 0)
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, pt1=(x, y), pt2=(x + w, y + h), color=green_color, thickness=2)

Cấu trúc code

.
├── face_detection_video.py
├── models
│   └── haarcascade_frontalface_default.xml
└── videos
    └── sample_video.mov
  • File face_detection_video.py là file code chính được sử dụng
  • Thư mục videos chứa các file videos
  • Thư mục models chứa mô hình tìm khuôn mặt người haarcascade_frontalface_default.xml

Để áp dụng nhận diện khuôn mặt trên video chúng ta có thể dùng face_detection_video.py với tham số --video

$ python face_detection_video.py --video videos/sample_video.mov
Ví dụ về nhận diện khuôn mặt trên video (video gốc từ America's Got Talent)

Còn với webcam thì dùng tham số --webcam

$ python face_detection_video.py --webcam [số tương ứng với webcam]

Nếu bạn dùng laptop như mình thì thường sẽ có 1 webcam có sẵn, và số tương ứng với webcam là 0. Còn nếu bạn dùng thêm webcam gắn ngoài thì số tương ứng sẽ là 1, 2, … bạn hãy thử xem số nào tương ứng với webcam nào nhé (nghe hên xui nhỉ).

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
28
29
30
31
32
33
34
35
36
37
38
39
40
import cv2

from argparse import ArgumentParser


# trích xuất đường dẫn video hoặc webcam (tham số khi chạy code)
parser = ArgumentParser(description="Apply Haar Cascade model on videos")
parser.add_argument('--video', dest="video_path", help='Path to video')
parser.add_argument('--webcam', dest="webcam_number", help='Webcam number', type=int)
args = parser.parse_args()
video_path = args.video_path or args.webcam_number
assert video_path is not None, "Please provide either video path (--image) parameter or webcam number (--webcam)"

# load mô hình nhận diện khuôn mặt
model_path = "models/haarcascade_frontalface_default.xml"
detector = cv2.CascadeClassifier(model_path)

# kết nối với video cần được xử lý
video = cv2.VideoCapture(video_path)

while(video.isOpened()):
    # đọc từng khung hình
    ret, frame = video.read()
    frame = cv2.resize(frame, dsize=(360, 240))
    # áp dụng nhận diện khuôn mặt
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = detector.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=7)
    
    face_color = (0, 255, 0)
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, pt1=(x, y), pt2=(x + w, y + h), color=face_color, thickness=2)
    
    # hiển thị từng khung hình đã được xử lý
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ngừng kết nối với video và dừng các khung hình được hiển thị
video.release()
cv2.destroyAllWindows()
  • Dòng 7 - 12: Trích xuất đường dẫn đến video hoặc webcam (ưu tiên video nếu có đường dẫn đến cả 2, và sẽ ngừng chạy nếu không có 1 trong 2).
  • Dòng 24 - 31: Đây chính là phần nhận diện khuôn mặt được áp dụng khi xử lý từng khung hình.
    • Lưu ý là độ phân giải của ảnh được thay đổi thành (360, 240) (dòng 24) với mục đích tăng tốc độ xử lý ở phần sau.
    • Các tham số trong detectMultiScale được chọn sẵn để phù hợp với video mà mình dùng, với video khác thì chúng ta cần tinh chỉnh lại cho phù hợp với video đó.

Thảo luận

Với video ở trên, chúng ta có thể thấy vẫn còn một vài khuôn mặt bị bỏ qua (đặc biệt là mặt người khi quay nghiêng), nhưng mình nghĩ thế là quá ổn cho một mô hình đã ra đời cách đây 20 năm.

Tuy nhiên, cần lưu ý rằng Haar Cacade có một số nhược điểm như:

  • Không nhận diện được mặt nghiêng
  • Không nhận diện được mặt bị che
  • Cần tinh chỉnh tham số cho từng trường hợp cụ thể

Trong các bài tiếp theo, hãy cùng mình tìm hiểu một số mô hình khác để xem những nhược điểm này có được khắc phục không nhé.

Tham khảo

Leave a comment