diff --git a/camera.py b/camera.py
index 065a321..9e528fb 100644
--- a/camera.py
+++ b/camera.py
@@ -3,11 +3,27 @@
import imutils
import time
import numpy as np
+import datetime
+
+video_res = (640, 480)
+object_det_res = (320, 240)
+
+video_frame_rate = 30
+video_out_dir = '/home/pi/'
+
+background_frame = None
+background_update_rate = 120 # 2 mins. Period to update background frame for motion detection.
+last_back_update = None
+motion_det_min_area = 2000 # Regulate motion detection sensitivity. Smaller value - greater sensitivity.
class VideoCamera(object):
def __init__(self, flip = False):
- self.vs = PiVideoStream().start()
+ self.vs = PiVideoStream(resolution=video_res, framerate=video_frame_rate).start()
self.flip = flip
+
+ self.x_coef = float(video_res[0]) / object_det_res[0] # needed to translate between frame sizes
+ self.y_coef = float(video_res[1]) / object_det_res[1]
+
time.sleep(2.0)
def __del__(self):
@@ -23,10 +39,50 @@ def get_frame(self):
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
+ def motion_detection(self):
+ frame = self.flip_if_needed(self.vs.read()) #grabbing frame
+
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #graying it
+ gray = cv2.GaussianBlur(gray, (5, 5), 0) #blurring
+
+ global background_frame
+ global last_back_update
+ global background_update_rate
+ global motion_det_min_area
+
+ if (background_frame is None) or (time.time() - last_back_update) > background_update_rate:
+ background_frame = gray
+ last_back_update = time.time()
+ return None, False
+
+ frameDelta = cv2.absdiff(background_frame, gray)
+ thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
+
+ thresh = cv2.dilate(thresh, None, iterations=2) #fill the holes
+ (_, cnts, _) = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #find contours of the objects
+
+ found_something = False
+
+ return_obj = frame
+ # return_obj = gray
+
+ for c in cnts:
+ # if the contour is too small, ignore it
+ if cv2.contourArea(c) < motion_det_min_area:
+ continue
+
+ found_something = True
+ (x, y, w, h) = cv2.boundingRect(c)
+ cv2.rectangle(return_obj, (x, y), (x + w, y + h), (153, 0, 204), 2) # different color rectangle for motion detect
+
+ ret, jpeg = cv2.imencode('.jpg', return_obj)
+ return (jpeg.tobytes(), found_something)
+
def get_object(self, classifier):
found_objects = False
frame = self.flip_if_needed(self.vs.read()).copy()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
+ gray = cv2.resize(gray, object_det_res, interpolation=cv2.INTER_AREA)
objects = classifier.detectMultiScale(
gray,
@@ -41,9 +97,27 @@ def get_object(self, classifier):
# Draw a rectangle around the objects
for (x, y, w, h) in objects:
- cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
+ cv2.rectangle(frame
+ , (int(self.x_coef * x), int(self.y_coef * y))
+ , (int(self.x_coef * x + self.x_coef * w), int(self.y_coef * y + self.y_coef * h))
+ , (0, 255, 0)
+ , 2)
ret, jpeg = cv2.imencode('.jpg', frame)
return (jpeg.tobytes(), found_objects)
+
+ def capture_video(self, send_video_len):
+ video_loc = video_out_dir + datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S") +'.avi'
+ fourcc = cv2.VideoWriter_fourcc(*'X264')
+ out = cv2.VideoWriter(video_loc, fourcc, video_frame_rate, video_res)
+
+ start_time = time.time()
+ while (time.time() - start_time) <= send_video_len:
+ frame = self.flip_if_needed(self.vs.read())
+ #(_, _, frame) = self.motion_detection()
+ out.write(frame)
+ out.release()
+
+ return video_loc
diff --git a/mail.py b/mail.py
index f1876f6..30c33d8 100644
--- a/mail.py
+++ b/mail.py
@@ -1,7 +1,9 @@
import smtplib
-from email.MIMEMultipart import MIMEMultipart
-from email.MIMEText import MIMEText
-from email.MIMEImage import MIMEImage
+from os.path import basename
+from email.mime.multipart import MIMEMultipart
+from email.mime.application import MIMEApplication
+from email.mime.text import MIMEText
+from email.mime.image import MIMEImage
# Email you want to send the update from (only works with gmail)
fromEmail = 'email@gmail.com'
@@ -20,8 +22,7 @@ def sendEmail(image):
msgRoot.preamble = 'Raspberry pi security camera update'
msgAlternative = MIMEMultipart('alternative')
- msgRoot.attach(msgAlternative)
- msgText = MIMEText('Smart security cam found object')
+ msgText = MIMEText('Smart security cam found object.')
msgAlternative.attach(msgText)
msgText = MIMEText('
', 'html')
@@ -29,6 +30,8 @@ def sendEmail(image):
msgImage = MIMEImage(image)
msgImage.add_header('Content-ID', '')
+
+ msgRoot.attach(msgAlternative)
msgRoot.attach(msgImage)
smtp = smtplib.SMTP('smtp.gmail.com', 587)
@@ -36,3 +39,33 @@ def sendEmail(image):
smtp.login(fromEmail, fromEmailPassword)
smtp.sendmail(fromEmail, toEmail, msgRoot.as_string())
smtp.quit()
+
+def sendVideoEmail(vid, keep_video_after_sending):
+ msgRoot = MIMEMultipart('related')
+ msgRoot['Subject'] = 'Security Update'
+ msgRoot['From'] = fromEmail
+ msgRoot['To'] = toEmail
+ msgRoot.preamble = 'Raspberry pi security camera update'
+
+ textStr = 'Smart security cam finished recording video.'
+ if keep_video_after_sending:
+ textStr += ' The video is saved on your Raspberry Pi at location: ' + vid
+
+ msgAlternative = MIMEMultipart('alternative')
+ msgText = MIMEText(textStr)
+ msgAlternative.attach(msgText)
+
+ with open(vid, "rb") as fil:
+ part = MIMEApplication(fil.read(), Name=basename(vid))
+
+ # After the file is closed
+ part['Content-Disposition'] = 'attachment; filename="%s"' % basename(vid)
+
+ msgRoot.attach(msgAlternative)
+ msgRoot.attach(part)
+
+ smtp = smtplib.SMTP('smtp.gmail.com', 587)
+ smtp.starttls()
+ smtp.login(fromEmail, fromEmailPassword)
+ smtp.sendmail(fromEmail, toEmail, msgRoot.as_string())
+ smtp.quit()
diff --git a/main.py b/main.py
index e6ed35d..d3127f5 100644
--- a/main.py
+++ b/main.py
@@ -1,15 +1,21 @@
import cv2
import sys
-from mail import sendEmail
+from mail import sendEmail, sendVideoEmail
from flask import Flask, render_template, Response
from camera import VideoCamera
from flask_basicauth import BasicAuth
import time
import threading
+import os
email_update_interval = 600 # sends an email only once in this time interval
-video_camera = VideoCamera(flip=True) # creates a camera object, flip vertically
+video_camera = VideoCamera(flip=False) # creates a camera object, flip vertically
object_classifier = cv2.CascadeClassifier("models/fullbody_recognition_model.xml") # an opencv classifier
+use_motion_detection = False
+
+send_video = True
+send_video_len = 30 #length of the video attached to the second email
+keep_video_after_sending = False
# App Globals (do not edit)
app = Flask(__name__)
@@ -21,17 +27,33 @@
last_epoch = 0
def check_for_objects():
- global last_epoch
- while True:
- try:
- frame, found_obj = video_camera.get_object(object_classifier)
- if found_obj and (time.time() - last_epoch) > email_update_interval:
- last_epoch = time.time()
- print "Sending email..."
- sendEmail(frame)
- print "done!"
- except:
- print "Error sending email: ", sys.exc_info()[0]
+ global last_epoch
+ while True:
+ try:
+ global use_motion_detection
+ if use_motion_detection:
+ frame, found_obj = video_camera.motion_detection()
+ if found_obj:
+ # motion detection is fired only if detected in two frames in a row (reduces false positive)
+ frame, found_obj = video_camera.motion_detection()
+ else:
+ frame, found_obj = video_camera.get_object(object_classifier)
+ if found_obj and (time.time() - last_epoch) > email_update_interval:
+ last_epoch = time.time()
+ print ("Sending email...")
+ sendEmail(frame)
+ print ("done!")
+ if send_video:
+ print ("Capturing video...")
+ vid = video_camera.capture_video(send_video_len)
+ print ("Sending video email...")
+ sendVideoEmail(vid, keep_video_after_sending)
+ print ("done!")
+ if not keep_video_after_sending:
+ os.remove(vid)
+ print ("Video file removed")
+ except:
+ print ("Error sending email: ", sys.exc_info()[0])
@app.route('/')
@basic_auth.required