99import cv2
1010from picamera .array import PiMotionAnalysis
1111from picamera import PiCamera
12+ from picamera import PiCameraRuntimeError
1213import numpy as np
1314from PIL import Image
1415from threading import Lock , Event
1516from queue import Queue
1617from .exit_clean import exit_error
1718from datetime import datetime
19+ from fractions import Fraction
1820
1921
2022logger = logging .getLogger ()
@@ -41,12 +43,13 @@ def __init__(self, photo_size, gif_size, motion_size, camera_vflip,
4143 self .camera_save_path = '/var/tmp'
4244 self .camera_capture_length = camera_capture_length
4345 self .camera_mode = camera_mode
46+ self .motion_detection_running = False
47+ self .too_dark_message_printed = False
4448
4549 try :
4650 self .camera = PiCamera ()
4751 self .camera .vflip = self .camera_vflip
4852 self .camera .hflip = self .camera_hflip
49- self .camera .awb_mode = 'off'
5053 except Exception as e :
5154 exit_error ('Camera module failed to intialise with error {0}' .format (repr (e )))
5255
@@ -57,10 +60,10 @@ def take_photo(self, filename_extra_suffix=''):
5760 timestamp = datetime .now ().strftime ("%Y-%m-%d-%H%M%S" )
5861 photo = '{0}/rpi-security-{1}{2}.jpeg' .format (self .camera_save_path , timestamp , filename_extra_suffix )
5962 try :
60- self .camera .resolution = self .photo_size
6163 with self .lock :
6264 while self .camera .recording :
6365 time .sleep (0.1 )
66+ time .sleep (1 )
6467 self .camera .resolution = self .photo_size
6568 self .camera .capture (photo , use_video_port = False )
6669 except PiCameraRuntimeError as e :
@@ -79,11 +82,11 @@ def take_gif(self):
7982 temp_jpeg_path = '{0}/rpi-security-{1}-gif-part' .format (self .temp_directory , timestamp )
8083 jpeg_files = ['{0}-{1}.jpg' .format (temp_jpeg_path , i ) for i in range (self .camera_capture_length * 3 )]
8184 try :
82- self .camera .resolution = self .gif_size
8385 for jpeg in jpeg_files :
8486 with self .lock :
8587 while self .camera .recording :
8688 time .sleep (0.1 )
89+ time .sleep (1 )
8790 self .camera .resolution = self .gif_size
8891 self .camera .capture (jpeg )
8992 im = Image .open (jpeg_files [0 ])
@@ -112,10 +115,12 @@ def trigger_camera(self):
112115
113116 def start_motion_detection (self , rpis ):
114117 past_frame = None
115- logger .debug ("Starting motion detection" )
116- self .camera .resolution = self .motion_size
117118 while not self .lock .locked () and rpis .state .current == 'armed' :
119+ if not self .motion_detection_running :
120+ logger .debug ("Starting motion detection" )
121+ self .motion_detection_running = True
118122 stream = io .BytesIO ()
123+ self .camera .resolution = self .motion_size
119124 self .camera .capture (stream , format = 'jpeg' , use_video_port = False )
120125 data = np .fromstring (stream .getvalue (), dtype = np .uint8 )
121126 frame = cv2 .imdecode (data , 1 )
@@ -126,7 +131,7 @@ def start_motion_detection(self, rpis):
126131 else :
127132 logger .error ("No more frame" )
128133 rpis .state .check ()
129- time .sleep (0.3 )
134+ time .sleep (0.2 )
130135 else :
131136 self .stop_motion_detection ()
132137
@@ -135,7 +140,6 @@ def handle_new_frame(self, frame, past_frame):
135140 r = 500 / float (w )
136141 dim = (500 , int (h * r ))
137142 frame = cv2 .resize (frame , dim , cv2 .INTER_AREA ) # We resize the frame
138-
139143 gray = cv2 .cvtColor (frame , cv2 .COLOR_BGR2GRAY ) # We apply a black & white filter
140144 gray = cv2 .GaussianBlur (gray , (21 , 21 ), 0 ) # Then we blur the picture
141145
@@ -151,10 +155,19 @@ def handle_new_frame(self, frame, past_frame):
151155 logger .error ('Past frame and current frame do not have the same sizes {0} {1} {2} {3}' .format (h_past_frame , w_past_frame , h_current_frame , w_current_frame ))
152156 return
153157
158+ # Detect when too dark to reduce false alarms
159+ if self .camera .digital_gain == Fraction (187 / 128 ) and self .camera .analog_gain == Fraction (8 ):
160+ if not self .too_dark_message_printed :
161+ logger .info ("Too dark to run motion detection" )
162+ self .too_dark_message_printed = True
163+ return None
164+ else :
165+ self .too_dark_message_printed = False
166+
154167 # compute the absolute difference between the current frame and first frame
155- frame_detla = cv2 .absdiff (past_frame , gray )
168+ frame_delta = cv2 .absdiff (past_frame , gray )
156169 # then apply a threshold to remove camera motion and other false positives (like light changes)
157- thresh = cv2 .threshold (frame_detla , 50 , 255 , cv2 .THRESH_BINARY )[1 ]
170+ thresh = cv2 .threshold (frame_delta , 50 , 255 , cv2 .THRESH_BINARY )[1 ]
158171
159172 # dilate the thresholded image to fill in holes, then find contours on thresholded image
160173 thresh = cv2 .dilate (thresh , None , iterations = 2 )
@@ -165,19 +178,20 @@ def handle_new_frame(self, frame, past_frame):
165178 for c in cnts :
166179 # if the contour is too small, ignore it
167180 countour_area = cv2 .contourArea (c )
181+
168182 if countour_area < self .motion_detection_threshold :
169183 continue
170184
171- logger .debug ("Motion detected! Motion level is {0} ( threshold is {1}) " .format (countour_area , self .motion_detection_threshold ))
185+ logger .info ("Motion detected! Motion level is {0}, threshold is {1}" .format (countour_area , self .motion_detection_threshold ))
172186 # Motion detected because there is a contour that is larger than the specified self.motion_detection_threshold
173187 # compute the bounding box for the contour, draw it on the frame,
174188 (x , y , w , h ) = cv2 .boundingRect (c )
175189 cv2 .rectangle (frame , (x , y ), (x + w , y + h ), (0 , 255 , 0 ), 2 )
176- self .handle_motion_detected (frame , gray , frame_detla , thresh )
190+ self .handle_motion_detected (frame )
177191
178192 return None
179193
180- def handle_motion_detected (self , frame , gray , frame_detla , thresh ):
194+ def handle_motion_detected (self , frame ):
181195 timestamp = datetime .now ().strftime ("%Y-%m-%d-%H%M%S" )
182196 bounding_box_path = '{0}/rpi-security-{1}-box.jpeg' .format (self .camera_save_path , timestamp )
183197 cv2 .imwrite (bounding_box_path , frame )
@@ -187,10 +201,12 @@ def handle_motion_detected(self, frame, gray, frame_detla, thresh):
187201
188202 def stop_motion_detection (self ):
189203 try :
204+ if self .motion_detection_running :
205+ logger .debug ("Stopping motion detection" )
206+ self .motion_detection_running = False
190207 if not self .camera .recording :
191208 return
192209 else :
193- logger .debug ("Stopping motion detection" )
194210 self .camera .stop_recording ()
195211 except Exception as e :
196212 logger .error ('Error in stop_motion_detection: {0}' .format (repr (e )))
0 commit comments