SHARE
TWEET

Lightweight python motion detection

a guest Jul 27th, 2013 13,772 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/python
  2.  
  3. # original script by brainflakes, improved by pageauc, peewee2 and Kesthal
  4. # www.raspberrypi.org/phpBB3/viewtopic.php?f=43&t=45235
  5.  
  6. # You need to install PIL to run this script
  7. # type "sudo apt-get install python-imaging-tk" in an terminal window to do this
  8.  
  9. import StringIO
  10. import subprocess
  11. import os
  12. import time
  13. from datetime import datetime
  14. from PIL import Image
  15.  
  16. # Motion detection settings:
  17. # Threshold          - how much a pixel has to change by to be marked as "changed"
  18. # Sensitivity        - how many changed pixels before capturing an image, needs to be higher if noisy view
  19. # ForceCapture       - whether to force an image to be captured every forceCaptureTime seconds, values True or False
  20. # filepath           - location of folder to save photos
  21. # filenamePrefix     - string that prefixes the file name for easier identification of files.
  22. # diskSpaceToReserve - Delete oldest images to avoid filling disk. How much byte to keep free on disk.
  23. # cameraSettings     - "" = no extra settings; "-hf" = Set horizontal flip of image; "-vf" = Set vertical flip; "-hf -vf" = both horizontal and vertical flip
  24. threshold = 10
  25. sensitivity = 20
  26. forceCapture = True
  27. forceCaptureTime = 60 * 60 # Once an hour
  28. filepath = "/home/pi/picam"
  29. filenamePrefix = "capture"
  30. diskSpaceToReserve = 40 * 1024 * 1024 # Keep 40 mb free on disk
  31. cameraSettings = ""
  32.  
  33. # settings of the photos to save
  34. saveWidth   = 1296
  35. saveHeight  = 972
  36. saveQuality = 15 # Set jpeg quality (0 to 100)
  37.  
  38. # Test-Image settings
  39. testWidth = 100
  40. testHeight = 75
  41.  
  42. # this is the default setting, if the whole image should be scanned for changed pixel
  43. testAreaCount = 1
  44. 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]] ]
  45. # testBorders are NOT zero-based, the first pixel is 1 and the last pixel is testWith or testHeight
  46.  
  47. # with "testBorders", you can define areas, where the script should scan for changed pixel
  48. # for example, if your picture looks like this:
  49. #
  50. #     ....XXXX
  51. #     ........
  52. #     ........
  53. #
  54. # "." is a street or a house, "X" are trees which move arround like crazy when the wind is blowing
  55. # because of the wind in the trees, there will be taken photos all the time. to prevent this, your setting might look like this:
  56.  
  57. # testAreaCount = 2
  58. # testBorders = [ [[1,50],[1,75]], [[51,100],[26,75]] ] # area y=1 to 25 not scanned in x=51 to 100
  59.  
  60. # even more complex example
  61. # testAreaCount = 4
  62. # testBorders = [ [[1,39],[1,75]], [[40,67],[43,75]], [[68,85],[48,75]], [[86,100],[41,75]] ]
  63.  
  64. # in debug mode, a file debug.bmp is written to disk with marked changed pixel an with marked border of scan-area
  65. # debug mode should only be turned on while testing the parameters above
  66. debugMode = False # False or True
  67.  
  68. # Capture a small test image (for motion detection)
  69. def captureTestImage(settings, width, height):
  70.     command = "raspistill %s -w %s -h %s -t 200 -e bmp -n -o -" % (settings, width, height)
  71.     imageData = StringIO.StringIO()
  72.     imageData.write(subprocess.check_output(command, shell=True))
  73.     imageData.seek(0)
  74.     im = Image.open(imageData)
  75.     buffer = im.load()
  76.     imageData.close()
  77.     return im, buffer
  78.  
  79. # Save a full size image to disk
  80. def saveImage(settings, width, height, quality, diskSpaceToReserve):
  81.     keepDiskSpaceFree(diskSpaceToReserve)
  82.     time = datetime.now()
  83.     filename = filepath + "/" + filenamePrefix + "-%04d%02d%02d-%02d%02d%02d.jpg" % (time.year, time.month, time.day, time.hour, time.minute, time.second)
  84.     subprocess.call("raspistill %s -w %s -h %s -t 200 -e jpg -q %s -n -o %s" % (settings, width, height, quality, filename), shell=True)
  85.     print "Captured %s" % filename
  86.  
  87. # Keep free space above given level
  88. def keepDiskSpaceFree(bytesToReserve):
  89.     if (getFreeSpace() < bytesToReserve):
  90.         for filename in sorted(os.listdir(filepath + "/")):
  91.             if filename.startswith(filenamePrefix) and filename.endswith(".jpg"):
  92.                 os.remove(filepath + "/" + filename)
  93.                 print "Deleted %s/%s to avoid filling disk" % (filepath,filename)
  94.                 if (getFreeSpace() > bytesToReserve):
  95.                     return
  96.  
  97. # Get available disk space
  98. def getFreeSpace():
  99.     st = os.statvfs(filepath + "/")
  100.     du = st.f_bavail * st.f_frsize
  101.     return du
  102.  
  103. # Get first image
  104. image1, buffer1 = captureTestImage(cameraSettings, testWidth, testHeight)
  105.  
  106. # Reset last capture time
  107. lastCapture = time.time()
  108.  
  109. while (True):
  110.  
  111.     # Get comparison image
  112.     image2, buffer2 = captureTestImage(cameraSettings, testWidth, testHeight)
  113.  
  114.     # Count changed pixels
  115.     changedPixels = 0
  116.     takePicture = False
  117.  
  118.     if (debugMode): # in debug mode, save a bitmap-file with marked changed pixels and with visible testarea-borders
  119.         debugimage = Image.new("RGB",(testWidth, testHeight))
  120.         debugim = debugimage.load()
  121.  
  122.     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
  123.         for x in xrange(testBorders[z][0][0]-1, testBorders[z][0][1]): # = xrange(0,100) with default-values
  124.             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)
  125.                 if (debugMode):
  126.                     debugim[x,y] = buffer2[x,y]
  127.                     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)):
  128.                         # print "Border %s %s" % (x,y)
  129.                         debugim[x,y] = (0, 0, 255) # in debug mode, mark all border pixel to blue
  130.                 # Just check green channel as it's the highest quality channel
  131.                 pixdiff = abs(buffer1[x,y][1] - buffer2[x,y][1])
  132.                 if pixdiff > threshold:
  133.                     changedPixels += 1
  134.                     if (debugMode):
  135.                         debugim[x,y] = (0, 255, 0) # in debug mode, mark all changed pixel to green
  136.                 # Save an image if pixels changed
  137.                 if (changedPixels > sensitivity):
  138.                     takePicture = True # will shoot the photo later
  139.                 if ((debugMode == False) and (changedPixels > sensitivity)):
  140.                     break  # break the y loop
  141.             if ((debugMode == False) and (changedPixels > sensitivity)):
  142.                 break  # break the x loop
  143.         if ((debugMode == False) and (changedPixels > sensitivity)):
  144.             break  # break the z loop
  145.  
  146.     if (debugMode):
  147.         debugimage.save(filepath + "/debug.bmp") # save debug image as bmp
  148.         print "debug.bmp saved, %s changed pixel" % changedPixels
  149.     # else:
  150.     #     print "%s changed pixel" % changedPixels
  151.  
  152.     # Check force capture
  153.     if forceCapture:
  154.         if time.time() - lastCapture > forceCaptureTime:
  155.             takePicture = True
  156.  
  157.     if takePicture:
  158.         lastCapture = time.time()
  159.         saveImage(cameraSettings, saveWidth, saveHeight, saveQuality, diskSpaceToReserve)
  160.  
  161.     # Swap comparison buffers
  162.     image1 = image2
  163.     buffer1 = buffer2
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top