-
Notifications
You must be signed in to change notification settings - Fork 1
/
pypicam.py
202 lines (176 loc) · 6.66 KB
/
pypicam.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/usr/bin/env python
# Based on original picam by brianflakes, improved by pageauc, peewee2
# and Kesthal
# Modified to use multiple storage backend and provide a pure python interface
# by Dave Wilde <[email protected]>
# PIL and python-picamera are required to run this script
# sudo apt-get update
# sudo apt-get install python-imaging-tk python-picamera
import io
import os
import picamera
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.
# cameraSettings
# "" = no extra settings
# "hflip" = Set horizontal flip of image
# "vflip" = Set vertical flip of the image
# "noled" = Turn off the camera LED
threshold = 20
sensitivity = 20
forceCapture = True
forceCaptureTime = 60 * 10
filepath = "/home/pi/picam"
filenamePrefix = "capture"
cameraSettings = ""
# settings of the photos to save
saveWidth = 1296
saveHeight = 972
saveQuality = 15 # Set jpeg quality (0 to 100)
# Test-Image settings
testWidth = 800
testHeight = 600
# this is the default setting, if the whole image should be scanned for
# changed pixel
testAreaCount = 2
testBorders = [[[1, 500], [1, 400]], [[1, 800], [401, 600]]]
# [ [[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
debugPics = 20
# Capture a small test image (for motion detection)
def captureTestImage(settings, width, height):
imageData = io.BytesIO()
with picamera.PiCamera() as camera:
camera.resolution = (width, height)
if 'noled' in settings:
camera.led = False
camera.capture(imageData, format='bmp')
imageData.seek(0)
im = Image.open(imageData)
buffer = im.load()
imageData.close()
return im, buffer
# Capture the full image and save it to disk
def captureImage(settings, width, height, jpegQuality):
time = datetime.now()
filename = filenamePrefix + "-%04d%02d%02d-%02d%02d%02d.jpg" % (
time.year, time.month, time.day, time.hour, time.minute, time.second
)
outfile = os.path.join(filepath, filename)
with picamera.PiCamera() as camera:
if 'hflip' in settings:
camera.hflip = True
if 'vflip' in settings:
camera.vflip = True
if 'noled' in settings:
camera.led = False
camera.resolution = (width, height)
camera.capture(outfile, quality=jpegQuality)
print "Captured {}".format(outfile)
# Do the motion detection
def detectMotion(image1, buffer1, image2, buffer2, testAreaCount, testBorders,
debugCounter=0):
# If debugMode is true we create a debug bitmap
if (debugMode):
debugimage = Image.new("RGB", (testWidth, testHeight))
debugim = debugimage.load()
changedPixels = 0
takePicture = False
for z in xrange(0, testAreaCount):
for x in xrange(testBorders[z][0][0]-1, testBorders[z][0][1]):
for y in xrange(testBorders[z][1][0]-1, testBorders[z][1][1]):
if (debugMode):
# Mark the borders of the test area blue
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)):
debugim[x, y] = (0, 0, 255)
# Check the green channel for motion as it has the greatest
# contrast
pixdiff = abs(buffer1[x, y][1] - buffer2[x, y][1])
if (pixdiff > threshold):
changedPixels += 1
if (debugMode):
# Make the changed pixels green
debugim[x, y] = (0, 255, 0)
if (changedPixels > sensitivity):
takePicture = True
# Break out of the loops if we're taking the picture
# and we're not in debug mode
if ((not debugMode) and takePicture):
break
if ((not debugMode) and takePicture):
break
if ((not debugMode) and takePicture):
break
if (debugMode):
debugimage.save('debug-' + str(debugCounter) + '.bmp')
print "Debug Image {} Written".format(debugCounter)
return takePicture
if __name__ == "__main__":
image1, buffer1 = captureTestImage(cameraSettings, testWidth, testHeight)
lastCapture = time.time()
debugCounter = 0
while (True):
image2, buffer2 = captureTestImage(cameraSettings, testWidth,
testHeight)
if detectMotion(image1, buffer1, image2, buffer2, testAreaCount,
testBorders, debugCounter):
captureImage(cameraSettings, saveWidth, saveHeight, saveQuality)
lastCapture = time.time()
if (forceCapture and time.time() - lastCapture > forceCaptureTime):
captureImage(cameraSettings, saveWidth, saveHeight, saveQuality)
lastCapture = time.time()
image1, buffer1 = image2, buffer2
if (debugMode):
if debugCounter == debugPics:
debugCounter = 0
else:
debugCounter += 1