This notebook does motion-activated image capture, using a simple motion sensor and OpenCV for catpure.
from IPython.display import display, Image
Image(filename="setup.png")
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.
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).
"""
PNG formatter(s) for PIL and OpenCV Image objects
Adapted from pil_display extension: https://github.com/minrk/ipython_extensions
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()
img.save(fp, 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 = f.read()
os.unlink(fname)
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
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('cv2.cv', 'iplimage', display_opencv_image)
png_formatter.for_type_by_name("numpy", "ndarray", array2imgdata_fs)
ip = get_ipython()
png_formatter = ip.display_formatter.formatters['image/png']
register_image_formatters(ip)
Some basic GPIO setup
GPIO = wiringpi2.GPIO
wiringpi2.wiringPiSetup()
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.
pin = 0
mode(pin, GPIO.INPUT)
speaker = 1
wiringpi2.softToneCreate(speaker)
0
def beep(f=261, pin=speaker):
wiringpi2.softToneWrite(pin, f)
We have a simple webcam on USB, and will capture with OpenCV.
import cv2
import cv
cam = cv2.VideoCapture(-1)
Set it to 640 pixels wide at 16x9 aspect ratio, with 50% brightness and contrast.
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)
False
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.
CCW = lambda img: cv2.flip(cv2.transpose(img), 0)
def capture(rotate=False, flush=5):
for i in range(flush):
data = cam.read()
if rotate:
return CCW(data[1])
else:
return data[1]
Test a capture
frame = capture()
frame
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.
import sys
from IPython.display import display, clear_output, Javascript
# little javascript trick to avoid bouncing
display(Javascript("$(this.element).show().css('min-height', '500px');"))
while True:
if read(pin):
sys.stdout.flush()
time.sleep(.01)
else:
# short beep to indicate capture
beep()
time.sleep(0.025)
beep(0)
tic = time.time()
frame = capture()
toc = time.time()
clear_output()
print toc - tic
tic = time.time()
display(frame)
print time.time() - tic
0.519706964493
0.646742105484
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) <ipython-input-24-20f5a137cce0> in <module>() 5 if read(pin): 6 sys.stdout.flush() ----> 7 time.sleep(.01) 8 else: 9 beep() KeyboardInterrupt:
That LED is also hooked up to the motion sensor alarm pin, so it is on whenever the motion sensor is detecting something.