Skip to content

Instantly share code, notes, and snippets.

@tacoman784
Last active May 19, 2017 22:01
Show Gist options
  • Save tacoman784/07e909e8528f412364f044985d7df32e to your computer and use it in GitHub Desktop.
Save tacoman784/07e909e8528f412364f044985d7df32e to your computer and use it in GitHub Desktop.
# **************************************
# Simple OpenCV / Python Security Camera
# Defines three Zones where activity is looked for.
# Each Zone is a region-of-interest for OpenCV.
# Simple toggle switches allow video on/off and Zone switching.
# Runs on a Raspberry Pi Model B with standard Pi Camera in a IP64 ELectrical box with
# Ethernet cable to home network.
# Adapted from http://www.pyimagesearch.com/2015/06/01/home-surveillance-and-motion-detection-with-the-raspberry-pi-python-and-opencv/
#
# **************************************
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import datetime
import numpy as np
import cv2
import glob
import os
# TO DO
# Script to start on boot and cron job to check if running
# see http://frederickvandenbosch.be/?p=1701
# ********************
def roi(img,vertices):
# ********************
mask = np.zeros_like(img)
cv2.fillPoly(mask,vertices,255)
masked = cv2.bitwise_and(img,mask)
return masked
# ********************************
def auto_canny(image, sigma=0.33):
# ********************************
# compute the median of the single channel pixel intensities
v = np.median(image)
# apply automatic Canny edge detection using the computed median
lower = int(max(0, (1.0 - sigma) * v))
upper = int(min(255, (1.0 + sigma) * v))
edged = cv2.Canny(image, lower, upper)
# return the edged image
return edged
# ********************************************
def DeleteImages(DaysImagesToKeep,DayOfMonth):
# ********************************************
if DaysImagesToKeep == 7:
if DayOfMonth <= 7:
for x in range(8,31):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
elif DayOfMonth <= 14:
for x in range(1,6):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
for x in range(15,31):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
elif DayOfMonth <= 21:
for x in range(1,15):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
for x in range(22,31):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
elif DayOfMonth <= 31:
for x in range(1,21):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
# *************************
elif DaysImagesToKeep == 14:
# *************************
if DayOfMonth <= 7:
for x in range(8,21):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
elif DayOfMonth <= 14:
for x in range(15,31):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
elif DayOfMonth <= 21:
for x in range(1,7):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
for x in range(22,31):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
elif DayOfMonth <= 31:
for x in range(1,14):
directory= '/home/pi/Desktop/snapshots/Day'+str(x)
print 'Deleting from ' + str(directory)
try:
os.chdir(directory)
files=glob.glob('*.jpg')
for filename in files:
os.unlink(filename)
except:
pass
return
# *******************************
def Main(Mode,DEBUG,Method,Zone):
# *******************************
# Yellow (0,255,255)
# Red
# Green (0,255,0)
# White (255, 255, 255)
# ************* # Set Variables
OldMode = Mode
ThreshValue = 12
MinArea = 450
OldMinArea = MinArea
contouredimage = None
min_motion_frames = 10
min_snapshot_seconds = 2
MaxSnapshots = 40
Output1 = True
Output2 = False
Snapshots = 0
MaxArea = 0
OccupiedCount = 0
DayNumber = 0
PhotoToggle = 'On'
DaysImagesToKeep = 7 # ,14
DoneMidnight = False
OldContour = 0 # Allows rectangles to suppress redraw
VideoToggle = 'Off'
# *********************************
# Set Region of Interest dimensions
# *********************************
# ********************************************************************
# initialize the camera and grab a reference to the raw camera capture
# ********************************************************************
camera = PiCamera()
camera.resolution = (640, 480)
#camera.framerate = 32
rawCapture = PiRGBArray(camera, size=(640, 480))
# ***************
# Camera Settings
# http://stackoverflow.com/questions/11420748/setting-camera-parameters-in-opencv-python
# ***************
# allow the camera to warmup
time.sleep(2)
avg = None
lastUploaded = datetime.datetime.now()
lp = 0
month = datetime.datetime.now().month
# ******************************
# capture frames from the camera
# ******************************
area_List = []
Alert = False
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
lp += 1
text = "Unoccupied"
ts = datetime.datetime.now().strftime("%A %d %B %Y %H:%M:%S")
# SUMMER OR WINTER? (DUSK IS DIFFERENT)
if lp < 5 or lp % 5000 == 0:
hour = datetime.datetime.now().hour
DayNumber = datetime.datetime.now().day
minute = datetime.datetime.now().minute
# **************
# MIDNIGHT CHECK
# **************
if int(hour) == 0:
if int(minute) == 1 and DoneMidnight == False:
DayOfMonth = datetime.datetime.now().day
lp = 0 # reset frame count
try:
DeleteImages(DaysImagesToKeep,DayOfMonth)
DoneMidnight = True
except exception as e:
print str(e)
elif int(minute) == 2:
DoneMidnight = False
# ***********************
# Check every 5000 frames
# ***********************
if lp % 5000 == 0:
DayNumber = datetime.datetime.now().day
# Check for Size of local directory
# /home/pi/Desktop/snapshots/Dayxx
if lp % 5 == 0:
OldContour = 0
# WHEN IS DUSK?
if month == 1:
Dusk = 18
Dawn = 7
elif month == 2:
Dusk = 19
Dawn = 7
elif month == 3:
Dusk = 19
Dawn = 6
elif month == 4:
Dusk = 20
Dawn = 6
elif month == 5:
Dusk = 21
Dawn = 6
elif month == 6:
Dusk = 22
Dawn = 6
elif month == 7:
Dusk = 22
Dawn = 5
elif month == 8:
Dusk = 21
Dawn = 5
elif month == 9:
Dusk = 21
Dawn = 6
elif month == 10:
Dusk = 20
Dawn = 6
elif month == 11:
Dusk = 19
Dawn = 6
elif month == 12:
Dusk = 18
Dawn = 7
if int(hour) >= Dusk or int(hour) <= Dawn:
Offline = True
MinArea = OldMinArea - (OldMinArea * 0.1)
Mode = 'Camera Offline'
else:
Offline = False
if Mode != 'Video':
Mode = OldMode
MinArea = OldMinArea
# grab the raw NumPy array representing the image - this array
# will be 3D, representing the width, height, and # of channels
image = frame.array
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Define a Region of Interest (ROI)
if Zone == 'PropertyBoundary':
TopOfHedge = 395
Pavement = 385
GatePillarTop = 390
pts = np.array([[60,400],[75,400],[200,390],[239,GatePillarTop-1],[242,Pavement+40],[635,Pavement],[635,479],[60,479]], np.int32)
gray = roi(gray,[pts])
cv2.polylines(image, [pts], True, (0,0,255), 1)
elif Zone == 'Road':
pts = np.array([[250,385],[83,395],[83,370],[635,300],[635,380],[252,425]],np.int32)
gray = roi(gray,[pts])
cv2.polylines(image, [pts], True, (0,0,255), 1)
#vertices = np.array([[5,280],[635,478]])
elif Zone == 'RoadCount':
pts = np.array([[400,365],[480,395],[350,420],[00,420]],np.int32)
gray = roi(gray,[pts])
cv2.polylines(image, [pts], True, (0,0,255), 1)
blurred = cv2.GaussianBlur(gray, (21, 21), 0) # smooth out every 21x21 pixel region
# apply Canny edge detection using a wide threshold, tight
# threshold, and automatically determined threshold
wide = cv2.Canny(blurred, 10, 200)
tight = cv2.Canny(blurred, 225, 250)
edged = auto_canny(blurred)
if avg is None:
avg = gray.copy().astype("float")
rawCapture.truncate(0)
continue
# Update the average frame with the new grayscale image
# Less weightage is given to the new grayscale image
cv2.accumulateWeighted(gray, avg, 0.5)
# compute the absolute difference between the current frame and first frame
frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
return_flag,thresh = cv2.threshold(frameDelta, ThreshValue,255, cv2.THRESH_BINARY)
# dilate the thresholded image to fill in holes, then find contours on thresholded image
dilated = cv2.dilate(thresh, None, iterations=2)
(im2,cnts,hierachy) = cv2.findContours(dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# People tend to be a vertical orientated rectangle, cars are horizontal orientated rectangle.
# ******************************************
if Method == 'PyImage':
# ******************************************
area = 0
if lp > 10: # Dont start snapping if just started
for c in cnts:
area = cv2.contourArea(c)
try:
if area < MinArea:
#print 'C is too small' + str(c) + ' ' + str(int(cv2.contourArea(c)))
if text == "Occupied":
text = 'Finished'
continue
else:
print 'Found a contour ' + str(area)
if area > MaxArea:MaxArea = area
area_List.append(area)
OccupiedCount += 1
text = "Occupied"
if int(area) > int(OldContour):
# compute the bounding box for the contour, draw it on the frame,
# and update the text
(x, y, w, h) = cv2.boundingRect(c)
try:
print ' Box is ' + str(x) + ',' + str(y) + ',' + str(w) + ',' + str(h)
cv2.rectangle(image, (x, y), (x + w, y + h), (0,255,0), 2)
#cv2.rectangle(image, (x,y), (x+10 + w, y +10 + h), (0,255,0),-1,1)
#cv2.putText(image, "Car", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255))
except:
pass
else:
OldContour = area
except Exception as e:
print 'Exception:' + str(e)
except:
pass
# ****************************************
# TAKE A SNAPSHOT EVERY X SECS IF OCCUPIED
# ****************************************
if PhotoToggle == 'On':
if text == "Occupied":
if int(MaxArea) >= int(area):
camera.capture('/home/pi/Desktop/snapshots/Day' + str(DayNumber) + '/'+ str(ts) + '_' + str(MaxArea) + '.jpg')
Snapshots += 1
Mode = 'Taking Photo'
if Snapshots > MaxSnapshots: # Dont store more than 50 pics
Snapshots = 1
else:
Snapshots = 1
if len(area_List) > 0:
AvgArea = sum(area_List) / float(len(area_List))
else:
AvgArea = 0
else:
AvgArea = 0
if Offline:
cv2.putText(image, "Front of House: {}".format(text), (10, 20),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1)
cv2.putText(image, "Mode: {}".format(Mode), (10, 40),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1)
cv2.putText(image, ts, (330,20), cv2.FONT_HERSHEY_SIMPLEX,0.55, (255, 255, 255), 1)
cv2.putText(thresh, "ThreshValue: " + str(ThreshValue), (10, 20),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1)
cv2.putText(image, "Occupied #: " + str(OccupiedCount), (10, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1)
cv2.putText(image, "Zone: " + str(Zone), (10, 80),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1)
cv2.putText(image, "Frame #: " + str(lp), (330, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1)
cv2.putText(image, "Photo Toggle (f): " + str(PhotoToggle), (330, 80),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1)
cv2.putText(image, "MaxArea: {}".format(str(MaxArea)), (330,40), cv2.FONT_HERSHEY_SIMPLEX,0.55, (255, 255, 255), 1)
cv2.putText(image, "Area: {}".format(str(area)), (510,40), cv2.FONT_HERSHEY_SIMPLEX,0.55, (255, 255, 255), 1)
cv2.putText(image, "Avg: {}".format(str(AvgArea)), (510,60), cv2.FONT_HERSHEY_SIMPLEX,0.55, (255, 255, 255), 1)
else:
cv2.putText(image, "Front of House: {}".format(text), (10, 20),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
cv2.putText(image, "Mode: {}".format(Mode), (10, 40),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
cv2.putText(image, ts, (330,20), cv2.FONT_HERSHEY_SIMPLEX,0.55, (0, 0, 255), 1)
cv2.putText(image, "MaxArea: {}".format(str(MaxArea)), (330,40), cv2.FONT_HERSHEY_SIMPLEX,0.55, (0, 0, 255), 1)
cv2.putText(image, "Area: {}".format(str(area)), (510,40), cv2.FONT_HERSHEY_SIMPLEX,0.55, (0, 0, 255), 1)
cv2.putText(frameDelta, "Delta: {}".format(ThreshValue), (10, 20),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
cv2.putText(image, "Occupied #: " + str(OccupiedCount), (10, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
cv2.putText(image, "Zone: " + str(Zone), (10, 80),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
cv2.putText(image, "Frame #: " + str(lp), (330, 60),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
cv2.putText(image, "Photo Toggle (f): " + str(PhotoToggle), (330, 80),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 1)
cv2.putText(image, "Avg: {}".format(str(AvgArea)), (510,60), cv2.FONT_HERSHEY_SIMPLEX,0.55, (0, 0, 255), 1)
if text == "Occupied":
if Alert == True:
cv2.putText(image, "OCCUPIED!", (105,170), cv2.FONT_HERSHEY_SIMPLEX,2.55, (0, 0, 255), 9)
Alert = False
elif Alert == False:
Alert = True
# Show the frame and record if the user presses a key
# *********************************
# RECORD VIDEO
# *********************************
#if Offline == False:
if VideoToggle == 'On':
if VideoFileName == None:
print 'started'
hour = datetime.datetime.now().hour
minute = datetime.datetime.now().minute
VideoFileName = '/home/pi/Desktop/snapshots/Day' + str(DayNumber) + '/' + str(hour) + str(minute) + '.h264'
camera.start_preview()
camera.start_recording(VideoFileName)
else:
print 'Recording'
if DEBUG:
cv2.namedWindow("Delta", cv2.WINDOW_NORMAL)
cv2.resizeWindow("Delta", 320,240)
cv2.imshow("Delta", frameDelta)
#cv2.namedWindow("ROI", cv2.WINDOW_NORMAL)
#cv2.resizeWindow("ROI", 320,240)
#cv2.imshow("ROI", ROI)
cv2.namedWindow("dilated", cv2.WINDOW_NORMAL)
cv2.resizeWindow("dilated", 320,240)
cv2.imshow("dilated",dilated)
#cv2.namedWindow("Thresh", cv2.WINDOW_NORMAL)
#cv2.resizeWindow("Thresh", 320,240)
#cv2.imshow("Thresh", thresh)
cv2.namedWindow("Security Feed", cv2.WINDOW_NORMAL)
if DEBUG: cv2.resizeWindow("Security Feed", 320,240)
cv2.imshow("Security Feed",image)
#cv2.namedWindow("Gray", cv2.WINDOW_NORMAL)
#cv2.resizeWindow("Gray", 320,240)
#cv2.imshow("Gray", gray)
#v2.namedWindow("Edge", cv2.WINDOW_NORMAL)
#cv2.resizeWindow("Edge", 320,240)
#cv2.imshow("Edge", edged)
# show the frame
#cv2.imshow("Office", image)
key = cv2.waitKey(1) & 0xFF
# clear the stream in preparation for the next frame
rawCapture.truncate(0)
# if the `q` key was pressed, break from the loop
if key == ord("q") or key == 27:
break
elif key == ord("r"):
Zone = 'Road'
lp = 0
MinArea = 800
AvgArea = 0
MaxArea = 0
OccupiedCount = 0
area_List = []
InCount = 0
OutCount = 0
elif key == ord("p"):
Zone = 'PropertyBoundary'
MinArea = 450
lp = 0
AvgArea = 0
MaxArea = 0
OccupiedCount = 0
area_List = []
InCount = 0
OutCount = 0
elif key == ord("f"):
lp = 0
if PhotoToggle == 'On':
PhotoToggle = 'Off'
else:
PhotoToggle = 'On'
elif key == ord("v"):
if VideoToggle == 'On':
VideoToggle = 'Off'
camera.stop_recording()
camera.stop_preview()
Mode = OldMode
elif VideoToggle == 'Off':
VideoToggle = 'On'
Mode = 'Video'
VideoFileName = None
# cleanup the camera and close any open windows
cv2.VideoCapture(0).release()
#out.release()
cv2.destroyAllWindows()
return
# ************************
if __name__ == "__main__":
# ************************
DEBUG = 0
Mode = 'Live'
Method = 'PyImage'
#Zone = 'Road'
#Zone = 'RoadCount'
Zone = 'PropertyBoundary'
Main(Mode,DEBUG,Method,Zone)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment