mirror of
https://github.com/rendies/People-Counting-in-Real-Time.git
synced 2025-07-01 14:19:16 +07:00
added main project files
This commit is contained in:
parent
e313324b9d
commit
d35d69148c
8
Log.csv
Normal file
8
Log.csv
Normal file
|
@ -0,0 +1,8 @@
|
|||
"End Time","In","Out","Total Inside"
|
||||
"2020-08-18 20:46:03.573500","1","1","4"
|
||||
"","2","2",""
|
||||
"","3","3",""
|
||||
"","4","",""
|
||||
"","5","",""
|
||||
"","6","",""
|
||||
"","7","",""
|
|
368
Run.py
Normal file
368
Run.py
Normal file
|
@ -0,0 +1,368 @@
|
|||
from mylib.centroidtracker import CentroidTracker
|
||||
from mylib.trackableobject import TrackableObject
|
||||
from imutils.video import VideoStream
|
||||
from imutils.video import FPS
|
||||
from mylib.mailer import Mailer
|
||||
import time, schedule, csv
|
||||
import numpy as np
|
||||
import argparse
|
||||
import imutils
|
||||
import time
|
||||
import dlib
|
||||
import cv2, datetime
|
||||
from itertools import zip_longest
|
||||
|
||||
t0 = time.time()
|
||||
|
||||
#===============================================================================
|
||||
""" Optional features """
|
||||
#===============================================================================
|
||||
# Enter mail below to receive real-time email alerts
|
||||
# e.g., 'email@gmail.com'
|
||||
MAIL = ''
|
||||
|
||||
# ON/OFF for mail feature. Enter True to turn on the email alert feature.
|
||||
ALERT = False
|
||||
|
||||
# Simple log to log the counting data
|
||||
Log = False
|
||||
# Auto run/Schedule the software to run at your desired time
|
||||
Scheduler = False
|
||||
# Auto stop the software after certain a time/hours
|
||||
Timer = False
|
||||
#===============================================================================
|
||||
#===============================================================================
|
||||
|
||||
|
||||
def run():
|
||||
|
||||
# construct the argument parse and parse the arguments
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("-p", "--prototxt", required=False,
|
||||
help="path to Caffe 'deploy' prototxt file")
|
||||
ap.add_argument("-m", "--model", required=True,
|
||||
help="path to Caffe pre-trained model")
|
||||
ap.add_argument("-i", "--input", type=str,
|
||||
help="path to optional input video file")
|
||||
ap.add_argument("-o", "--output", type=str,
|
||||
help="path to optional output video file")
|
||||
# confidence default 0.4
|
||||
ap.add_argument("-c", "--confidence", type=float, default=0.4,
|
||||
help="minimum probability to filter weak detections")
|
||||
ap.add_argument("-s", "--skip-frames", type=int, default=30,
|
||||
help="# of skip frames between detections")
|
||||
args = vars(ap.parse_args())
|
||||
|
||||
# initialize the list of class labels MobileNet SSD was trained to
|
||||
# detect
|
||||
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
|
||||
"bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
|
||||
"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
|
||||
"sofa", "train", "tvmonitor"]
|
||||
|
||||
# load our serialized model from disk
|
||||
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])
|
||||
|
||||
# if a video path was not supplied, grab a reference to the ip camera
|
||||
if not args.get("input", False):
|
||||
print("[INFO] Starting the live stream..")
|
||||
# the following is an ip camera url example
|
||||
# just enter your camera url and it should work
|
||||
url = 'http://191.138.0.100:8040/video'
|
||||
vs = VideoStream(url).start()
|
||||
time.sleep(2.0)
|
||||
|
||||
# otherwise, grab a reference to the video file
|
||||
else:
|
||||
vs = cv2.VideoCapture(args["input"])
|
||||
|
||||
# initialize the video writer (we'll instantiate later if need be)
|
||||
writer = None
|
||||
|
||||
# initialize the frame dimensions (we'll set them as soon as we read
|
||||
# the first frame from the video)
|
||||
W = None
|
||||
H = None
|
||||
|
||||
# instantiate our centroid tracker, then initialize a list to store
|
||||
# each of our dlib correlation trackers, followed by a dictionary to
|
||||
# map each unique object ID to a TrackableObject
|
||||
ct = CentroidTracker(maxDisappeared=40, maxDistance=50)
|
||||
trackers = []
|
||||
trackableObjects = {}
|
||||
|
||||
# initialize the total number of frames processed thus far, along
|
||||
# with the total number of objects that have moved either up or down
|
||||
totalFrames = 0
|
||||
totalDown = 0
|
||||
totalUp = 0
|
||||
x = []
|
||||
empty=[]
|
||||
empty1=[]
|
||||
|
||||
# start the frames per second throughput estimator
|
||||
fps = FPS().start()
|
||||
|
||||
# loop over frames from the video stream
|
||||
while True:
|
||||
# grab the next frame and handle if we are reading from either
|
||||
# VideoCapture or VideoStream
|
||||
frame = vs.read()
|
||||
frame = frame[1] if args.get("input", False) else frame
|
||||
|
||||
# if we are viewing a video and we did not grab a frame then we
|
||||
# have reached the end of the video
|
||||
if args["input"] is not None and frame is None:
|
||||
break
|
||||
|
||||
# resize the frame to have a maximum width of 500 pixels (the
|
||||
# less data we have, the faster we can process it), then convert
|
||||
# the frame from BGR to RGB for dlib
|
||||
frame = imutils.resize(frame, width=500)
|
||||
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
|
||||
# if the frame dimensions are empty, set them
|
||||
if W is None or H is None:
|
||||
(H, W) = frame.shape[:2]
|
||||
|
||||
# if we are supposed to be writing a video to disk, initialize
|
||||
# the writer
|
||||
if args["output"] is not None and writer is None:
|
||||
fourcc = cv2.VideoWriter_fourcc(*"MJPG")
|
||||
writer = cv2.VideoWriter(args["output"], fourcc, 30,
|
||||
(W, H), True)
|
||||
|
||||
# initialize the current status along with our list of bounding
|
||||
# box rectangles returned by either (1) our object detector or
|
||||
# (2) the correlation trackers
|
||||
status = "Waiting"
|
||||
rects = []
|
||||
|
||||
# check to see if we should run a more computationally expensive
|
||||
# object detection method to aid our tracker
|
||||
if totalFrames % args["skip_frames"] == 0:
|
||||
# set the status and initialize our new set of object trackers
|
||||
status = "Detecting"
|
||||
trackers = []
|
||||
|
||||
# convert the frame to a blob and pass the blob through the
|
||||
# network and obtain the detections
|
||||
blob = cv2.dnn.blobFromImage(frame, 0.007843, (W, H), 127.5)
|
||||
net.setInput(blob)
|
||||
detections = net.forward()
|
||||
|
||||
# loop over the detections
|
||||
for i in np.arange(0, detections.shape[2]):
|
||||
# extract the confidence (i.e., probability) associated
|
||||
# with the prediction
|
||||
confidence = detections[0, 0, i, 2]
|
||||
|
||||
# filter out weak detections by requiring a minimum
|
||||
# confidence
|
||||
if confidence > args["confidence"]:
|
||||
# extract the index of the class label from the
|
||||
# detections list
|
||||
idx = int(detections[0, 0, i, 1])
|
||||
|
||||
# if the class label is not a person, ignore it
|
||||
if CLASSES[idx] != "person":
|
||||
continue
|
||||
|
||||
# compute the (x, y)-coordinates of the bounding box
|
||||
# for the object
|
||||
box = detections[0, 0, i, 3:7] * np.array([W, H, W, H])
|
||||
(startX, startY, endX, endY) = box.astype("int")
|
||||
|
||||
|
||||
# construct a dlib rectangle object from the bounding
|
||||
# box coordinates and then start the dlib correlation
|
||||
# tracker
|
||||
tracker = dlib.correlation_tracker()
|
||||
rect = dlib.rectangle(startX, startY, endX, endY)
|
||||
tracker.start_track(rgb, rect)
|
||||
|
||||
# add the tracker to our list of trackers so we can
|
||||
# utilize it during skip frames
|
||||
trackers.append(tracker)
|
||||
|
||||
# otherwise, we should utilize our object *trackers* rather than
|
||||
# object *detectors* to obtain a higher frame processing throughput
|
||||
else:
|
||||
# loop over the trackers
|
||||
for tracker in trackers:
|
||||
# set the status of our system to be 'tracking' rather
|
||||
# than 'waiting' or 'detecting'
|
||||
status = "Tracking"
|
||||
|
||||
# update the tracker and grab the updated position
|
||||
tracker.update(rgb)
|
||||
pos = tracker.get_position()
|
||||
|
||||
# unpack the position object
|
||||
startX = int(pos.left())
|
||||
startY = int(pos.top())
|
||||
endX = int(pos.right())
|
||||
endY = int(pos.bottom())
|
||||
|
||||
# add the bounding box coordinates to the rectangles list
|
||||
rects.append((startX, startY, endX, endY))
|
||||
|
||||
# draw a horizontal line in the center of the frame -- once an
|
||||
# object crosses this line we will determine whether they were
|
||||
# moving 'up' or 'down'
|
||||
#cv2.line(frame, (0, H // 2), (W, H // 2), (0, 255, 255), 2)
|
||||
cv2.line(frame, (0, H // 2), (W, H // 2), (255, 255, 255), 3)
|
||||
cv2.putText(frame, "-Prediction border - Entrance-", (10, H - ((i * 20) + 200)),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
|
||||
|
||||
# use the centroid tracker to associate the (1) old object
|
||||
# centroids with (2) the newly computed object centroids
|
||||
objects = ct.update(rects)
|
||||
|
||||
# loop over the tracked objects
|
||||
for (objectID, centroid) in objects.items():
|
||||
# check to see if a trackable object exists for the current
|
||||
# object ID
|
||||
to = trackableObjects.get(objectID, None)
|
||||
|
||||
# if there is no existing trackable object, create one
|
||||
if to is None:
|
||||
to = TrackableObject(objectID, centroid)
|
||||
|
||||
# otherwise, there is a trackable object so we can utilize it
|
||||
# to determine direction
|
||||
else:
|
||||
# the difference between the y-coordinate of the *current*
|
||||
# centroid and the mean of *previous* centroids will tell
|
||||
# us in which direction the object is moving (negative for
|
||||
# 'up' and positive for 'down')
|
||||
y = [c[1] for c in to.centroids]
|
||||
direction = centroid[1] - np.mean(y)
|
||||
to.centroids.append(centroid)
|
||||
|
||||
# check to see if the object has been counted or not
|
||||
if not to.counted:
|
||||
# if the direction is negative (indicating the object
|
||||
# is moving up) AND the centroid is above the center
|
||||
# line, count the object
|
||||
if direction < 0 and centroid[1] < H // 2:
|
||||
totalUp += 1
|
||||
empty.append(totalUp)
|
||||
to.counted = True
|
||||
|
||||
# if the direction is positive (indicating the object
|
||||
# is moving down) AND the centroid is below the
|
||||
# center line, count the object
|
||||
elif direction > 0 and centroid[1] > H // 2:
|
||||
totalDown += 1
|
||||
empty1.append(totalDown)
|
||||
#print(empty1[-1])
|
||||
x = []
|
||||
# compute the sum of total people inside
|
||||
x.append(len(empty1)-len(empty))
|
||||
#print("Total people inside:", x)
|
||||
# Optimise number below: 10, 50, 100, etc., indicate the max. people inside limit
|
||||
# if the limit exceeds, send an email alert
|
||||
people_limit = 10
|
||||
if sum(x) == people_limit:
|
||||
if ALERT:
|
||||
print("[INFO] Sending email alert..")
|
||||
Mailer().send(MAIL)
|
||||
print("[INFO] Alert sent")
|
||||
|
||||
to.counted = True
|
||||
|
||||
|
||||
# store the trackable object in our dictionary
|
||||
trackableObjects[objectID] = to
|
||||
|
||||
# draw both the ID of the object and the centroid of the
|
||||
# object on the output frame
|
||||
text = "ID {}".format(objectID)
|
||||
cv2.putText(frame, text, (centroid[0] - 10, centroid[1] - 10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
|
||||
cv2.circle(frame, (centroid[0], centroid[1]), 4, (255, 255, 255), -1)
|
||||
|
||||
# construct a tuple of information we will be displaying on the
|
||||
info = [
|
||||
("Up", totalUp),
|
||||
("Down", totalDown),
|
||||
("Status", status),
|
||||
]
|
||||
|
||||
info2 = [
|
||||
("Total people inside", x),
|
||||
]
|
||||
|
||||
# display the output
|
||||
for (i, (k, v)) in enumerate(info):
|
||||
text = "{}: {}".format(k, v)
|
||||
cv2.putText(frame, text, (10, H - ((i * 20) + 20)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
|
||||
|
||||
for (i, (k, v)) in enumerate(info2):
|
||||
text = "{}: {}".format(k, v)
|
||||
cv2.putText(frame, text, (270, H - ((i * 20) + 60)),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
|
||||
|
||||
# Simple log to save data at end of the day
|
||||
if Log:
|
||||
datetimee = [datetime.datetime.now()]
|
||||
d = [datetimee, empty1, empty, x]
|
||||
export_data = zip_longest(*d, fillvalue = '')
|
||||
|
||||
with open('Log.csv', 'w', newline='') as myfile:
|
||||
wr = csv.writer(myfile, quoting=csv.QUOTE_ALL)
|
||||
wr.writerow(("End Time", "In", "Out", "Total Inside"))
|
||||
wr.writerows(export_data)
|
||||
|
||||
|
||||
# show the output frame
|
||||
cv2.imshow("Real-Time Monitoring/Analysis Window", frame)
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
|
||||
# if the `q` key was pressed, break from the loop
|
||||
if key == ord("q"):
|
||||
break
|
||||
|
||||
# increment the total number of frames processed thus far and
|
||||
# then update the FPS counter
|
||||
totalFrames += 1
|
||||
fps.update()
|
||||
|
||||
if Timer:
|
||||
# Automatic timer to stop the live stream. Set to 8 hours (28800s).
|
||||
t1 = time.time()
|
||||
num_seconds=(t1-t0)
|
||||
if num_seconds > 28800:
|
||||
break
|
||||
|
||||
# stop the timer and display FPS information
|
||||
fps.stop()
|
||||
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
|
||||
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
|
||||
|
||||
|
||||
# if we are not using a video file, stop the camera video stream
|
||||
if not args.get("input", False):
|
||||
vs.stop()
|
||||
|
||||
# otherwise, release the video file pointer
|
||||
else:
|
||||
vs.release()
|
||||
|
||||
# close any open windows
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
##learn more about different schedules here: https://pypi.org/project/schedule/
|
||||
if Scheduler:
|
||||
##Runs for every 1 second
|
||||
#schedule.every(1).seconds.do(run)
|
||||
##Runs at every day (9:00 am). You can change it.
|
||||
schedule.every().day.at("9:00").do(run)
|
||||
|
||||
while 1:
|
||||
schedule.run_pending()
|
||||
|
||||
else:
|
||||
run()
|
BIN
mobilenet_ssd/MobileNetSSD_deploy.caffemodel
Normal file
BIN
mobilenet_ssd/MobileNetSSD_deploy.caffemodel
Normal file
Binary file not shown.
1912
mobilenet_ssd/MobileNetSSD_deploy.prototxt
Normal file
1912
mobilenet_ssd/MobileNetSSD_deploy.prototxt
Normal file
File diff suppressed because it is too large
Load Diff
163
mylib/centroidtracker.py
Normal file
163
mylib/centroidtracker.py
Normal file
|
@ -0,0 +1,163 @@
|
|||
# import the necessary packages
|
||||
from scipy.spatial import distance as dist
|
||||
from collections import OrderedDict
|
||||
import numpy as np
|
||||
|
||||
class CentroidTracker:
|
||||
def __init__(self, maxDisappeared=50, maxDistance=50):
|
||||
# initialize the next unique object ID along with two ordered
|
||||
# dictionaries used to keep track of mapping a given object
|
||||
# ID to its centroid and number of consecutive frames it has
|
||||
# been marked as "disappeared", respectively
|
||||
self.nextObjectID = 0
|
||||
self.objects = OrderedDict()
|
||||
self.disappeared = OrderedDict()
|
||||
|
||||
# store the number of maximum consecutive frames a given
|
||||
# object is allowed to be marked as "disappeared" until we
|
||||
# need to deregister the object from tracking
|
||||
self.maxDisappeared = maxDisappeared
|
||||
|
||||
# store the maximum distance between centroids to associate
|
||||
# an object -- if the distance is larger than this maximum
|
||||
# distance we'll start to mark the object as "disappeared"
|
||||
self.maxDistance = maxDistance
|
||||
|
||||
def register(self, centroid):
|
||||
# when registering an object we use the next available object
|
||||
# ID to store the centroid
|
||||
self.objects[self.nextObjectID] = centroid
|
||||
self.disappeared[self.nextObjectID] = 0
|
||||
self.nextObjectID += 1
|
||||
|
||||
def deregister(self, objectID):
|
||||
# to deregister an object ID we delete the object ID from
|
||||
# both of our respective dictionaries
|
||||
del self.objects[objectID]
|
||||
del self.disappeared[objectID]
|
||||
|
||||
def update(self, rects):
|
||||
# check to see if the list of input bounding box rectangles
|
||||
# is empty
|
||||
if len(rects) == 0:
|
||||
# loop over any existing tracked objects and mark them
|
||||
# as disappeared
|
||||
for objectID in list(self.disappeared.keys()):
|
||||
self.disappeared[objectID] += 1
|
||||
|
||||
# if we have reached a maximum number of consecutive
|
||||
# frames where a given object has been marked as
|
||||
# missing, deregister it
|
||||
if self.disappeared[objectID] > self.maxDisappeared:
|
||||
self.deregister(objectID)
|
||||
|
||||
# return early as there are no centroids or tracking info
|
||||
# to update
|
||||
return self.objects
|
||||
|
||||
# initialize an array of input centroids for the current frame
|
||||
inputCentroids = np.zeros((len(rects), 2), dtype="int")
|
||||
|
||||
# loop over the bounding box rectangles
|
||||
for (i, (startX, startY, endX, endY)) in enumerate(rects):
|
||||
# use the bounding box coordinates to derive the centroid
|
||||
cX = int((startX + endX) / 2.0)
|
||||
cY = int((startY + endY) / 2.0)
|
||||
inputCentroids[i] = (cX, cY)
|
||||
|
||||
# if we are currently not tracking any objects take the input
|
||||
# centroids and register each of them
|
||||
if len(self.objects) == 0:
|
||||
for i in range(0, len(inputCentroids)):
|
||||
self.register(inputCentroids[i])
|
||||
|
||||
# otherwise, are are currently tracking objects so we need to
|
||||
# try to match the input centroids to existing object
|
||||
# centroids
|
||||
else:
|
||||
# grab the set of object IDs and corresponding centroids
|
||||
objectIDs = list(self.objects.keys())
|
||||
objectCentroids = list(self.objects.values())
|
||||
|
||||
# compute the distance between each pair of object
|
||||
# centroids and input centroids, respectively -- our
|
||||
# goal will be to match an input centroid to an existing
|
||||
# object centroid
|
||||
D = dist.cdist(np.array(objectCentroids), inputCentroids)
|
||||
|
||||
# in order to perform this matching we must (1) find the
|
||||
# smallest value in each row and then (2) sort the row
|
||||
# indexes based on their minimum values so that the row
|
||||
# with the smallest value as at the *front* of the index
|
||||
# list
|
||||
rows = D.min(axis=1).argsort()
|
||||
|
||||
# next, we perform a similar process on the columns by
|
||||
# finding the smallest value in each column and then
|
||||
# sorting using the previously computed row index list
|
||||
cols = D.argmin(axis=1)[rows]
|
||||
|
||||
# in order to determine if we need to update, register,
|
||||
# or deregister an object we need to keep track of which
|
||||
# of the rows and column indexes we have already examined
|
||||
usedRows = set()
|
||||
usedCols = set()
|
||||
|
||||
# loop over the combination of the (row, column) index
|
||||
# tuples
|
||||
for (row, col) in zip(rows, cols):
|
||||
# if we have already examined either the row or
|
||||
# column value before, ignore it
|
||||
if row in usedRows or col in usedCols:
|
||||
continue
|
||||
|
||||
# if the distance between centroids is greater than
|
||||
# the maximum distance, do not associate the two
|
||||
# centroids to the same object
|
||||
if D[row, col] > self.maxDistance:
|
||||
continue
|
||||
|
||||
# otherwise, grab the object ID for the current row,
|
||||
# set its new centroid, and reset the disappeared
|
||||
# counter
|
||||
objectID = objectIDs[row]
|
||||
self.objects[objectID] = inputCentroids[col]
|
||||
self.disappeared[objectID] = 0
|
||||
|
||||
# indicate that we have examined each of the row and
|
||||
# column indexes, respectively
|
||||
usedRows.add(row)
|
||||
usedCols.add(col)
|
||||
|
||||
# compute both the row and column index we have NOT yet
|
||||
# examined
|
||||
unusedRows = set(range(0, D.shape[0])).difference(usedRows)
|
||||
unusedCols = set(range(0, D.shape[1])).difference(usedCols)
|
||||
|
||||
# in the event that the number of object centroids is
|
||||
# equal or greater than the number of input centroids
|
||||
# we need to check and see if some of these objects have
|
||||
# potentially disappeared
|
||||
if D.shape[0] >= D.shape[1]:
|
||||
# loop over the unused row indexes
|
||||
for row in unusedRows:
|
||||
# grab the object ID for the corresponding row
|
||||
# index and increment the disappeared counter
|
||||
objectID = objectIDs[row]
|
||||
self.disappeared[objectID] += 1
|
||||
|
||||
# check to see if the number of consecutive
|
||||
# frames the object has been marked "disappeared"
|
||||
# for warrants deregistering the object
|
||||
if self.disappeared[objectID] > self.maxDisappeared:
|
||||
self.deregister(objectID)
|
||||
|
||||
# otherwise, if the number of input centroids is greater
|
||||
# than the number of existing object centroids we need to
|
||||
# register each new input centroid as a trackable object
|
||||
else:
|
||||
for col in unusedCols:
|
||||
self.register(inputCentroids[col])
|
||||
|
||||
# return the set of trackable objects
|
||||
return self.objects
|
31
mylib/mailer.py
Normal file
31
mylib/mailer.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
import smtplib, ssl
|
||||
|
||||
class Mailer:
|
||||
|
||||
"""
|
||||
This script initiaties the email alert function.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
# Enter your email below. This email will be used to send alerts.
|
||||
# E.g., "email@gmail.com"
|
||||
self.EMAIL = ""
|
||||
# Enter the email password below. Note that the password varies if you have secured
|
||||
# 2 step verification turned on. You can refer the links below and create an application specific password.
|
||||
# Google mail has a guide here: https://myaccount.google.com/lesssecureapps
|
||||
# For 2 step verified accounts: https://support.google.com/accounts/answer/185833
|
||||
self.PASS = ""
|
||||
self.PORT = 465
|
||||
self.server = smtplib.SMTP_SSL('smtp.gmail.com', self.PORT)
|
||||
|
||||
def send(self, mail):
|
||||
self.server = smtplib.SMTP_SSL('smtp.gmail.com', self.PORT)
|
||||
self.server.login(self.EMAIL, self.PASS)
|
||||
# message to be sent
|
||||
SUBJECT = 'ALERT!'
|
||||
TEXT = f'People limit exceeded in your building!'
|
||||
message = 'Subject: {}\n\n{}'.format(SUBJECT, TEXT)
|
||||
|
||||
# sending the mail
|
||||
self.server.sendmail(self.EMAIL, mail, message)
|
||||
self.server.quit()
|
10
mylib/trackableobject.py
Normal file
10
mylib/trackableobject.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
class TrackableObject:
|
||||
def __init__(self, objectID, centroid):
|
||||
# store the object ID, then initialize a list of centroids
|
||||
# using the current centroid
|
||||
self.objectID = objectID
|
||||
self.centroids = [centroid]
|
||||
|
||||
# initialize a boolean used to indicate if the object has
|
||||
# already been counted or not
|
||||
self.counted = False
|
15
requirements.txt
Normal file
15
requirements.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
smtplib
|
||||
ssl
|
||||
time
|
||||
schedule
|
||||
csv
|
||||
numpy
|
||||
argparse
|
||||
imutils
|
||||
time
|
||||
dlib
|
||||
cv2
|
||||
datetime
|
||||
itertools
|
||||
scipy
|
||||
collections
|
BIN
videos/example_01.mp4
Normal file
BIN
videos/example_01.mp4
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user