Motion Detector - Disregard Background Motion/Incorrect ID

387 views Asked by At

I have a script in python which acts as a motion detector. I read a video file using cv2, convert to grayscale, and do simple background subtraction from the current frame to detect motion, which I draw a rectangle over. The video is eventually saved as a new file, where I can finally view it.

This works fine, except sometimes the starting frame (background frame) already has motion in it, or sometimes there are features in the background which move but I don't want to detect (eg if I was detecting people, I wouldn't be interested in a flag blowing in the breeze). So I want to somehow disregard 'stationary' movement (ie motion which does not move vertically/horizontally over the course of the video). However I'm having trouble with my approach. There doesn't seem to be any functions or scripts on the internet to solve this.

One idea I had was to draw a larger rectangle over the original, and then if the original rectangle doesn't leave the outer rectangle (which stays put) over the video, then that motion can be cancelled altogether. I have no idea how to implement this. I have managed to draw a larger rectangle, but it follows the original and doesn't stay in place.

Does anyone have any idea how I might be able to do this? Or any resources they could point me in? Thank you. Below is my code starting from when I draw the rectangles.

for c in cnts:
    # if the contour is too small, ignore it
    if cv2.contourArea(c) < min_area:
        continue
    # compute the bounding box for the contour, draw it on the frame, and update the text
    (x, y, w, h) = cv2.boundingRect(c)
    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
    text = "Occupied" # frame is occupied 
    
    half_w=int(w/2) # get 50% sizing width
    half_h=int(h/2) # get 50% sizing height
    x_surr = int (x - (half_w/2))
    y_surr = int(y - (half_h/2))
    w_surr = (w+half_w)
    h_surr = (h+half_h)
    cv2.rectangle(frame, (x_surr, y_surr), (x_surr+w_surr, y_surr + h_surr), (255, 255, 255), 2)
1

There are 1 answers

0
Tim Huff On

I think this code might help you. Basically it compares the value of each pixel in the current frame to the corresponding value of that pixel in the average of the previous n frames. When no motion is present, it is all black. When there is motion, it will show the color of the moving option. Since it is keeping track average of recent frames. You should be able to filter our slight movements for flags fluttering, etc. You will probably need to play around with some thresholding on the final image to get the result you want.

Stillness: stillness

Motion: movement

import cv2

def main():
    # define the length of the list of the number of recent frames to keep track of
    NUMBER_FRAMES_TO_TRACK = 30

    # start the webcam
    cap = cv2.VideoCapture(1)
    ret, frame = cap.read()
    if ret == False:
        print("No webcam detected.")
        return

    # generate a list of recent frames
    recent_frames = [frame for n in range(NUMBER_FRAMES_TO_TRACK)]

    # start the video loop
    while True:
        ret, frame = cap.read()
        if ret == False:
            break
        
        # update the list of recent frames with the most recent frame
        recent_frames = recent_frames[1:]
        recent_frames.append(frame)

        # calculate the average of all recent frames
        average = recent_frames[0]
        for i in range(len(recent_frames)):
            if i == 0:
                pass
            else:
                alpha = 1.0/(i + 1)
                beta = 1.0 - alpha
                average = cv2.addWeighted(recent_frames[i], alpha, average, beta, 0.0)

        # find the difference between the current frame and the average of recent frames
        difference = cv2.subtract(frame, average)

        # show the results
        cv2.imshow("video", frame)
        cv2.imshow("average", average)
        cv2.imshow("difference", difference)
        key = cv2.waitKey(1)
        
        if key == ord('q'):
            break

    cv2.destroyAllWindows()
    cap.release()

if __name__ == "__main__":
    main()