Last active
January 22, 2017 16:08
-
-
Save nicolaskern/8be47aac596c524c5a0b785b02800307 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 (not anymore) | |
# type "sudo apt-get install python-imaging-tk" in an terminal window to do this | |
# You need to install Astral : | |
# pip install astral | |
# And install pytz from https://pypi.python.org/pypi/pytz | |
import StringIO | |
import subprocess | |
import os | |
import time | |
import pytz | |
from astral import Astral | |
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 = 500 | |
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 = True # False or True | |
def letThereBeLight(): | |
a = Astral() | |
a.solar_depression = 'civil' | |
sun = a['Paris'].sun() | |
time = datetime.now() | |
# If it is daytime, we will see a sunset sooner than a sunrise. | |
if datetime.now(pytz.utc) < sun['sunrise'] or datetime.now(pytz.utc) > sun['sunset']: | |
print "%02dh%02dm%02d - it's night" % (time.hour, time.minute, time.second) | |
return "-drc high" | |
return "-ex night" | |
print "%02dh%02dm%02d - it's day" % (time.hour, time.minute, time.second) | |
return "" | |
return "-ex off" | |
# Capture a small test image (for motion detection) | |
def captureTestImage(settings, width, height): | |
settings = letThereBeLight() | |
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): | |
settings = letThereBeLight() | |
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 -e jpg -q %s -n -o %s" % (settings, width, height, quality, filename), shell=True) | |
#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 | |
print "Save a photo, just for the sport" | |
filename = filepath + "/" + filenamePrefix + "-current.jpg" | |
settings = letThereBeLight() | |
print "raspistill %s -w %s -h %s -e jpg -q %s -n -o %s" % (settings, saveWidth, saveHeight, saveQuality, filename) | |
subprocess.call("raspistill %s -w %s -h %s -e jpg -q %s -n -o %s --annotate 12" % (settings, saveWidth, saveHeight, saveQuality, filename), shell=True) | |
#subprocess.call("raspistill %s -w %s -h %s -t 200 -e jpg -q %s -n -o %s" % (settings, saveWidth, saveHeight, saveQuality, filename), shell=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment