Skip to content

Instantly share code, notes, and snippets.

@amir-saniyan
Last active December 10, 2022 09:49
Show Gist options
  • Save amir-saniyan/cfd5dfa08d4f910d657fb271645895c0 to your computer and use it in GitHub Desktop.
Save amir-saniyan/cfd5dfa08d4f910d657fb271645895c0 to your computer and use it in GitHub Desktop.
Python Motion Detector

Python Motion Detector

Motion Detector Using BackgroundSubtractorKNN

import cv2

_IMAGE_SIZE = (512, 512)

_BACKGROUND_SUBTRACTOR_HISTORY = 100
_BACKGROUND_SUBTRACTOR_DISTANCE_THRESHOLD = 400
_BACKGROUND_DETECT_SHADOWS = True

_BLUR_KERNEL_SIZE = (5, 5)

_BINARY_THRESHOLD = 5

_ERODE_KERNEL_SIZE = (3, 3)
_ERODE_ITERATIONS = 1

_DILATE_KERNEL_SIZE = (5, 5)
_DILATE_ITERATIONS = 1


class MotionDetector:

    def __init__(self, motion_threshold=0.001):
        self.motion_threshold = motion_threshold

        self._background_subtractor = cv2.createBackgroundSubtractorKNN(
            history=_BACKGROUND_SUBTRACTOR_HISTORY,
            dist2Threshold=_BACKGROUND_SUBTRACTOR_DISTANCE_THRESHOLD,
            detectShadows=_BACKGROUND_DETECT_SHADOWS)

    def detect(self, image):
        image = cv2.resize(src=image, dsize=_IMAGE_SIZE)

        image = cv2.GaussianBlur(src=image, ksize=_BLUR_KERNEL_SIZE, sigmaX=0)

        foreground = self._background_subtractor.apply(image=image)

        _, foreground = cv2.threshold(src=foreground, thresh=_BINARY_THRESHOLD, maxval=255, type=cv2.THRESH_BINARY)

        kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=_ERODE_KERNEL_SIZE)
        foreground = cv2.erode(src=foreground, kernel=kernel, iterations=_ERODE_ITERATIONS)

        kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=_DILATE_KERNEL_SIZE)
        foreground = cv2.dilate(src=foreground, kernel=kernel, iterations=_DILATE_ITERATIONS)

        contours, _ = cv2.findContours(foreground, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        max_area = 0
        for contour in contours:
            area = cv2.contourArea(contour)
            if area > max_area:
                max_area = area

        image_area = _IMAGE_SIZE[0] * _IMAGE_SIZE[1]

        motion_ratio = max_area / image_area

        if motion_ratio >= self.motion_threshold:
            return True
        else:
            return False

Motion Detector Using absdiff

import cv2

_IMAGE_SIZE = (512, 512)

_BLUR_KERNEL_SIZE = (21, 21)

_BINARY_THRESHOLD = 5

_MORPHOLOGY_KERNEL_SIZE = (5, 5)
_MORPHOLOGY_ITERATIONS = 1


class MotionDetector:

    def __init__(self, motion_threshold=0.001):
        self.motion_threshold = motion_threshold

        self._previous_image = None

    def detect(self, image):
        image = cv2.resize(src=image, dsize=_IMAGE_SIZE)

        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        image = cv2.GaussianBlur(src=image, ksize=_BLUR_KERNEL_SIZE, sigmaX=0)

        if self._previous_image is None:
            self._previous_image = image
            return False

        foreground = cv2.absdiff(self._previous_image, image)

        _, foreground = cv2.threshold(src=foreground, thresh=_BINARY_THRESHOLD, maxval=255, type=cv2.THRESH_BINARY)

        kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=_MORPHOLOGY_KERNEL_SIZE)
        foreground = cv2.morphologyEx(src=foreground, op=cv2.MORPH_DILATE, kernel=kernel,
                                      iterations=_MORPHOLOGY_ITERATIONS)

        contours, _ = cv2.findContours(foreground, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        max_area = 0
        for contour in contours:
            area = cv2.contourArea(contour)
            if area > max_area:
                max_area = area

        image_area = _IMAGE_SIZE[0] * _IMAGE_SIZE[1]

        motion_ratio = max_area / image_area

        self._previous_image = image

        if motion_ratio >= self.motion_threshold:
            return True
        else:
            return False

Usage

import cv2

from motion_detector import MotionDetector

motion_detector = MotionDetector(motion_threshold=0.001)

video_capture = cv2.VideoCapture('test.mp4')

while True:
    success, frame = video_capture.read()

    if not success:
        break

    motion_detected = motion_detector.detect(frame)
    
    print('Motion:', motion_detected)

    cv2.imshow('Video', frame)

    if cv2.waitKey(1) == 27:
        break

video_capture.release()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment