Motion detection on the Pi is a very common task that many people have done on the net. The best performance is accomplished using c, however I ran into the picamera python library and decided that I wanted to give it a try since I needed to get more proficient at python code for work. I have been using a modified version of the ‘motion’ program that was customized for the pi, but I did not like how it worked, so why not try something else?
I was not able to find an example on the net that gave me exactly what I wanted, so I decided I would post my work in case it would help someone else who wants to build a very simple motion detection system.
I used a Pi Model A with a Raspberry Pi camera modified to remove the IR filter for this project, and a wifi usb dongle.
Using the picamera python package was very easy, and resulted in a decent motion detection program. It can only pull and compare images at 1 frame per second, I have not looked into speeding it up yet, I will try to improve it later as i get more familiar with python…
To install the picamera software:
sudo apt-get update sudo apt-get install python3-picamera
Now save the following code in the file /home/pi/motion.py
#! /usr/bin/env python3 # #The MIT License (MIT) #Copyright (c) 2014 Ron Ostafichuk # #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal #in the Software without restriction, including without limitation the rights #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the Software is #furnished to do so, subject to the following conditions: # #The above copyright notice and this permission notice shall be included in all #copies or substantial portions of the Software. # #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #SOFTWARE. import io import os import time import picamera import numpy as np widthH = 2592 # too slow for motion check, only use for the save sequence heightH = 1944 width = 1440 # use lower resolution for motion check height = 1080 threshold = 30 # how much must the color value (0-255) change to be considered a change minPixelsChanged = width * height * 2 / 100 # % change # how many pixels must change to begin a save sequence print("minPixelsChanged=",minPixelsChanged) # debug print ('Creating in-memory stream') stream = io.BytesIO() step = 1 # use this to toggle where the image gets saved numImages = 1 # count number of images processed captureCount = 0 # flag used to begin a sequence capture # function to generate a sequence of 20 filenames for the picamera capture_sequence command to use def filenames(): frame = 0 fileName = time.strftime("NAS/%Y%m%d/%Y%m%d-%H%M%S-",time.localtime()) while frame < 20: yield '%s%02d.jpg' % (fileName, frame) frame += 1 # begin monitoring with picamera.PiCamera() as camera: time.sleep(1) # let camera warm up try: while threshold > 0: camera.resolution = (1440,1080) # use a smaller resolution for higher speed compare print ('Capture ' , numImages) if step == 1: stream.seek(0) camera.capture(stream, 'rgba',True) # use video port for high speed data1 = np.fromstring(stream.getvalue(), dtype=np.uint8) step = 2 else: stream.seek(0) camera.capture(stream, 'rgba',True) data2 = np.fromstring(stream.getvalue(), dtype=np.uint8) step = 1 numImages = numImages + 1 if numImages > 4: # ignore first few images because if the camera is not quite ready it will register as motion right away # look for motion unless we are in save mode if captureCount <= 0: print("Compare") # not capturing, test for motion (very simplistic, but works good enough for my purposes) data3 = np.abs(data1 - data2) # get difference between 2 successive images numTriggers = np.count_nonzero(data3 > threshold) / 4 / threshold #there are 4 times the number of pixels due to rgba print("Trigger cnt=",numTriggers) if numTriggers > minPixelsChanged: captureCount = 1 # capture ? sequences in a row # make sure directory exists for today d = time.strftime("NAS/%Y%m%d") #unfortunately this saves as UTC time instead of local, will fix it later sorry if not os.path.exists(d): os.makedirs(d) if captureCount > 0: # in capture mode, save an image in hi res camera.resolution = (widthH,heightH) dtFileStr = time.strftime("NAS/%Y%m%d/%Y%m%d-%H%M%S-00.jpg",time.localtime()) # once again, UTC time instead of local time print("Saving sequence ",dtFileStr) # save full resolution images to the NAS camera.capture_sequence(filenames(),'jpeg',use_video_port=True, quality=92) captureCount = captureCount-1 finally: camera.close() print ('Program Terminated')
If you make this file executable using
chmod a+x motion.py
Then you can run the program by typing
To make the capture of large numbers of files practical, the program needs to save the files to a network share. I have set up my network share on a NAS box running linux, added a user named pi with a password and given that user a share name ‘Security’ with a subdirectory named ‘Pi’.
On the pi type:
mkdir ~/NAS sudo nano /etc/fstab
Put this line at the bottom of the /etc/fstab file
//192.168.1.100/Security/Pi /home/pi/NAS cifs credentials=/home/pi/.smbcredentials,uid=pi,file_mode=0777,dir_mode=0777 0 0
You must create the credentials entry as follows, and it must match what you have set up on your NAS box:
sudo nano ~/.smbcredentials
Put the following lines into the file and save it:
username=pi password=YourPasswordOnTheNASBox domain=workgroup
To get this program to run on the pi after every boot you need to edit the /etc/rc.local file
sudo nano /etc/rc.local
Place this line BEFORE the ‘Exit 0’ line
(sleep 8;sudo su - pi -c "/home/pi/motion.py")&
This will cause the program to start about 8 seconds after the bootup, and it will run as the ‘pi’ user instead of root, which you need if you want the NAS box to work. Remember the credentials for the NAS are based on the pi user.
The hardest part of this exercise is getting your NAS box to play nice with the pi. There are so many different NAS boxes out there that I have not included any instructions. I got tired of low performance NAS boxes and built my own Linux based NAS using a very old atom based netbook. It was still way faster than my DNS-323, glad to see that thing gone.
Well, I hope this is helpful for someone! I will be tweaking it once in a while and posting any improvements back to this page.