Motion-activated camera with Raspberry Pi

This notebook does motion-activated image capture, using a simple motion sensor and OpenCV for catpure.

In [29]:
from IPython.display import display, Image

The Raspberry Pi is hooked up to the breadboard via an Adafruit Pi T-Cobbler, with the motion sensor at 11 volts by chaining 4 AA batteries with the +5V pin. A green LED and GPIO pin 0 are connected to the motion sensor's alarm pin, and a small speaker is connected to GPIO pin 1.

In [1]:
import time
import cv2
import cv
import numpy as np
import wiringpi2

Some image formatters, so our image data are displayed automatically (significantly more than is actually necessary).

In [2]:
PNG formatter(s) for PIL and OpenCV Image objects

Adapted from pil_display extension:

Now when displayhook gets an image, it will be drawn in the browser.

from io import BytesIO
from PIL import Image
import os
import tempfile

import cv2

def pil2imgdata(img, format='PNG'):
    fp = BytesIO(), format=format)
    return fp.getvalue()

def array2imgdata_pil(cvimg, format='PNG'):
    """get png data via converting to PIL Image"""
    img = Image.fromstring("L", cvimg.shape[:2], cvimg.tostring())
    return pil2imgdata(img, format)

def array2imgdata_fs(cvimg, format='PNG'):
    """get png data via filesystem, using cv2.imwrite
    This is much faster than in-memory conversion with PIL on the rPi
    for some reason.
    fname = os.path.join(tempfile.gettempdir(), "_ipdisplay.%s" % format)
    cv2.imwrite(fname, cvimg)
    with open(fname) as f:
        data =
    return data

def display_image_array(a):
    """If an array looks like RGB data, display it as one"""
    if len(a.shape) != 3 or a.shape[-1] != 3:
    return array2pngdata_fs(a)

def register_image_formatters(ip):
    png_formatter = ip.display_formatter.formatters['image/png']
    # both, in case of pillow or true PIL
    png_formatter.for_type_by_name('PIL.Image', 'Image', pil2imgdata)
    png_formatter.for_type_by_name('Image', 'Image', pil2imgdata)
    # png_formatter.for_type_by_name('', 'iplimage', display_opencv_image)
    png_formatter.for_type_by_name("numpy", "ndarray", array2imgdata_fs)
In [3]:
ip = get_ipython()
png_formatter = ip.display_formatter.formatters['image/png']

Some basic GPIO setup

In [4]:
GPIO = wiringpi2.GPIO
mode = wiringpi2.pinMode
write = wiringpi2.digitalWrite
read = wiringpi2.digitalRead

The motion sensor alarm input is hooked up to GPIO pin 0, and the speaker is hooked up to GPIO pin 1. We set pin 0 as digital input, and pin 1 for tonal output.

In [7]:
pin = 0
mode(pin, GPIO.INPUT)
speaker = 1
In [8]:
def beep(f=261, pin=speaker):
    wiringpi2.softToneWrite(pin, f)

Image Capture with OpenCV

We have a simple webcam on USB, and will capture with OpenCV.

In [9]:
import cv2
import cv
cam = cv2.VideoCapture(-1)

Set it to 640 pixels wide at 16x9 aspect ratio, with 50% brightness and contrast.

In [10]:
width = 640
height = int(9 * width / 16)
cam.set(cv.CV_CAP_PROP_FRAME_WIDTH, width)
cam.set(cv.CV_CAP_PROP_FRAME_HEIGHT, height)
cam.set(cv. CV_CAP_PROP_BRIGHTNESS, 0.5)
cam.set(cv. CV_CAP_PROP_CONTRAST, 0.5)

Define our capture function - it flushes a few frames, because sometimes it seems there are stale images in the buffer.

It also can rotate the image, since sometimes we capture in portrait orientation.

In [22]:
CCW = lambda img: cv2.flip(cv2.transpose(img), 0)

def capture(rotate=False, flush=5):
    for i in range(flush):
        data =
    if rotate:
        return CCW(data[1])
        return data[1]

Test a capture

In [25]:
frame = capture()
In [26]:

Automating capture

Now we just start a simple polling loop, to capture images any time the motion sensor detects movement. It also emits a short beep on the speaker to indicate the capture.

In [23]:
import sys
from IPython.display import display, clear_output, Javascript
In [24]:
# little javascript trick to avoid bouncing
display(Javascript("$(this.element).show().css('min-height', '500px');"))

while True:
    if read(pin):
        # short beep to indicate capture
        tic = time.time()
        frame = capture()
        toc = time.time()
        print toc - tic
        tic = time.time()
        print time.time() - tic