Introduction

Raspberry Pi can connect to a camera to capture picture and video. As an advance feature, it can be used as a CCTV to monitor the environment. For example, from official Raspberry Pi web site “https://www.raspberrypi.org/forums/viewtopic.php?t=45235”, there shows a simple and efficient motion detection script in Python using PIL.

While watching for motion it pipes a thumbnail image from raspistill at around 1fps to analyse (it keeps everything in memory to avoid wearing out the SD card). Once motion is detected it calls raspistill again to write a high-res jpeg to disk. It also checks free disk space and if under a set limit it starts to delete the oldest images to make sure there is always enough free space for new images. While running on my rev1 B it consumes around 12% CPU / 4% ram and manages to capture a full size image once ever 2-3 secs.

picamera

picamera

In the following, I will show you the step of of installation of this motion detection python script.

Step 1. Install PIL by running “sudo aptitude install python-imaging-tk”

Step 2. Create a python script with file name “picam.py” as below:

#!/usr/bin/python

# original script by brainflakes, improved by pageauc, peewee2 and Kesthal
# www.raspberrypi.org/phpBB3/viewtopic.php?f=43&t=45235

# You need to install PIL to run this script
# type “sudo apt-get install python-imaging-tk” in an terminal window to do this

import StringIO
import subprocess
import os
import time
from datetime import datetime
from PIL import Image

# Motion detection settings:
# Threshold          – how much a pixel has to change by to be marked as “changed”
# Sensitivity        – how many changed pixels before capturing an image, needs to be higher if noisy view
# ForceCapture       – whether to force an image to be captured every forceCaptureTime seconds, values True or False
# filepath           – location of folder to save photos
# filenamePrefix     – string that prefixes the file name for easier identification of files.
# diskSpaceToReserve – Delete oldest images to avoid filling disk. How much byte to keep free on disk.
# cameraSettings     – “” = no extra settings; “-hf” = Set horizontal flip of image; “-vf” = Set vertical flip; “-hf -vf” = both horizontal and vertical flip
threshold = 10
sensitivity = 20
forceCapture = True
forceCaptureTime = 60 * 60 # Once an hour
filepath = “/home/pi/picam”
filenamePrefix = “capture”
diskSpaceToReserve = 40 * 1024 * 1024 # Keep 40 mb free on disk
cameraSettings = “”

# settings of the photos to save
saveWidth   = 1296
saveHeight  = 972
saveQuality = 15 # Set jpeg quality (0 to 100)

# Test-Image settings
testWidth = 100
testHeight = 75

# this is the default setting, if the whole image should be scanned for changed pixel
testAreaCount = 1
testBorders = [ [[1,testWidth],[1,testHeight]] ]  # [ [[start pixel on left side,end pixel on right side],[start pixel on top side,stop pixel on bottom side]] ]
# testBorders are NOT zero-based, the first pixel is 1 and the last pixel is testWith or testHeight

# with “testBorders”, you can define areas, where the script should scan for changed pixel
# for example, if your picture looks like this:
#
#     ….XXXX
#     ……..
#     ……..
#
# “.” is a street or a house, “X” are trees which move arround like crazy when the wind is blowing
# because of the wind in the trees, there will be taken photos all the time. to prevent this, your setting might look like this:

# testAreaCount = 2
# testBorders = [ [[1,50],[1,75]], [[51,100],[26,75]] ] # area y=1 to 25 not scanned in x=51 to 100

# even more complex example
# testAreaCount = 4
# testBorders = [ [[1,39],[1,75]], [[40,67],[43,75]], [[68,85],[48,75]], [[86,100],[41,75]] ]

# in debug mode, a file debug.bmp is written to disk with marked changed pixel an with marked border of scan-area
# debug mode should only be turned on while testing the parameters above
debugMode = False # False or True

# Capture a small test image (for motion detection)
def captureTestImage(settings, width, height):
    command = “raspistill %s -w %s -h %s -t 200 -e bmp -n -o -” % (settings, width, height)
    imageData = StringIO.StringIO()
    imageData.write(subprocess.check_output(command, shell=True))
    imageData.seek(0)
    im = Image.open(imageData)
    buffer = im.load()
    imageData.close()
    return im, buffer

# Save a full size image to disk
def saveImage(settings, width, height, quality, diskSpaceToReserve):
    keepDiskSpaceFree(diskSpaceToReserve)
    time = datetime.now()
    filename = filepath + “/” + filenamePrefix + “-%04d%02d%02d-%02d%02d%02d.jpg” % (time.year, time.month, time.day, time.hour, time.minute, time.second)
    subprocess.call(“raspistill %s -w %s -h %s -t 200 -e jpg -q %s -n -o %s” % (settings, width, height, quality, filename), shell=True)
    print “Captured %s” % filename

# Keep free space above given level
def keepDiskSpaceFree(bytesToReserve):
    if (getFreeSpace() < bytesToReserve):
        for filename in sorted(os.listdir(filepath + “/”)):
            if filename.startswith(filenamePrefix) and filename.endswith(“.jpg”):
                os.remove(filepath + “/” + filename)
                print “Deleted %s/%s to avoid filling disk” % (filepath,filename)
                if (getFreeSpace() > bytesToReserve):
                    return

# Get available disk space
def getFreeSpace():
    st = os.statvfs(filepath + “/”)
    du = st.f_bavail * st.f_frsize
    return du

# Get first image
image1, buffer1 = captureTestImage(cameraSettings, testWidth, testHeight)

# Reset last capture time
lastCapture = time.time()

while (True):

    # Get comparison image
    image2, buffer2 = captureTestImage(cameraSettings, testWidth, testHeight)

    # Count changed pixels
    changedPixels = 0
    takePicture = False

    if (debugMode): # in debug mode, save a bitmap-file with marked changed pixels and with visible testarea-borders
        debugimage = Image.new(“RGB”,(testWidth, testHeight))
        debugim = debugimage.load()

    for z in xrange(0, testAreaCount): # = xrange(0,1) with default-values = z will only have the value of 0 = only one scan-area = whole picture
        for x in xrange(testBorders[z][0][0]-1, testBorders[z][0][1]): # = xrange(0,100) with default-values
            for y in xrange(testBorders[z][1][0]-1, testBorders[z][1][1]):   # = xrange(0,75) with default-values; testBorders are NOT zero-based, buffer1[x,y] are zero-based (0,0 is top left of image, testWidth-1,testHeight-1 is botton right)
                if (debugMode):
                    debugim[x,y] = buffer2[x,y]
                    if ((x == testBorders[z][0][0]-1) or (x == testBorders[z][0][1]-1) or (y == testBorders[z][1][0]-1) or (y == testBorders[z][1][1]-1)):
                        # print “Border %s %s” % (x,y)
                        debugim[x,y] = (0, 0, 255) # in debug mode, mark all border pixel to blue
                # Just check green channel as it’s the highest quality channel
                pixdiff = abs(buffer1[x,y][1] – buffer2[x,y][1])
                if pixdiff > threshold:
                    changedPixels += 1
                    if (debugMode):
                        debugim[x,y] = (0, 255, 0) # in debug mode, mark all changed pixel to green
                # Save an image if pixels changed
                if (changedPixels > sensitivity):
                    takePicture = True # will shoot the photo later
                if ((debugMode == False) and (changedPixels > sensitivity)):
                    break  # break the y loop
            if ((debugMode == False) and (changedPixels > sensitivity)):
                break  # break the x loop
        if ((debugMode == False) and (changedPixels > sensitivity)):
            break  # break the z loop

    if (debugMode):
        debugimage.save(filepath + “/debug.bmp”) # save debug image as bmp
        print “debug.bmp saved, %s changed pixel” % changedPixels
    # else:
    #     print “%s changed pixel” % changedPixels

    # Check force capture
    if forceCapture:
        if time.time() – lastCapture > forceCaptureTime:
            takePicture = True

    if takePicture:
        lastCapture = time.time()
        saveImage(cameraSettings, saveWidth, saveHeight, saveQuality, diskSpaceToReserve)

    # Swap comparison buffers
    image1 = image2
    buffer1 = buffer2

Step 3: Change execute mode of picam.py by running the command “sudo chmod 755 picam.py

Step 4: Make a directory by running the command “mkdir /home/pi/picam” which is a hard code path in the picam.py script.

Step 5: Run the picam.py script by execute the command “sudo python picam.py

Step 6: You will see that a series of picture files will be created under the /home/pi/picam directory.

Step 7: If you want to auto-start this program at boot-up, you should create the following script by download a file to directory “/etc/init.d/picam_init” as below link –> https://infotechmanagefactory.com/wp-content/uploads/2016/01/picam_init.zip.

Step 8. change its mode as: sudo chmod 755 /etc/init.d/picam_init

Step 9: make the boot system aware of this script as “sudo update-rc.d picam_init defaults

Step 10: This script will now start and shutdown along with the Raspberry Pi. You can also manually control it like any other daemon. Running “/etc/init.d/picam_init stop" will stop the script and “/etc/init.d/picam_init start" will start it.

Reference Doc Link : https://www.maketecheasier.com/raspberry-pi-as-surveillance-camera/

Leave a Reply

Your email address will not be published.

Enter Captcha Here : *

Reload Image