Computer Vision

Motion Detector using Background Subtraction

Xin chào, hôm nay chúng ta sẽ tìm hiểu về Motion Detection.

GIỚI THIỆU

Để thực hiện được Motion Detection chúng ta cần áp dụng các kỹ thuật Background Subtraction. Có một vài kỹ thuật khá đơn giản, nhưng một số khác lại khá phức tạp, tuy nhiên tất cả chúng đều nhằm tách foreground và background của từng frame trong video.

Tại sao chúng ta cần áp dụng Background Subtraction? Tại sao chúng ta cần quan tâm đến foreground và background của một frame?

Background trong video là một vùng hình ảnh tĩnh và không thay đổi trong các khung hình liên tiếp. Do đó, việc mô hình hóa Background sẽ giúp chúng ta theo dõi được những thay đổi của nó, từ đó xác định được các chuyển động trong video.

Trong điều kiện thực tế, ảnh hưởng của các yếu tố môi trường (điều kiện sáng, gió, cây cối,…) sẽ làm thay đổi Background, làm cho các kỹ thuật Background Subtraction không đạt hiệu quả cao. Để khắc phục điều này, chúng ta bắt buộc phải kiểm soát được điều kiện ánh sáng và vị trí gắn camera.

Trong bài viết này chúng ta sẽ lần lượt thực hiện 2 kỹ thuật Background Subtraction:

  1.  Average background model.
  2.  OpenCV Background Subtraction Functions.

AVERAGE BACKGROUND MODEL

Đây là một kỹ thuật Background Subtraction rất đơn giản. Chúng ta sẽ tính trung bình theo trọng số một tập hợp N khung hình (Average Background), các khung hình càng cũ thì sẽ có trọng số càng nhỏ, sự khác biệt giữa Average Background và khung hình hiện tại chính là do sự chuyển động.

Trong kỹ thuật này chúng ta sẽ sử dụng các function sau:

Updates a running average

cv2.accumulateWeighted(currentFrame, background, alpha)

Trong đó:

  • currentFrame – khung hình hiện tại;
  • background – Background model;
  • alpha – trọng số alpha càng lớn thì currentFrame càng có ảnh hưởng nhiều đến Background model, vì:

background = (1-alpha)*background + alpha*currentFrame

Tính giá trị tuyệt đối của hiệu giữa input1 và input2

delta = cv2.absdiff(input1, input2)

Trong đó:

  • input1, input2 – các giá trị được đưa vào, cụ thể là 2 bức ảnh;
  • delta – giá trị tuyệt đối của hiệu giữa input1 và input2.

Example 1: Motion Detector with Average background model

import cv2
import numpy as np

class SingleMotionDetector:
    def __init__(self, accumWeight=0.3):
        self.accumWeight = accumWeight
        self.bg = None

    def update(self, image):
        if self.bg is None:
            self.bg = image.copy().astype("float")
            return

        cv2.accumulateWeighted(image, self.bg, self.accumWeight)

    def detect(self, image, tVal=25):
        delta = cv2.absdiff(self.bg.astype("uint8"), image)
        thresh = cv2.threshold(delta, tVal, 255, cv2.THRESH_BINARY)[1]

        thresh = cv2.erode(thresh, None, iterations=2)
        thresh = cv2.dilate(thresh, None, iterations=2)

        cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cnts = cnts[1]
        (minX, minY) = (np.inf, np.inf)
        (maxX, maxY) = (-np.inf, -np.inf)

        if len(cnts) == 0:
            return None
        for c in cnts:
            (x,y,w,h) = cv2.boundingRect(c)
            (minX, minY) = (min(minX, x), min(minY, y))
            (maxX, maxY) = (max(maxX, x+w), max(maxY, y+h))

        return (thresh, (minX, minY, maxX, maxY))

md = SingleMotionDetector(accumWeight=0.5)
video = cv2.VideoCapture(0)
total = 0
frameShape = None

while True:
    ret, frame = video.read()

    if not ret:
        print("Not captured")
        continue

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (7,7), 0)

    if total > 32:
        motion = md.detect(gray)

        if motion is not None:
            (thresh, (minX, minY, maxX, maxY)) = motion

            masked = cv2.bitwise_and(frame, frame, mask=thresh)
            cv2.imshow("masked", masked)
            cv2.rectangle(frame, (minX, minY), (maxX, maxY), (0, 0, 255), 2)

    md.update(gray)
    total += 1

    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF

    if key == ord("q"):
        break

video.release()
cv2.destroyAllWindows()

Giải thích:
– Ở dòng 14, chúng ta đã tính toán Average Background model với trọng số là accumWeight;
– Ở các dòng 17-21, chúng ta sử dụng function cv2.absdiff() để tìm ra sự khác biệt giữa Background model và khung hình hiện tại, sau đó áp dụng các kỹ thuật xử lý hình ảnh để thu được kết quả tốt nhất;
– Ở dòng 23, chúng ta áp dụng Contours Detection để xác định vị trí của chuyển động;
– Ở dòng 35, sau khi tìm được vị trí của chuyển động, kết quả trả về của method detect() là bức ảnh nhị phân biểu diễn sự khác biệt giữa Background và frame hình hiện tại, cũng như là vị trí của chuyển động, nếu có.

Kết quả:

averagebackground

Figure 1 – Average Background Model


OPENCV BACKGROUND SUBTRACTION FUNCTIONS

OpenCV có nhiều class hỗ trợ Background Subtraction, như: createBackgroundSubtractorMOG(), createBackgroundSubtractorMOG2(), createBackgroundSubtractorGMG(), createBackgroundSubtractorKNN().

Trong phần này chúng ta sẽ tìm hiểu về createBackgroundSubtractorMOG2().

Usage:

– Tạo một Background Subtractor:

MOG2BackgroundSubtractor = cv2.createBackgroundSubtractorMOG2(history=500, varThreshold=16, detectShadows=True)

Trong đó:

  • history – số frame ảnh được dùng để tạo Background Model, mặc định là 500;
  • detectShadows – cho phép xác định “shadow” của chuyển động và được biểu diễn bởi màu xám, tuy nhiên sẽ làm giảm tốc độ xử lý, mặc định là True.
  • MOG2BackgroundSubtractor – đối tượng Background Subtractor được tạo.

– Áp dụng Background Subtractor:

fgMask = MOG2BackgroundSubtractor.apply(frame)

Trong đó:

  • frame – frame ảnh hiện tại;
  • fgMask – kết quả trả về của method apply, là một bức ảnh nhị phân biểu diễn foreground của frame ảnh.

Example 2: MOG2 Background Subtractor

import cv2
import numpy as np
import time

video = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorMOG2(history=100, detectShadows=False)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))

while True:
    ret, frame = video.read()
    if not ret:
        continue

    mask = fgbg.apply(frame)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)

    cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]

    (minX, minY) = (np.inf, np.inf)
    (maxX, maxY) = (-np.inf, -np.inf)

    if len(cnts) > 0:
        cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:3]
        for c in cnts:
            if cv2.contourArea(c) > 8000:
                (x,y,w,h) = cv2.boundingRect(c)
                (minX, minY) = (min(minX, x), min(minY, y))
                (maxX, maxY) = (max(maxX, x+w), max(maxY, y+h))

                cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
                cv2.circle(frame, (x+w/2, y+h/2), 10, (0,255,0),-1)

    cv2.imshow("mask", mask)
    cv2.imshow("frame", frame)
    if cv2.waitKey(1) & 0xFF == 27:
        break

video.release()
cv2.destroyAllWindows()

Giải thích:
– Ở dòng 6, chúng ta khởi tạo một MOG2 Background Subtractor với history=100 và detectShadows=False;
– Ở dòng 14, chúng ta gọi method apply() để thực hiện tách Background và Foreground của frame ảnh;
– Ở dòng 17, chúng ta áp dụng Contours Detection để xác định vị trí của chuyển động.

Kết quả:

mog2

Figure 2 – MOG2 Background Subtraction


KẾT LUẬN

Như vậy chúng ta đã tìm hiểu qua 2 kỹ thuật Background Subtraction. Sau khi thực hiện cho sánh thì mình thấy rằng, Average background Model cho frame rate cao hơn so với khi áp dụng createBackgroundSubtractorMOG2 class, tuy nhiên việc sử dụng createBackgroundSubtractorMOG2 class lại đơn giản hơn nhiều (vì thuật toán Gaussian Mixture-based Background/Foreground Segmentation đã được đóng gói vào class và các method).

Cảm ơn các bạn đã theo dõi bài viết.

Thân ái và quyết thắng.

Reference:
[1] Motion Analysis and Object Tracking.
[2] OpenCV – Background Subtraction.

One thought on “Motion Detector using Background Subtraction

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