-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdashcam.py
285 lines (254 loc) · 11.9 KB
/
dashcam.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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
"""
Synopsis:
- Capture images or video from a WebCam connected to a Raspberry Pi that receives commands from an Android Phone over BlueTooth
- Can be used to create timelapses or record car trips
Commands:
capture : Captures a single image
repeat [seconds] : Captures an image over a period defined in seconds. Default is 15 seconds.
setTime [seconds] : Sets the duration for a recurring image capture
stop : Stops recurring image capture
record : Starts a video recording
end : Ends a video recording
exit : Terminates dashcam
help : Returns this help message
Requirements:
- Python 3
- Raspberry Pi 3 (Tested on Model B V1.2)
- openCV's cv2 module
- pybluez's bluetooth module
- Serial bluetooth Terminal App (On Google Play)
Additional Notes:
- On Raspberry Pi, add the following line to the root user's crontab:
@reboot sleep 60 && usr/bin/python3 /home/pi/Documents/Projects/dashcam/dashcam.py >> ~/cron.log 2>&1
- This will execute the dashcam python script whenever the Raspberry Pi restarts.
- Video is hardcoded to record at 1280 by 720 at 10 fps to preven the file size from exceeding the size of the USB drive used during testing.
- Instructions on how bluetooth is enabled on the Raspberry Pi: https://raspberrypi.stackexchange.com/questions/45246/bluetooth-import-for-python-raspberry-pi-3
"""
import cv2
import bluetooth
import threading
import logging
from time import sleep
from datetime import datetime
from os import path, mkdir, getcwd, walk
FLASHDRIVENAME = '0410-673B' # Change this to match name assigned to USB drive when it is plugged into the Raspberry Pi.
ROOTDIR = '/media/pi/{0}/dashcam'.format(FLASHDRIVENAME)
PORT = 1
EXITFLAG = False
class stoppableThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self, *args, **kwargs):
super(stoppableThread, self).__init__(*args, **kwargs)
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
class cameraClass:
def __init__(self, fileDir):
self.cam = cv2.VideoCapture(0)
self.cam.set(3,1280)
self.cam.set(4,720)
self.cam.set(5, 10)
self.fileDir = fileDir
self.cameraThread = stoppableThread(target=self.captureImage, daemon=True)
self.cameraThread.start()
def captureImage(self):
while not self.cameraThread.stopped():
if EXITFLAG:
break
self.ret, self.frame = self.cam.read()
self.cam.release()
MESSAGES.append('Image capture stopped\r\n')
def saveImage(self):
if self.ret:
time = datetime.now()
currentTime = time.strftime('%Y%m%d%H%M%S')
currentTimeFormatted = time.strftime('%Y-%m-%d %H:%M:%S')
imageName = self.fileDir +'/' + currentTime + '.png'
cv2.imwrite(imageName, self.frame)
logging.debug('Image Saved: {0}'.format(imageName))
MESSAGES.append('Image captured @ {0} \r\n'.format(currentTimeFormatted))
def stopCapture(self):
if self.cameraThread.isAlive():
self.cameraThread.stop()
class repeatClass:
def __init__(self, cameraClass, sleepTime):
self.camera = cameraClass
self.sleepTime = sleepTime
self.repeatThread = stoppableThread(target=self.captureImageRepeat, daemon=True)
self.repeatThread.start()
def captureImageRepeat(self):
while not self.repeatThread.stopped():
if EXITFLAG:
break
self.camera.saveImage()
sleep(self.sleepTime)
MESSAGES.append('Recurring image capture stopped\r\n')
def stopRepeat(self):
if self.repeatThread.isAlive():
self.repeatThread.stop()
def setSleepTime(self, time):
self.sleepTime = time
class captureVideoClass:
def __init__(self, fileDir):
self.fileDir = fileDir
self.videoThread = stoppableThread(target=self.captureVideo, args=(self.fileDir,), daemon=True)
self.videoThread.start()
def captureVideo(self, fileDir):
cam = cv2.VideoCapture(0)
cam.set(3,1280)
cam.set(4,720)
cam.set(5, 20)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
time = datetime.now()
currentTime = time.strftime('%Y%m%d%H%M%S')
currentTimeFormatted = time.strftime('%Y-%m-%d %H:%M:%S')
videoFileName = fileDir + '/' + currentTime + '.avi'
out = cv2.VideoWriter(videoFileName,fourcc, 10.0, (1280,720))
MESSAGES.append('Recording Video @ {0} \r\n'.format(currentTimeFormatted))
while not self.videoThread.stopped():
ret, frame = cam.read()
if ret:
out.write(frame)
cam.release()
out.release()
time = datetime.now()
currentTimeFormatted = time.strftime('%Y-%m-%d %H:%M:%S')
MESSAGES.append('Ending Recording @ {0} \r\n'.format(currentTimeFormatted))
def stopVideo(self):
if self.videoThread.isAlive():
self.videoThread.stop()
class parseCommandClass:
def __init__(self, port):
self.server_socket=bluetooth.BluetoothSocket(bluetooth.RFCOMM)
self.server_socket.bind(("",port))
self.server_socket.listen(1)
logging.debug('Waiting for connection with client')
(self.client_socket, self.address) = self.server_socket.accept()
logging.debug('Accepted connection from {0}'.format(self.address))
self.sendMessage('dashcam initiated. Welcome!')
self.commandThread = stoppableThread(target=self.parseCommand, args=(self.client_socket,), daemon=True)
self.commandThread.start()
def parseCommand(self, client_socket):
while not self.commandThread.stopped():
if EXITFLAG:
break
rawData = client_socket.recv(1024)
mungedData = rawData.decode("utf-8").rstrip()
COMMANDS.append(mungedData)
def stopListening(self):
if self.commandThread.isAlive():
self.commandThread.stop()
def sendMessage(self, message):
self.client_socket.send(message)
## Main
for handler in logging.root.handlers[:]:
logging.root.removeHandler(handler)
logging.basicConfig(level=logging.ERROR)
logFileName = '{0}/dashcam.log'.format(getcwd())
logging.basicConfig(level=logging.DEBUG, filename=logFileName, format='%(asctime)s:%(name)s:%(levelname)s:%(message)s')
# Check to see if ROOTDIR exists (if the USB drive is inserted) before beginning video capture
if path.exists(ROOTDIR):
# Create current date folder if it does not already exists
currentDateDir = '{0}/{1}'.format(ROOTDIR, datetime.now().strftime('%Y%m%d'))
if not path.exists(currentDateDir):
mkdir(currentDateDir)
# Create a new session directory
dirList = [curDir[0] for curDir in walk(currentDateDir)][1:]
sessionDir = '{0}/Session_{1}'.format(currentDateDir, str(len(dirList)))
mkdir(sessionDir)
COMMANDS = []
MESSAGES = []
commandThread = parseCommandClass(PORT)
cameraThread = cameraClass(sessionDir)
captureThreads = []
repeatThreads = []
videoThreads = []
repeatFlag = False
while not EXITFLAG:
# Collect commands inputed from phone and execute in FIFO order.
logging.debug('Command List: {0}'.format(COMMANDS))
COMMANDS.reverse()
while COMMANDS:
curCommand = COMMANDS.pop(0)
if curCommand == 'capture':
if not videoThreads:
cameraThread.saveImage()
else:
MESSAGES.append('Cannot capture images while video is recording. Use \'end\' to end video recording.\r\n')
if curCommand.split(' ')[0] == 'repeat':
if not videoThreads:
if not repeatThreads:
if len(curCommand.split(' ')) > 1:
time = int(curCommand.split(' ')[1])
else:
time = 15
repeatThreads.append(repeatClass(cameraThread, time))
MESSAGES.append('Recurring image capture started. Duration set to: {0} seconds\r\n'.format(str(time)))
else:
MESSAGES.append('Recurring image capture already started. Use \'stop\' or \'setTime\' to modify.\r\n')
else:
MESSAGES.append('Cannot capture images while video is recording. Use \'end\' to end video recording.\r\n')
if curCommand.split(' ')[0] == 'setTime':
if repeatThreads:
if len(curCommand.split(' ')) > 1:
time = int(curCommand.split(' ')[1])
else:
time = 15
repeatThreads[0].setSleepTime(time)
logging.debug('Recurring image capture duration set to: {0} seconds'.format(str(time)))
MESSAGES.append('Recurring image capture duration set to: {0} seconds\r\n'.format(str(time)))
if curCommand == 'stop':
while repeatThreads:
curThread = repeatThreads.pop(0)
curThread.stopRepeat()
if curCommand == 'record':
if not repeatThreads and not captureThreads:
if not videoThreads:
videoThreads.append(captureVideoClass(sessionDir))
else:
MESSAGES.append('Video already recording. Use \'end\' to end the recording.\r\n')
else:
MESSAGES.append('Cannot record video while image is being captured.\r\n')
if curCommand == 'end':
while videoThreads:
curThread = videoThreads.pop(0)
curThread.stopVideo()
if curCommand == 'exit':
while repeatThreads:
curThread = repeatThreads.pop(0)
curThread.stopRepeat()
while videoThreads:
curThread = videoThreads.pop(0)
curThread.stopVideo()
cameraThread.stopCapture()
EXITFLAG = True
if curCommand == 'help':
MESSAGES.append('Synopsis: Capture images or video from Raspberry Pi Webcam using Android Phone over BlueTooth\r\n'
+ 'Commands: \r\n'
+ 'capture : Captures a single image\r\n'
+ 'repeat [seconds] : Captures an image over a period defined in seconds. Default is 15 seconds.\r\n'
+ 'setTime [seconds] : Sets the duration for a recurring image capture\r\n'
+ 'stop : Stops recurring image capture\r\n'
+ 'record : Starts a video recording\r\n'
+ 'end : Ends a video recording\r\n'
+ 'exit : Terminates dashcam\r\n'
+ 'help : Returns this help message\r\n'
)
logging.debug('captureThreads length: {0}'.format(len(captureThreads)))
while MESSAGES:
curMessage = MESSAGES.pop(0)
commandThread.sendMessage(curMessage)
#sleep(1)
commandThread.sendMessage('Terminating dashcam. GoodBye! \r\n')
commandThread.stopListening()
logging.debug('dashcam Successfully Terminated')
else:
logging.debug('USB drive not found. dashcam Terminated')
# Main Loop
# Spin up Listener thread; This thread listens for instructions from phone
# Spin up new thread based on instructions received:
# Repeat Thread: Capture image periodically until a stop instruction is received
# Capture Thread: Capture a single image and then terminate the thread