· 4 years ago · Apr 25, 2021, 12:08 AM
1# USAGE
2# python pi_surveillance.py --conf conf.json
3
4# import the necessary packages
5from pyimagesearch.tempimage import TempImage
6from picamera.array import PiRGBArray
7from picamera import PiCamera
8import argparse
9import warnings
10import datetime
11import dropbox
12import imutils
13import json
14import time
15import cv2
16
17# time strings
18td = datetime.datetime.now().strftime("%d %m %Y")
19tfd = datetime.datetime.now().strftime("%H:%M %d-%m-%Y")
20
21# construct the argument parser and parse the arguments
22ap = argparse.ArgumentParser()
23ap.add_argument("-c", "--conf", required=True,
24 help="path to the JSON configuration file")
25args = vars(ap.parse_args())
26
27# filter warnings, load the configuration and initialize the Dropbox
28# client
29warnings.filterwarnings("ignore")
30conf = json.load(open(args["conf"]))
31client = None
32
33# pushover
34if conf['use_pushover']:
35 # imports pushover
36 import pushover.init as PO_init
37 import pushover.Client as PO_Client
38
39 # pushover credentials
40 PO_API_TOKEN = conf['pushover_api_token']
41 PO_USER_TOKEN = conf['pushover_api_token']
42 print("[SUCCES] pushover linked")
43
44# check to see if the Dropbox should be used
45if conf["use_dropbox"]:
46 # connect to dropbox and start the session authorization process
47 client = dropbox.Dropbox(conf["dropbox_access_token"])
48 print("[SUCCESS] dropbox account linked")
49
50
51# find the weekday
52def find_weekday():
53 days = ["Monday", "Tuesday", "Wedensday", "Thursday", "Friday", "Saturday", "Sunday"]
54 weekday = datetime.datetime.weekday(datetime.datetime.now())
55 weekday = days[weekday]
56 return weekday
57
58
59# initialize the camera and grab a reference to the raw camera capture
60camera = PiCamera()
61camera.resolution = tuple(conf["resolution"])
62camera.led = True
63camera.exposure_mode = "night"
64camera.framerate = conf["fps"]
65rawCapture = PiRGBArray(camera, size=tuple(conf["resolution"]))
66
67# allow the camera to warmup, then initialize the average frame, last
68# uploaded timestamp, and frame motion counter
69print("[INFO] warming up...")
70time.sleep(conf["camera_warmup_time"])
71avg = None
72lastUploaded = datetime.datetime.now()
73motionCounter = 0
74
75# Capture counter
76motionCaptures = 0
77
78# capture frames from the camera
79for f in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
80 # grab the raw NumPy array representing the image and initialize
81 # the timestamp and occupied/unoccupied text
82 frame = f.array
83 timestamp = datetime.datetime.now()
84 text = "Unoccupied"
85
86 # resize the frame, convert it to grayscale, and blur it
87 frame = imutils.resize(frame, width=500)
88 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
89 gray = cv2.GaussianBlur(gray, (21, 21), 0)
90
91 # if the average frame is None, initialize it
92 if avg is None:
93 print("[INFO] starting background model...")
94 avg = gray.copy().astype("float")
95 rawCapture.truncate(0)
96 continue
97
98 # accumulate the weighted average between the current frame and
99 # previous frames, then compute the difference between the current
100 # frame and running average
101 cv2.accumulateWeighted(gray, avg, 0.5)
102 frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
103
104 # threshold the delta image, dilate the thresholded image to fill
105 # in holes, then find contours on thresholded image
106 thresh = cv2.threshold(frameDelta, conf["delta_thresh"], 255,
107 cv2.THRESH_BINARY)[1]
108 thresh = cv2.dilate(thresh, None, iterations=2)
109 cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
110 cv2.CHAIN_APPROX_SIMPLE)
111 cnts = imutils.grab_contours(cnts)
112
113 # loop over the contours
114 for c in cnts:
115 # if the contour is too small, ignore it
116 if cv2.contourArea(c) < conf["min_area"]:
117 continue
118
119 # compute the bounding box for the contour, draw it on the frame,
120 # and update the text
121 (x, y, w, h) = cv2.boundingRect(c)
122 cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
123 text = "Occupied"
124
125 # draw the text and timestamp on the frame
126 ts = timestamp.strftime("%A %d %B %Y %I:%M:%S%p")
127 cv2.putText(frame, "Room Status: {}".format(text), (10, 20),
128 cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
129 cv2.putText(frame, ts, (10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX,
130 0.55, (0, 0, 255), 1)
131
132 # check to see if the room is occupied
133 if text == "Occupied":
134 # check to see if enough time has passed between uploads
135 if (timestamp - lastUploaded).seconds >= conf["min_upload_seconds"]:
136 # increment the motion counter
137 motionCounter += 1
138
139 # check to see if the number of frames with consistent motion is
140 # high enough
141 if motionCounter >= conf["min_motion_frames"]:
142 # check to se if pushover should be used
143 if conf["use_pushover"]:
144 PO_init(PO_API_TOKEN)
145 PO_CLIENT = PO_Client(PO_API_TOKEN, PO_USER_TOKEN)
146
147 # write the image to temporary file
148 t = TempImage()
149 cv2.imwrite(t.path, frame)
150
151 # sends the notification and a image to api
152 print("[PUSHOVER] Sending notification")
153 PO_Client.send_message(
154 message="Camera captured something {timestamp}",
155 title="PiCamera Caption",
156 attachment=open(t.path, "rb")
157 )
158 t.cleanup()
159
160 # check to see if dropbox should be used
161 if conf["use_dropbox"]:
162 # write the image to temporary file
163 t = TempImage()
164 cv2.imwrite(t.path, frame)
165
166 # upload the image to Dropbox and cleanup the temporary image
167 print("[UPLOAD] {}".format(ts))
168 path = "/{base_path}/{timestamp}.jpg".format(
169 base_path=conf["dropbox_base_path"], timestamp=ts)
170 client.files_upload(open(t.path, "rb").read(), path)
171 #
172 t.cleanup()
173
174 # update the last uploaded timestamp and reset the motion
175 # counter
176 lastUploaded = timestamp
177 motionCounter = 0
178
179 # increment the motion captures
180 motionCaptures += 1
181 print("[CAPTURES] capture counter: {}".format(motionCaptures))
182
183 # call function if motion captures reaches 2
184
185 if motionCaptures == 2:
186 print("[COUNTER REACHED]")
187
188# otherwise, the room is not occupied
189else:
190 motionCounter = 0
191
192# check to see if the frames should be displayed to screen
193if conf["show_video"]:
194 # display the security feed
195 cv2.imshow("Security Feed", frame)
196 key = cv2.waitKey(1) & 0xFF
197
198 # if the `q` key is pressed, break from the lop
199 if key == ord("q"):
200 print("[STOPPING]")
201 break
202
203# clear the stream in preparation for the next frame
204rawCapture.truncate(0)