Frame Subtraction để phát hiện chuyển động trong video
OpenCV - tut 16: Frame Subtraction
Môi trường "hành sự"
- Linux (bài viết sử dụng Ubuntu 16.04)
- OpenCV (bài viết sử dụng OpenCV 4.0.0)
- Python (bài viết sử dụng Python 3.5.5)
- FFMPEG (bài viết sử dụng FFMPEG 4.0)
- Video mẫu để xử lý: bài viết không cung cấp sẵn!
Frame Subtraction
Ý tưởng của Frame Subtraction (tạm dịch: trừ khung hình) rất đơn giản, đó là ta trừ hai khung hình với nhau sau đó chuẩn hóa về miền giá trị mong muốn. Ví dụ: Nếu muốn trực quan hóa sự khác biệt giữa hai ảnh thì ta có thể lấy giá trị tuyệt đối. Hoặc cũng có thể áp dụng các kiểu chuẩn hóa khác tùy mục đích bạn muốn xử lý là gì.
Khi áp dụng Frame Subtraction cho video thì ta sẽ lấy 2 khung hình liền kề (pair) trừ cho nhau để tìm điểm khác biệt. Nói về bản chất của video thì có thể xem nó là một chồng ảnh, bởi video chính là việc trình chiếu các khung ảnh theo thời gian. Do đó, việc áp dụng xử lý ảnh trên video sẽ là việc ta áp dụng xử lý ảnh trên từng khung hình (frame) bằng cách lần lượt lặp cho đến hết tất cả khung hình có trong video.
Các thông tin trên cho ta biết cách thao tác với video ở mức luận lý. Thực chất một file video thực thụ (ví dụ: đuôi .mp4) nó sẽ cần phải nén lại nữa. Bởi nếu ta lưu thô hoàn toàn thì dung lượng của video sẽ rất lớn. Giải thuật nén ảnh / nén video là cả một chủ đề trong Xử lý ảnh đó nha chứ không có đơn giản đâu.
Trong bài viết này, sau khi trừ hai ảnh Minh sẽ "visualize" lên thành hình ảnh do đó mình sẽ chuẩn hóa bằng cách lấy giá trị tuyệt đối cho đơn giản. Khi sử dụng OpenCV chỉ cần đúng một hàm sau: cv2.absdiff
(https://docs.opencv.org/4.0.0/d2/de8/group__core__array.html#ga6fef31bc8c4071cbc114a758a2b79c14)
Hiện thực giải thuật Background Subtraction trên ảnh màu
Tiết mục chém gió đến rồi :">. Có thể bỏ qua các dòng này để đến ngay với source code bên dưới cũng được (hehe). Đại loại lần này mình sẽ cung cấp các điểm mới sau, đó là:
- Đọc video trong OpenCV: điểm mấu chốt chính là
cv2.VideoCapture
và lặp trêncap.read()
. Vòng lặp các frame ảnh kết thúc khi hết video hoặc bị lỗi. Khá là dễ hiểu, mà nếu có không hiểu thì thôi, trong mỗi vòng lặp ta sẽ có khung hình để thực hiện xử lý. - Để lưu frame liền trước thì ta chỉ việc cho nó vào biến
last_gray
để lưu trữ. Đương nhiên là ở frame đầu tiên ta làm quái gì có frame liền trước để mà xử lý :)). Có thể bỏ qua frame đầu nè cho nó đơn giản. - Hàm tính sự khác biệt trong OpenCV: trừ điểm với điểm giữa 2 ảnh, sau đó lấy trị tuyệt đối. Tài liệu tham khảo
cv2.absdiff
có đính kèm cả link ở phần trước, tha hồ đọc ngập mặt :v. - Cuối cùng là hàm
print_image
để in áo cặp, ý lộn, cặp ảnh cạnh nhau. Để khi ta ghép đống hình này lại sẽ ra video kết quả sinh đờ ộng (sinh động) :))). Cách làm tạo ảnh đen thui kích thước bề ngang gấp đôi ảnh gốc, sau đó lấy ảnh gốc dán bên trái, lấy ảnh khác biệt dán bên phải. Do ảnhdiff
chỉ có một kênh, nên mình phải dán 3 lần trên 3 kênh màu để nó hiển thị thành màu xám! - Lưu ý BỰ: sửa biến
INPUT_VIDEO
thành đường dẫn (tương đối hay tuyệt đối) đến video ếch, í lộn, video mấy ông muốn xử lý nhá. CònOUTPUT_IMG
là thư mục sẽ tự động được tạo mới nếu nó chưa tồn tại và ảnh kết quả sẽ được in vèo vèo vô đây. - Bước ghép video: Tham khảo bài viết này và dùng lệnh này nha, hiểu hôn:
ffmpeg -framerate 24 -f image2 -start_number 1 -i out_my_video/img_%*.jpg -crf 10 -q:v 5 -pix_fmt yuv420p out_video.mp4
frame_sub.py
import os
import cv2
import numpy as np
INPUT_VIDEO = 'my_video.mp4'
OUTPUT_IMG = 'out_my_video'
os.makedirs(OUTPUT_IMG, exist_ok=True)
def print_image(img, frame_diff):
"""
Place images side-by-side
"""
new_img = np.zeros([img.shape[0], img.shape[1]*2, img.shape[2]]) # [height, width*2, channel]
new_img[:, :img.shape[1], :] = img # place color image on the left side
new_img[:, img.shape[1]:, 0] = frame_diff # place gray image on the right side
new_img[:, img.shape[1]:, 1] = frame_diff
new_img[:, img.shape[1]:, 2] = frame_diff
return new_img
def main(video_path):
cap = cv2.VideoCapture(video_path) # https://docs.opencv.org/4.0.0/d8/dfe/classcv_1_1VideoCapture.html
last_gray = None
idx = -1
while(True):
ret, frame = cap.read() # read frames
idx += 1
if not ret:
print('Stopped reading the video (%s)' % video_path)
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # convert color image to gray
if last_gray is None:
last_gray = gray
continue
diff = cv2.absdiff(gray, last_gray) # frame difference! https://docs.opencv.org/4.0.0/d2/de8/group__core__array.html#ga6fef31bc8c4071cbc114a758a2b79c14
cv2.imwrite(os.path.join(OUTPUT_IMG, 'img_%06d.jpg' % idx), print_image(frame, diff))
last_gray = gray
print('Done image @ %d...' % idx)
pass
pass
if __name__ == "__main__":
print('Running frame difference algorithm on %s' % INPUT_VIDEO)
main(video_path=INPUT_VIDEO)
print('* Follow me @ ' + "\x1b[1;%dm" % (34) + ' https://www.facebook.com/minhng.info/' + "\x1b[0m")
print('* Join GVGroup for discussion @ ' + "\x1b[1;%dm" % (34) + 'https://www.facebook.com/groups/ip.gvgroup/' + "\x1b[0m")
print('* Thank you ^^~')
print('[NOTE] Run the following command to turn you images in to video:')
print('ffmpeg -framerate 24 -f image2 -start_number 1 -i out_my_video/img_%*.jpg -crf 10 -q:v 5 -pix_fmt yuv420p out_video.mp4')
Video kết quả:
Cảm ơn bạn đã theo dõi bài viết. Hãy kết nối với tớ nhé!
- Minh: https://www.facebook.com/minhng.info
- Khám phá xử lý ảnh - GVGroup: https://www.facebook.com/groups/ip.gvgroup
Khám phá xử lý ảnh - GVGroup
Danh sách bài viết series OpenCV:
- Hashtag #OpenCV
- Tut 1: Xử lý ảnh - OpenCV đọc ghi hình ảnh (code Python và C++)
- Tut 1.1: Xử lý ảnh - Cấu trúc dữ liệu ảnh trong OpenCV. Pixel là gì?
- Tut 1.2: Xử lý ảnh - Chuyển đổi ảnh OpenCV sang Pillow và ngược lại
- Tut 2: Xử lý ảnh - OpenCV resize, crop và padding hình ảnh (code Python và C++)
- Tut 3: Xử lý ảnh - OpenCV biến đổi mức sáng hình ảnh (code Python)
- Tut 4: Xử lý ảnh - OpenCV vùng quan tâm (ROI) là gì? (code Python)
- Tut 4.1: Xử lý ảnh - OpenCV: vẽ văn bản, đường thẳng, mũi tên, hình chữ nhật, hình tròn, ellipse, đa giác
- Tut 4.2: Xử lý ảnh - Pha trộn ảnh trong OpenCV (blending)
- Tut 5: Xử lý ảnh - OpenCV ảnh nhị phân
- Tut 6: Xử lý ảnh - OpenCV cân bằng sáng (histogram equalization)
- Tut 7: Xử lý ảnh - OpenCV kỹ thuật cửa sổ trượt (sliding window)
- Tut 8: Xử lý ảnh - Convolution là gì?
- Tut 9: Xử lý ảnh - Làm mờ ảnh (blur)
- Tut 10: Xử lý ảnh - Gradient của ảnh là gì?
- Tut 11: Xử lý ảnh - Phát hiện cạnh Canny (Canny Edge Detection)
- Tut 12: Xử lý ảnh - Phát hiện đường thẳng bằng Hough Transform (Hough Line)
- Tut 13: Xử lý ảnh - Hiện thực phát hiện đoạn thẳng dùng Hough Transform (Hough Line)
- Tut 14: Xử lý ảnh - Giải thuật phân vùng Region Growing trên ảnh màu
- Tut 15: Xử lý ảnh - Giải thuật Background Subtraction trên ảnh màu
- Tut 16: Xử lý ảnh - Frame Subtraction để phát hiện chuyển động trong video
- Tut 17: Xử lý ảnh - HOG - Histograms of Oriented Gradients
- Tut 18: Xử lý ảnh - HOG - Huấn luyện mô hình phân loại người
- Tut 19: Xử lý ảnh - HOG - Phát hiện người
- Tut 20: Xử lý ảnh - Tổng hợp kinh nghiệm xử lý ảnh (End)
- Tut 21: Xử lý ảnh - Hiện thực trích đặc trưng Local Binary Patterns (LBP)
- Tut 22: Xử lý ảnh - Trích đặc trưng Gabor filters