Have a sneaky sibling who’s secretly stealing your lunch money? A couple angry Seniors planning to toilet paper your home? Will this is for you!

The motion activated camera is created using only two pieces of hardware: a Raspberry Pi and a USB camera. Raspberry Pi’s are pouch sized mini-computers that are both compact in its design and customizable in its hardware. By plugging a Microsoft camera into one of its four USB ports, it becomes a makeshift GoPro that is capable of checking for movement, taking pictures, and sending those pictures straight into your Google Drive!

Raspberry Pi and Camera

Motivation

I started this project because someone had broken into my school's robotics team's room. The window was left open and the door became unlocked which, of course, is a serious issue. Nothing was stolen but it did give us a scare, and as such I created the motion activated Raspberry Pi as an extra level of security.

Before I start coding the Raspberry Pi, I realize a GoPro would work too. However, a GoPro would be more expensive and cannot send images to a Google Drive. Perhaps most importantly, the Raspberry Pi would be a fun learning experience.

The Code

The first scenario that we are going to tackle is detecting motion and saving those photos into a USB drive.

 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
import cv2
import os
import time

def blur_image(image):
    return cv2.medianBlur(cv2.cvtColor(image, cv2.COLOR_RGB2GRAY), 13)

# noinspection PyArgumentList
cam = cv2.VideoCapture(0)
images = [None, None]
max_frame = 0
source = '../../../media/pi/9AED-ED1D/'
while True:
    images[0] = cam.read()[1]  # takes first picture
    time.sleep(1)  # delay
    images[1] = cam.read()[1]  # takes second picture
    diff = cv2.absdiff(blur_image(images[0]), blur_image(images[1]))
    # cv2.imshow("diff", diff)
    diff_black_white = cv2.threshold(diff, 7, 255, cv2.THRESH_BINARY)[1]
    # cv2.imshow("diff_black_white", diff_black_white)

    print('Pixels Different ' + str(cv2.countNonZero(diff)))
    if (cv2.countNonZero(diff) > 1000):
        cv2.imwrite(source + 'pictures/frame-' + str(max_frame) + '.png', images[1])
        max_frame += 1

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

Strategy

In order to check for motion, the strategy is to take two pictures with a delay between each, slightly blur both in order to account for noise in the images, find pixels that have changed between the first and the second picture, and count the number of changed pixels. The result would be a black and white image in which white pixels correspond to changed pixels and black pixels correspond to pixels that did not change.

Code Analysis

The first four lines import the necessary packages, in this case, cv2 for image processing, os for saving images to a flash drive, and time for temporarily pausing the program. Ignoring the methods for now, line 9 creates the video capture object and the integer, 0, references which camera, in which case is the Logitech camera.

Line 9 creates a variable that is going to keep track of the index of each photo taken which will be necessary for taking multiple pictures.

Line 10 is where we create an array named images that we will eventually populate with the two images. Line 11 denotes the location where we will be storing the pictures. Line 14 takes the first image. cam.read(0) returns an array of two values: the first is a boolean of whether the image was taken successfully and the second value is the image taken. In this case we only want the second one. Next we delay the program and take the second image.

In Line 17, we blur both images by calling the method, blur_image(image), on line 5 and find the difference between the two blurred images through cv2's absdiff(image, image) method. The result looks something like this:

Motion Detection Result

If you are running this code on a computer you can un-comment line 18 to see this. Line 19 takes the image we created on Line 17 and through cv2’s threshold method, turns all pixels to 255 (white) if the pixel difference is greater than 7 and 0 (black) if pixels difference is less. This is to both filter out random noise that blurring might not have completely eliminated as well as highlight the changes between the two pictures.

To see this in action, un-comment line 20. I'm not going to show the result, mainly because the result is kinda creepy.

Line 22 adds up all the white pixels which is the difference between the two images and prints out that number.

Line 23 checks if the number of changed pixels is greater than 1000 (which is a threshold I got through trial and error) and if it is greater than 1000, it will save the image to the folder specified and give it the name, frame-#.png with the # representing the index of the image. Then we increment the index on line 25 in preparation for the next image.

Lines 27-29 specifies what happens if escape is pressed but this is completely optional.

Success!

Congratulations. If you follow this tutorial, you should see the Raspberry Pi taking pictures if it detects motion. No more angry seniors and no more robot-snatching burglars!

Robotics Room