diff --git a/Log.csv b/Log.csv new file mode 100644 index 0000000..6fcd4b5 --- /dev/null +++ b/Log.csv @@ -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","","" diff --git a/Run.py b/Run.py new file mode 100644 index 0000000..747a470 --- /dev/null +++ b/Run.py @@ -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() diff --git a/mobilenet_ssd/MobileNetSSD_deploy.caffemodel b/mobilenet_ssd/MobileNetSSD_deploy.caffemodel new file mode 100644 index 0000000..7104f06 Binary files /dev/null and b/mobilenet_ssd/MobileNetSSD_deploy.caffemodel differ diff --git a/mobilenet_ssd/MobileNetSSD_deploy.prototxt b/mobilenet_ssd/MobileNetSSD_deploy.prototxt new file mode 100644 index 0000000..fdc8126 --- /dev/null +++ b/mobilenet_ssd/MobileNetSSD_deploy.prototxt @@ -0,0 +1,1912 @@ +name: "MobileNet-SSD" +input: "data" +input_shape { + dim: 1 + dim: 3 + dim: 300 + dim: 300 +} +layer { + name: "conv0" + type: "Convolution" + bottom: "data" + top: "conv0" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 32 + pad: 1 + kernel_size: 3 + stride: 2 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv0/relu" + type: "ReLU" + bottom: "conv0" + top: "conv0" +} +layer { + name: "conv1/dw" + type: "Convolution" + bottom: "conv0" + top: "conv1/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 32 + pad: 1 + kernel_size: 3 + group: 32 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv1/dw/relu" + type: "ReLU" + bottom: "conv1/dw" + top: "conv1/dw" +} +layer { + name: "conv1" + type: "Convolution" + bottom: "conv1/dw" + top: "conv1" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 64 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv1/relu" + type: "ReLU" + bottom: "conv1" + top: "conv1" +} +layer { + name: "conv2/dw" + type: "Convolution" + bottom: "conv1" + top: "conv2/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 64 + pad: 1 + kernel_size: 3 + stride: 2 + group: 64 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv2/dw/relu" + type: "ReLU" + bottom: "conv2/dw" + top: "conv2/dw" +} +layer { + name: "conv2" + type: "Convolution" + bottom: "conv2/dw" + top: "conv2" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 128 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv2/relu" + type: "ReLU" + bottom: "conv2" + top: "conv2" +} +layer { + name: "conv3/dw" + type: "Convolution" + bottom: "conv2" + top: "conv3/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 128 + pad: 1 + kernel_size: 3 + group: 128 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv3/dw/relu" + type: "ReLU" + bottom: "conv3/dw" + top: "conv3/dw" +} +layer { + name: "conv3" + type: "Convolution" + bottom: "conv3/dw" + top: "conv3" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 128 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv3/relu" + type: "ReLU" + bottom: "conv3" + top: "conv3" +} +layer { + name: "conv4/dw" + type: "Convolution" + bottom: "conv3" + top: "conv4/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 128 + pad: 1 + kernel_size: 3 + stride: 2 + group: 128 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv4/dw/relu" + type: "ReLU" + bottom: "conv4/dw" + top: "conv4/dw" +} +layer { + name: "conv4" + type: "Convolution" + bottom: "conv4/dw" + top: "conv4" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 256 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv4/relu" + type: "ReLU" + bottom: "conv4" + top: "conv4" +} +layer { + name: "conv5/dw" + type: "Convolution" + bottom: "conv4" + top: "conv5/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + group: 256 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv5/dw/relu" + type: "ReLU" + bottom: "conv5/dw" + top: "conv5/dw" +} +layer { + name: "conv5" + type: "Convolution" + bottom: "conv5/dw" + top: "conv5" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 256 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv5/relu" + type: "ReLU" + bottom: "conv5" + top: "conv5" +} +layer { + name: "conv6/dw" + type: "Convolution" + bottom: "conv5" + top: "conv6/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + stride: 2 + group: 256 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv6/dw/relu" + type: "ReLU" + bottom: "conv6/dw" + top: "conv6/dw" +} +layer { + name: "conv6" + type: "Convolution" + bottom: "conv6/dw" + top: "conv6" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv6/relu" + type: "ReLU" + bottom: "conv6" + top: "conv6" +} +layer { + name: "conv7/dw" + type: "Convolution" + bottom: "conv6" + top: "conv7/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + group: 512 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv7/dw/relu" + type: "ReLU" + bottom: "conv7/dw" + top: "conv7/dw" +} +layer { + name: "conv7" + type: "Convolution" + bottom: "conv7/dw" + top: "conv7" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv7/relu" + type: "ReLU" + bottom: "conv7" + top: "conv7" +} +layer { + name: "conv8/dw" + type: "Convolution" + bottom: "conv7" + top: "conv8/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + group: 512 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv8/dw/relu" + type: "ReLU" + bottom: "conv8/dw" + top: "conv8/dw" +} +layer { + name: "conv8" + type: "Convolution" + bottom: "conv8/dw" + top: "conv8" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv8/relu" + type: "ReLU" + bottom: "conv8" + top: "conv8" +} +layer { + name: "conv9/dw" + type: "Convolution" + bottom: "conv8" + top: "conv9/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + group: 512 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv9/dw/relu" + type: "ReLU" + bottom: "conv9/dw" + top: "conv9/dw" +} +layer { + name: "conv9" + type: "Convolution" + bottom: "conv9/dw" + top: "conv9" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv9/relu" + type: "ReLU" + bottom: "conv9" + top: "conv9" +} +layer { + name: "conv10/dw" + type: "Convolution" + bottom: "conv9" + top: "conv10/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + group: 512 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv10/dw/relu" + type: "ReLU" + bottom: "conv10/dw" + top: "conv10/dw" +} +layer { + name: "conv10" + type: "Convolution" + bottom: "conv10/dw" + top: "conv10" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv10/relu" + type: "ReLU" + bottom: "conv10" + top: "conv10" +} +layer { + name: "conv11/dw" + type: "Convolution" + bottom: "conv10" + top: "conv11/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + group: 512 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv11/dw/relu" + type: "ReLU" + bottom: "conv11/dw" + top: "conv11/dw" +} +layer { + name: "conv11" + type: "Convolution" + bottom: "conv11/dw" + top: "conv11" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv11/relu" + type: "ReLU" + bottom: "conv11" + top: "conv11" +} +layer { + name: "conv12/dw" + type: "Convolution" + bottom: "conv11" + top: "conv12/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + stride: 2 + group: 512 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv12/dw/relu" + type: "ReLU" + bottom: "conv12/dw" + top: "conv12/dw" +} +layer { + name: "conv12" + type: "Convolution" + bottom: "conv12/dw" + top: "conv12" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 1024 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv12/relu" + type: "ReLU" + bottom: "conv12" + top: "conv12" +} +layer { + name: "conv13/dw" + type: "Convolution" + bottom: "conv12" + top: "conv13/dw" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 1024 + pad: 1 + kernel_size: 3 + group: 1024 + engine: CAFFE + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv13/dw/relu" + type: "ReLU" + bottom: "conv13/dw" + top: "conv13/dw" +} +layer { + name: "conv13" + type: "Convolution" + bottom: "conv13/dw" + top: "conv13" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 1024 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv13/relu" + type: "ReLU" + bottom: "conv13" + top: "conv13" +} +layer { + name: "conv14_1" + type: "Convolution" + bottom: "conv13" + top: "conv14_1" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 256 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv14_1/relu" + type: "ReLU" + bottom: "conv14_1" + top: "conv14_1" +} +layer { + name: "conv14_2" + type: "Convolution" + bottom: "conv14_1" + top: "conv14_2" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 512 + pad: 1 + kernel_size: 3 + stride: 2 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv14_2/relu" + type: "ReLU" + bottom: "conv14_2" + top: "conv14_2" +} +layer { + name: "conv15_1" + type: "Convolution" + bottom: "conv14_2" + top: "conv15_1" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 128 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv15_1/relu" + type: "ReLU" + bottom: "conv15_1" + top: "conv15_1" +} +layer { + name: "conv15_2" + type: "Convolution" + bottom: "conv15_1" + top: "conv15_2" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + stride: 2 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv15_2/relu" + type: "ReLU" + bottom: "conv15_2" + top: "conv15_2" +} +layer { + name: "conv16_1" + type: "Convolution" + bottom: "conv15_2" + top: "conv16_1" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 128 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv16_1/relu" + type: "ReLU" + bottom: "conv16_1" + top: "conv16_1" +} +layer { + name: "conv16_2" + type: "Convolution" + bottom: "conv16_1" + top: "conv16_2" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + stride: 2 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv16_2/relu" + type: "ReLU" + bottom: "conv16_2" + top: "conv16_2" +} +layer { + name: "conv17_1" + type: "Convolution" + bottom: "conv16_2" + top: "conv17_1" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 64 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv17_1/relu" + type: "ReLU" + bottom: "conv17_1" + top: "conv17_1" +} +layer { + name: "conv17_2" + type: "Convolution" + bottom: "conv17_1" + top: "conv17_2" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 128 + pad: 1 + kernel_size: 3 + stride: 2 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv17_2/relu" + type: "ReLU" + bottom: "conv17_2" + top: "conv17_2" +} +layer { + name: "conv11_mbox_loc" + type: "Convolution" + bottom: "conv11" + top: "conv11_mbox_loc" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 12 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv11_mbox_loc_perm" + type: "Permute" + bottom: "conv11_mbox_loc" + top: "conv11_mbox_loc_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv11_mbox_loc_flat" + type: "Flatten" + bottom: "conv11_mbox_loc_perm" + top: "conv11_mbox_loc_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv11_mbox_conf" + type: "Convolution" + bottom: "conv11" + top: "conv11_mbox_conf" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 63 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv11_mbox_conf_perm" + type: "Permute" + bottom: "conv11_mbox_conf" + top: "conv11_mbox_conf_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv11_mbox_conf_flat" + type: "Flatten" + bottom: "conv11_mbox_conf_perm" + top: "conv11_mbox_conf_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv11_mbox_priorbox" + type: "PriorBox" + bottom: "conv11" + bottom: "data" + top: "conv11_mbox_priorbox" + prior_box_param { + min_size: 60.0 + aspect_ratio: 2.0 + flip: true + clip: false + variance: 0.1 + variance: 0.1 + variance: 0.2 + variance: 0.2 + offset: 0.5 + } +} +layer { + name: "conv13_mbox_loc" + type: "Convolution" + bottom: "conv13" + top: "conv13_mbox_loc" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 24 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv13_mbox_loc_perm" + type: "Permute" + bottom: "conv13_mbox_loc" + top: "conv13_mbox_loc_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv13_mbox_loc_flat" + type: "Flatten" + bottom: "conv13_mbox_loc_perm" + top: "conv13_mbox_loc_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv13_mbox_conf" + type: "Convolution" + bottom: "conv13" + top: "conv13_mbox_conf" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 126 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv13_mbox_conf_perm" + type: "Permute" + bottom: "conv13_mbox_conf" + top: "conv13_mbox_conf_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv13_mbox_conf_flat" + type: "Flatten" + bottom: "conv13_mbox_conf_perm" + top: "conv13_mbox_conf_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv13_mbox_priorbox" + type: "PriorBox" + bottom: "conv13" + bottom: "data" + top: "conv13_mbox_priorbox" + prior_box_param { + min_size: 105.0 + max_size: 150.0 + aspect_ratio: 2.0 + aspect_ratio: 3.0 + flip: true + clip: false + variance: 0.1 + variance: 0.1 + variance: 0.2 + variance: 0.2 + offset: 0.5 + } +} +layer { + name: "conv14_2_mbox_loc" + type: "Convolution" + bottom: "conv14_2" + top: "conv14_2_mbox_loc" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 24 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv14_2_mbox_loc_perm" + type: "Permute" + bottom: "conv14_2_mbox_loc" + top: "conv14_2_mbox_loc_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv14_2_mbox_loc_flat" + type: "Flatten" + bottom: "conv14_2_mbox_loc_perm" + top: "conv14_2_mbox_loc_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv14_2_mbox_conf" + type: "Convolution" + bottom: "conv14_2" + top: "conv14_2_mbox_conf" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 126 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv14_2_mbox_conf_perm" + type: "Permute" + bottom: "conv14_2_mbox_conf" + top: "conv14_2_mbox_conf_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv14_2_mbox_conf_flat" + type: "Flatten" + bottom: "conv14_2_mbox_conf_perm" + top: "conv14_2_mbox_conf_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv14_2_mbox_priorbox" + type: "PriorBox" + bottom: "conv14_2" + bottom: "data" + top: "conv14_2_mbox_priorbox" + prior_box_param { + min_size: 150.0 + max_size: 195.0 + aspect_ratio: 2.0 + aspect_ratio: 3.0 + flip: true + clip: false + variance: 0.1 + variance: 0.1 + variance: 0.2 + variance: 0.2 + offset: 0.5 + } +} +layer { + name: "conv15_2_mbox_loc" + type: "Convolution" + bottom: "conv15_2" + top: "conv15_2_mbox_loc" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 24 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv15_2_mbox_loc_perm" + type: "Permute" + bottom: "conv15_2_mbox_loc" + top: "conv15_2_mbox_loc_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv15_2_mbox_loc_flat" + type: "Flatten" + bottom: "conv15_2_mbox_loc_perm" + top: "conv15_2_mbox_loc_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv15_2_mbox_conf" + type: "Convolution" + bottom: "conv15_2" + top: "conv15_2_mbox_conf" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 126 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv15_2_mbox_conf_perm" + type: "Permute" + bottom: "conv15_2_mbox_conf" + top: "conv15_2_mbox_conf_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv15_2_mbox_conf_flat" + type: "Flatten" + bottom: "conv15_2_mbox_conf_perm" + top: "conv15_2_mbox_conf_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv15_2_mbox_priorbox" + type: "PriorBox" + bottom: "conv15_2" + bottom: "data" + top: "conv15_2_mbox_priorbox" + prior_box_param { + min_size: 195.0 + max_size: 240.0 + aspect_ratio: 2.0 + aspect_ratio: 3.0 + flip: true + clip: false + variance: 0.1 + variance: 0.1 + variance: 0.2 + variance: 0.2 + offset: 0.5 + } +} +layer { + name: "conv16_2_mbox_loc" + type: "Convolution" + bottom: "conv16_2" + top: "conv16_2_mbox_loc" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 24 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv16_2_mbox_loc_perm" + type: "Permute" + bottom: "conv16_2_mbox_loc" + top: "conv16_2_mbox_loc_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv16_2_mbox_loc_flat" + type: "Flatten" + bottom: "conv16_2_mbox_loc_perm" + top: "conv16_2_mbox_loc_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv16_2_mbox_conf" + type: "Convolution" + bottom: "conv16_2" + top: "conv16_2_mbox_conf" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 126 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv16_2_mbox_conf_perm" + type: "Permute" + bottom: "conv16_2_mbox_conf" + top: "conv16_2_mbox_conf_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv16_2_mbox_conf_flat" + type: "Flatten" + bottom: "conv16_2_mbox_conf_perm" + top: "conv16_2_mbox_conf_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv16_2_mbox_priorbox" + type: "PriorBox" + bottom: "conv16_2" + bottom: "data" + top: "conv16_2_mbox_priorbox" + prior_box_param { + min_size: 240.0 + max_size: 285.0 + aspect_ratio: 2.0 + aspect_ratio: 3.0 + flip: true + clip: false + variance: 0.1 + variance: 0.1 + variance: 0.2 + variance: 0.2 + offset: 0.5 + } +} +layer { + name: "conv17_2_mbox_loc" + type: "Convolution" + bottom: "conv17_2" + top: "conv17_2_mbox_loc" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 24 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv17_2_mbox_loc_perm" + type: "Permute" + bottom: "conv17_2_mbox_loc" + top: "conv17_2_mbox_loc_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv17_2_mbox_loc_flat" + type: "Flatten" + bottom: "conv17_2_mbox_loc_perm" + top: "conv17_2_mbox_loc_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv17_2_mbox_conf" + type: "Convolution" + bottom: "conv17_2" + top: "conv17_2_mbox_conf" + param { + lr_mult: 1.0 + decay_mult: 1.0 + } + param { + lr_mult: 2.0 + decay_mult: 0.0 + } + convolution_param { + num_output: 126 + kernel_size: 1 + weight_filler { + type: "msra" + } + bias_filler { + type: "constant" + value: 0.0 + } + } +} +layer { + name: "conv17_2_mbox_conf_perm" + type: "Permute" + bottom: "conv17_2_mbox_conf" + top: "conv17_2_mbox_conf_perm" + permute_param { + order: 0 + order: 2 + order: 3 + order: 1 + } +} +layer { + name: "conv17_2_mbox_conf_flat" + type: "Flatten" + bottom: "conv17_2_mbox_conf_perm" + top: "conv17_2_mbox_conf_flat" + flatten_param { + axis: 1 + } +} +layer { + name: "conv17_2_mbox_priorbox" + type: "PriorBox" + bottom: "conv17_2" + bottom: "data" + top: "conv17_2_mbox_priorbox" + prior_box_param { + min_size: 285.0 + max_size: 300.0 + aspect_ratio: 2.0 + aspect_ratio: 3.0 + flip: true + clip: false + variance: 0.1 + variance: 0.1 + variance: 0.2 + variance: 0.2 + offset: 0.5 + } +} +layer { + name: "mbox_loc" + type: "Concat" + bottom: "conv11_mbox_loc_flat" + bottom: "conv13_mbox_loc_flat" + bottom: "conv14_2_mbox_loc_flat" + bottom: "conv15_2_mbox_loc_flat" + bottom: "conv16_2_mbox_loc_flat" + bottom: "conv17_2_mbox_loc_flat" + top: "mbox_loc" + concat_param { + axis: 1 + } +} +layer { + name: "mbox_conf" + type: "Concat" + bottom: "conv11_mbox_conf_flat" + bottom: "conv13_mbox_conf_flat" + bottom: "conv14_2_mbox_conf_flat" + bottom: "conv15_2_mbox_conf_flat" + bottom: "conv16_2_mbox_conf_flat" + bottom: "conv17_2_mbox_conf_flat" + top: "mbox_conf" + concat_param { + axis: 1 + } +} +layer { + name: "mbox_priorbox" + type: "Concat" + bottom: "conv11_mbox_priorbox" + bottom: "conv13_mbox_priorbox" + bottom: "conv14_2_mbox_priorbox" + bottom: "conv15_2_mbox_priorbox" + bottom: "conv16_2_mbox_priorbox" + bottom: "conv17_2_mbox_priorbox" + top: "mbox_priorbox" + concat_param { + axis: 2 + } +} +layer { + name: "mbox_conf_reshape" + type: "Reshape" + bottom: "mbox_conf" + top: "mbox_conf_reshape" + reshape_param { + shape { + dim: 0 + dim: -1 + dim: 21 + } + } +} +layer { + name: "mbox_conf_softmax" + type: "Softmax" + bottom: "mbox_conf_reshape" + top: "mbox_conf_softmax" + softmax_param { + axis: 2 + } +} +layer { + name: "mbox_conf_flatten" + type: "Flatten" + bottom: "mbox_conf_softmax" + top: "mbox_conf_flatten" + flatten_param { + axis: 1 + } +} +layer { + name: "detection_out" + type: "DetectionOutput" + bottom: "mbox_loc" + bottom: "mbox_conf_flatten" + bottom: "mbox_priorbox" + top: "detection_out" + include { + phase: TEST + } + detection_output_param { + num_classes: 21 + share_location: true + background_label_id: 0 + nms_param { + nms_threshold: 0.45 + top_k: 100 + } + code_type: CENTER_SIZE + keep_top_k: 100 + confidence_threshold: 0.25 + } +} diff --git a/mylib/centroidtracker.py b/mylib/centroidtracker.py new file mode 100644 index 0000000..3b89b0a --- /dev/null +++ b/mylib/centroidtracker.py @@ -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 \ No newline at end of file diff --git a/mylib/mailer.py b/mylib/mailer.py new file mode 100644 index 0000000..a0fd0e2 --- /dev/null +++ b/mylib/mailer.py @@ -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() diff --git a/mylib/trackableobject.py b/mylib/trackableobject.py new file mode 100644 index 0000000..2bc279f --- /dev/null +++ b/mylib/trackableobject.py @@ -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 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..511609b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +smtplib +ssl +time +schedule +csv +numpy +argparse +imutils +time +dlib +cv2 +datetime +itertools +scipy +collections \ No newline at end of file diff --git a/videos/example_01.mp4 b/videos/example_01.mp4 new file mode 100644 index 0000000..74ae877 Binary files /dev/null and b/videos/example_01.mp4 differ