├── .gitattributes ├── LICENSE ├── README.md ├── Run.py ├── mylib ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── config.cpython-37.pyc │ ├── detection.cpython-37.pyc │ ├── mailer.cpython-37.pyc │ └── thread.cpython-37.pyc ├── config.py ├── detection.py ├── mailer.py ├── thread.py └── videos │ ├── output.gif │ ├── output1.gif │ ├── test.mp4 │ └── test1.mp4 ├── requirements.txt └── yolo ├── coco.names ├── yolov3.cfg └── yolov3.weights /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | *.h5 filter=lfs diff=lfs merge=lfs -text 4 | *.weights filter=lfs diff=lfs merge=lfs -text 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sai Subhakar T 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Social-Distancing-in-Real-Time 2 | Social distancing in Real-Time using live video stream/IP camera in OpenCV. 3 | 4 | > This is an improvement/modification to (https://www.pyimagesearch.com/2020/06/01/opencv-social-distancing-detector/). 5 | 6 | > Please refer to the added [Features](#features). 7 | 8 | Output | Output 9 | :-------------------------:|:-------------------------: 10 | ![Output](mylib/videos/output.gif?raw=true "Output") | ![Output](mylib/videos/output1.gif?raw=true "Output") 11 | 12 | - Use case: counting the number of people in the stores/buildings/shopping malls etc., in real-time. 13 | - Sending an alert to the staff if the people are way over the social distancing limits. 14 | - Optimizing the real-time stream for better performance (with threading). 15 | - Acts as a measure to tackle COVID-19. 16 | 17 | --- 18 | 19 | ## Table of Contents 20 | * [Simple Theory](#simple-theory) 21 | * [Running Inference](#running-inference) 22 | * [Features](#features) 23 | * [References](#references) 24 | 25 | ## Simple Theory 26 | **Object detection:** 27 | - We will be using YOLOv3, trained on COCO dataset for object detection. 28 | - In general, single-stage detectors like YOLO tend to be less accurate than two-stage detectors (R-CNN) but are significantly faster. 29 | - YOLO treats object detection as a regression problem, taking a given input image and simultaneously learning bounding box coordinates and corresponding class label probabilities. 30 | - It is used to return the person prediction probability, bounding box coordinates for the detection, and the centroid of the person. 31 | 32 | --- 33 | **Distance calculation:** 34 | - NMS (Non-maxima suppression) is also used to reduce overlapping bounding boxes to only a single bounding box, thus representing the true detection of the object. Having overlapping boxes is not exactly practical and ideal, especially if we need to count the number of objects in an image. 35 | - Euclidean distance is then computed between all pairs of the returned centroids. Simply, a centroid is the center of a bounding box. 36 | - Based on these pairwise distances, we check to see if any two people are less than/close to 'N' pixels apart. 37 | 38 | ## Running Inference 39 | - Install all the required Python dependencies: 40 | ``` 41 | pip install -r requirements.txt 42 | ``` 43 | - If you would like to use GPU, set ```USE_GPU = True``` in the config. options at 'mylib/config.py'. 44 | 45 | - Note that you need to build OpenCV with CUDA (for an NVIDIA GPU) support first: 46 | 47 | > Click [**here**](https://jamesbowley.co.uk/accelerate-opencv-4-2-0-build-with-cuda-and-python-bindings/) for build instructions on Windows. 48 | 49 | > This tutorial also might help. Click [**here**](https://www.youtube.com/watch?v=TT3_dlPL4vo&list=WL&index=108&t=0s). 50 | 51 | - To run inference on a test video file, head into the directory/use the command: 52 | ``` 53 | python run.py -i mylib/videos/test.mp4 54 | ``` 55 | - To run inference on an IP camera, Setup your camera url in 'mylib/config.py': 56 | 57 | ``` 58 | # Enter the ip camera url (e.g., url = 'http://191.138.0.100:8040/video') 59 | url = '' 60 | ``` 61 | - Then run with the command: 62 | 63 | ``` 64 | python run.py 65 | ``` 66 | > Set url = 0 for webcam. 67 | 68 | ## Features 69 | The following are examples of the added features. Note: You can easily on/off them in the config. options (mylib/config.py): 70 | 71 | ***1. Real-Time alert:*** 72 | - If selected, we send an email alert in real-time. Use case: If the total number of violations (say 10 or 30) exceeded in a store/building, we simply alert the staff. 73 | - You can set the max. violations limit in config (```Threshold = 15```). 74 | - This is pretty useful considering the COVID-19 scenario. 75 | 76 | > Note: To setup the sender email, please refer the instructions inside 'mylib/mailer.py'. Setup receiver email in the config. 77 | 78 | ***2. Threading:*** 79 | - Multi-Threading is implemented in 'mylib/thread.py'. If you ever see a lag/delay in your real-time stream, consider using it. 80 | - Threading removes OpenCV's internal buffer (which basically stores the new frames yet to be processed until your system processes the old frames) and thus reduces the lag/increases fps. 81 | - If your system is not capable of simultaneously processing and outputting the result, you might see a delay in the stream. This is where threading comes into action. 82 | - It is most suitable for solid performance on complex real-time applications. To use threading: 83 | 84 | set ```Thread = True``` in the config. 85 | 86 | ***3. People counter:*** 87 | - If enabled, we simply count the total number of people: set ```People_Counter = True``` in the config. 88 | 89 | ***4. Desired violations limits:*** 90 | - You can also set your desired minimum and maximum violations limits. For example, ```MAX_DISTANCE = 80``` implies the maximum distance 2 people can be closer together is 80 pixels. If they fell under 80, we treat it as an 'abnormal' violation (yellow). 91 | - Similarly ```MIN_DISTANCE = 50``` implies the minimum distance between 2 people. If they fell under 50 px (which is closer than 80), we treat it as a more 'serious' violation (red). 92 | - Anything above 80 px is considered as a safe distance and thus, 'no' violation (green). 93 | 94 | ## References 95 | ***Main:*** 96 | - YOLOv3 paper: https://arxiv.org/pdf/1804.02767.pdf 97 | - YOLO original paper: https://arxiv.org/abs/1506.02640 98 | - YOLO TensorFlow implementation (darkflow): https://github.com/thtrieu/darkflow 99 | 100 | ***Optional:*** 101 | - More theory: https://www.pyimagesearch.com/2018/11/12/yolo-object-detection-with-opencv/ 102 | - Other trained model weights from official doc: https://pjreddie.com/darknet/yolo/ 103 | 104 | --- 105 | 106 | ## Thanks for the read & have fun! 107 | 108 | > To get started/contribute quickly (optional) ... 109 | 110 | - **Option 1** 111 | - 🍴 Fork this repo and pull request! 112 | 113 | - **Option 2** 114 | - 👯 Clone this repo: 115 | ``` 116 | $ git clone https://github.com/saimj7/Social-Distancing-Detection-in-Real-Time.git 117 | ``` 118 | 119 | - **Roll it!** 120 | 121 | --- 122 | 123 | saimj7/ 02-11-2020 © Sai_Mj. 124 | -------------------------------------------------------------------------------- /Run.py: -------------------------------------------------------------------------------- 1 | from mylib import config, thread 2 | from mylib.mailer import Mailer 3 | from mylib.detection import detect_people 4 | from imutils.video import VideoStream, FPS 5 | from scipy.spatial import distance as dist 6 | import numpy as np 7 | import argparse, imutils, cv2, os, time, schedule 8 | 9 | #----------------------------Parse req. arguments------------------------------# 10 | ap = argparse.ArgumentParser() 11 | ap.add_argument("-i", "--input", type=str, default="", 12 | help="path to (optional) input video file") 13 | ap.add_argument("-o", "--output", type=str, default="", 14 | help="path to (optional) output video file") 15 | ap.add_argument("-d", "--display", type=int, default=1, 16 | help="whether or not output frame should be displayed") 17 | args = vars(ap.parse_args()) 18 | #------------------------------------------------------------------------------# 19 | 20 | # load the COCO class labels our YOLO model was trained on 21 | labelsPath = os.path.sep.join([config.MODEL_PATH, "coco.names"]) 22 | LABELS = open(labelsPath).read().strip().split("\n") 23 | 24 | # derive the paths to the YOLO weights and model configuration 25 | weightsPath = os.path.sep.join([config.MODEL_PATH, "yolov3.weights"]) 26 | configPath = os.path.sep.join([config.MODEL_PATH, "yolov3.cfg"]) 27 | 28 | # load our YOLO object detector trained on COCO dataset (80 classes) 29 | net = cv2.dnn.readNetFromDarknet(configPath, weightsPath) 30 | 31 | # check if we are going to use GPU 32 | if config.USE_GPU: 33 | # set CUDA as the preferable backend and target 34 | print("") 35 | print("[INFO] Looking for GPU") 36 | net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA) 37 | net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA) 38 | 39 | # determine only the *output* layer names that we need from YOLO 40 | ln = net.getLayerNames() 41 | ln = [ln[i - 1] for i in net.getUnconnectedOutLayers()] 42 | 43 | # if a video path was not supplied, grab a reference to the camera 44 | if not args.get("input", False): 45 | print("[INFO] Starting the live stream..") 46 | vs = cv2.VideoCapture(config.url) 47 | if config.Thread: 48 | cap = thread.ThreadingClass(config.url) 49 | time.sleep(2.0) 50 | 51 | # otherwise, grab a reference to the video file 52 | else: 53 | print("[INFO] Starting the video..") 54 | vs = cv2.VideoCapture(args["input"]) 55 | if config.Thread: 56 | cap = thread.ThreadingClass(args["input"]) 57 | 58 | writer = None 59 | # start the FPS counter 60 | fps = FPS().start() 61 | 62 | # loop over the frames from the video stream 63 | while True: 64 | # read the next frame from the file 65 | if config.Thread: 66 | frame = cap.read() 67 | 68 | else: 69 | (grabbed, frame) = vs.read() 70 | # if the frame was not grabbed, then we have reached the end of the stream 71 | if not grabbed: 72 | break 73 | 74 | # resize the frame and then detect people (and only people) in it 75 | frame = imutils.resize(frame, width=700) 76 | results = detect_people(frame, net, ln, 77 | personIdx=LABELS.index("person")) 78 | 79 | # initialize the set of indexes that violate the max/min social distance limits 80 | serious = set() 81 | abnormal = set() 82 | 83 | # ensure there are *at least* two people detections (required in 84 | # order to compute our pairwise distance maps) 85 | if len(results) >= 2: 86 | # extract all centroids from the results and compute the 87 | # Euclidean distances between all pairs of the centroids 88 | centroids = np.array([r[2] for r in results]) 89 | D = dist.cdist(centroids, centroids, metric="euclidean") 90 | 91 | # loop over the upper triangular of the distance matrix 92 | for i in range(0, D.shape[0]): 93 | for j in range(i + 1, D.shape[1]): 94 | # check to see if the distance between any two 95 | # centroid pairs is less than the configured number of pixels 96 | if D[i, j] < config.MIN_DISTANCE: 97 | # update our violation set with the indexes of the centroid pairs 98 | serious.add(i) 99 | serious.add(j) 100 | # update our abnormal set if the centroid distance is below max distance limit 101 | if (D[i, j] < config.MAX_DISTANCE) and not serious: 102 | abnormal.add(i) 103 | abnormal.add(j) 104 | 105 | # loop over the results 106 | for (i, (prob, bbox, centroid)) in enumerate(results): 107 | # extract the bounding box and centroid coordinates, then 108 | # initialize the color of the annotation 109 | (startX, startY, endX, endY) = bbox 110 | (cX, cY) = centroid 111 | color = (0, 255, 0) 112 | 113 | # if the index pair exists within the violation/abnormal sets, then update the color 114 | if i in serious: 115 | color = (0, 0, 255) 116 | elif i in abnormal: 117 | color = (0, 255, 255) #orange = (0, 165, 255) 118 | 119 | # draw (1) a bounding box around the person and (2) the 120 | # centroid coordinates of the person, 121 | cv2.rectangle(frame, (startX, startY), (endX, endY), color, 2) 122 | cv2.circle(frame, (cX, cY), 5, color, 2) 123 | 124 | # draw some of the parameters 125 | Safe_Distance = "Safe distance: >{} px".format(config.MAX_DISTANCE) 126 | cv2.putText(frame, Safe_Distance, (470, frame.shape[0] - 25), 127 | cv2.FONT_HERSHEY_SIMPLEX, 0.60, (255, 0, 0), 2) 128 | Threshold = "Threshold limit: {}".format(config.Threshold) 129 | cv2.putText(frame, Threshold, (470, frame.shape[0] - 50), 130 | cv2.FONT_HERSHEY_SIMPLEX, 0.60, (255, 0, 0), 2) 131 | 132 | # draw the total number of social distancing violations on the output frame 133 | text = "Total serious violations: {}".format(len(serious)) 134 | cv2.putText(frame, text, (10, frame.shape[0] - 55), 135 | cv2.FONT_HERSHEY_SIMPLEX, 0.70, (0, 0, 255), 2) 136 | 137 | text1 = "Total abnormal violations: {}".format(len(abnormal)) 138 | cv2.putText(frame, text1, (10, frame.shape[0] - 25), 139 | cv2.FONT_HERSHEY_SIMPLEX, 0.70, (0, 255, 255), 2) 140 | 141 | #------------------------------Alert function----------------------------------# 142 | if len(serious) >= config.Threshold: 143 | cv2.putText(frame, "-ALERT: Violations over limit-", (10, frame.shape[0] - 80), 144 | cv2.FONT_HERSHEY_COMPLEX, 0.60, (0, 0, 255), 2) 145 | if config.ALERT: 146 | print("") 147 | print('[INFO] Sending mail...') 148 | Mailer().send(config.MAIL) 149 | print('[INFO] Mail sent') 150 | #config.ALERT = False 151 | #------------------------------------------------------------------------------# 152 | # check to see if the output frame should be displayed to our screen 153 | if args["display"] > 0: 154 | # show the output frame 155 | cv2.imshow("Real-Time Monitoring/Analysis Window", frame) 156 | key = cv2.waitKey(1) & 0xFF 157 | 158 | # if the `q` key was pressed, break from the loop 159 | if key == ord("q"): 160 | break 161 | # update the FPS counter 162 | fps.update() 163 | 164 | # if an output video file path has been supplied and the video 165 | # writer has not been initialized, do so now 166 | if args["output"] != "" and writer is None: 167 | # initialize our video writer 168 | fourcc = cv2.VideoWriter_fourcc(*"MJPG") 169 | writer = cv2.VideoWriter(args["output"], fourcc, 25, 170 | (frame.shape[1], frame.shape[0]), True) 171 | 172 | # if the video writer is not None, write the frame to the output video file 173 | if writer is not None: 174 | writer.write(frame) 175 | 176 | # stop the timer and display FPS information 177 | fps.stop() 178 | print("===========================") 179 | print("[INFO] Elasped time: {:.2f}".format(fps.elapsed())) 180 | print("[INFO] Approx. FPS: {:.2f}".format(fps.fps())) 181 | 182 | # close any open windows 183 | cv2.destroyAllWindows() 184 | -------------------------------------------------------------------------------- /mylib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/__init__.py -------------------------------------------------------------------------------- /mylib/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /mylib/__pycache__/config.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/__pycache__/config.cpython-37.pyc -------------------------------------------------------------------------------- /mylib/__pycache__/detection.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/__pycache__/detection.cpython-37.pyc -------------------------------------------------------------------------------- /mylib/__pycache__/mailer.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/__pycache__/mailer.cpython-37.pyc -------------------------------------------------------------------------------- /mylib/__pycache__/thread.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/__pycache__/thread.cpython-37.pyc -------------------------------------------------------------------------------- /mylib/config.py: -------------------------------------------------------------------------------- 1 | # base path to YOLO directory 2 | MODEL_PATH = "yolo" 3 | # initialize minimum probability to filter weak detections along with 4 | # the threshold when applying non-maxima suppression 5 | MIN_CONF = 0.3 6 | NMS_THRESH = 0.3 7 | 8 | #=============================================================================== 9 | #=================================\CONFIG./===================================== 10 | """ Below are your desired config. options to set for real-time inference """ 11 | # To count the total number of people (True/False). 12 | People_Counter = True 13 | # Threading ON/OFF. Please refer 'mylib>thread.py'. 14 | Thread = False 15 | # Set the threshold value for total violations limit. 16 | Threshold = 15 17 | # Enter the ip camera url (e.g., url = 'http://191.138.0.100:8040/video'); 18 | # Set url = 0 for webcam. 19 | url = '' 20 | # Turn ON/OFF the email alert feature. 21 | ALERT = False 22 | # Set mail to receive the real-time alerts. E.g., 'xxx@gmail.com'. 23 | MAIL = '' 24 | # Set if GPU should be used for computations; Otherwise uses the CPU by default. 25 | USE_GPU = True 26 | # Define the max/min safe distance limits (in pixels) between 2 people. 27 | MAX_DISTANCE = 80 28 | MIN_DISTANCE = 50 29 | #=============================================================================== 30 | #=============================================================================== 31 | -------------------------------------------------------------------------------- /mylib/detection.py: -------------------------------------------------------------------------------- 1 | # import the necessary packages 2 | from .config import NMS_THRESH, MIN_CONF, People_Counter 3 | import numpy as np 4 | import cv2 5 | 6 | def detect_people(frame, net, ln, personIdx=0): 7 | # grab the dimensions of the frame and initialize the list of 8 | # results 9 | (H, W) = frame.shape[:2] 10 | results = [] 11 | 12 | # construct a blob from the input frame and then perform a forward 13 | # pass of the YOLO object detector, giving us our bounding boxes 14 | # and associated probabilities 15 | blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), 16 | swapRB=True, crop=False) 17 | net.setInput(blob) 18 | layerOutputs = net.forward(ln) 19 | 20 | # initialize our lists of detected bounding boxes, centroids, and 21 | # confidences, respectively 22 | boxes = [] 23 | centroids = [] 24 | confidences = [] 25 | 26 | # loop over each of the layer outputs 27 | for output in layerOutputs: 28 | # loop over each of the detections 29 | for detection in output: 30 | # extract the class ID and confidence (i.e., probability) 31 | # of the current object detection 32 | scores = detection[5:] 33 | classID = np.argmax(scores) 34 | confidence = scores[classID] 35 | 36 | # filter detections by (1) ensuring that the object 37 | # detected was a person and (2) that the minimum 38 | # confidence is met 39 | if classID == personIdx and confidence > MIN_CONF: 40 | # scale the bounding box coordinates back relative to 41 | # the size of the image, keeping in mind that YOLO 42 | # actually returns the center (x, y)-coordinates of 43 | # the bounding box followed by the boxes' width and 44 | # height 45 | box = detection[0:4] * np.array([W, H, W, H]) 46 | (centerX, centerY, width, height) = box.astype("int") 47 | 48 | # use the center (x, y)-coordinates to derive the top 49 | # and and left corner of the bounding box 50 | x = int(centerX - (width / 2)) 51 | y = int(centerY - (height / 2)) 52 | 53 | # update our list of bounding box coordinates, 54 | # centroids, and confidences 55 | boxes.append([x, y, int(width), int(height)]) 56 | centroids.append((centerX, centerY)) 57 | confidences.append(float(confidence)) 58 | 59 | # apply non-maxima suppression to suppress weak, overlapping 60 | # bounding boxes 61 | idxs = cv2.dnn.NMSBoxes(boxes, confidences, MIN_CONF, NMS_THRESH) 62 | #print('Total people count:', len(idxs)) 63 | # compute the total people counter 64 | if People_Counter: 65 | human_count = "Human count: {}".format(len(idxs)) 66 | cv2.putText(frame, human_count, (470, frame.shape[0] - 75), cv2.FONT_HERSHEY_SIMPLEX, 0.70, (0, 0, 0), 2) 67 | 68 | # ensure at least one detection exists 69 | if len(idxs) > 0: 70 | # loop over the indexes we are keeping 71 | for i in idxs.flatten(): 72 | # extract the bounding box coordinates 73 | (x, y) = (boxes[i][0], boxes[i][1]) 74 | (w, h) = (boxes[i][2], boxes[i][3]) 75 | 76 | # update our results list to consist of the person 77 | # prediction probability, bounding box coordinates, 78 | # and the centroid 79 | r = (confidences[i], (x, y, x + w, y + h), centroids[i]) 80 | results.append(r) 81 | 82 | # return the list of results 83 | return results 84 | -------------------------------------------------------------------------------- /mylib/mailer.py: -------------------------------------------------------------------------------- 1 | import smtplib, ssl 2 | 3 | class Mailer: 4 | 5 | """ 6 | This script initiaties the email alert function. 7 | """ 8 | def __init__(self): 9 | # Enter your email below. This email will be used to send alerts. 10 | # E.g., "email@gmail.com" 11 | self.EMAIL = "" 12 | # Enter the email password below. Note that the password varies if you have secured 13 | # 2 step verification turned on. You can refer the links below and create an application specific password. 14 | # Google mail has a guide here: https://myaccount.google.com/lesssecureapps 15 | # For 2 step verified accounts: https://support.google.com/accounts/answer/185833 16 | # Example: aoiwhdoaldmwopau 17 | self.PASS = "" 18 | self.PORT = 465 19 | self.server = smtplib.SMTP_SSL('smtp.gmail.com', self.PORT) 20 | 21 | def send(self, mail): 22 | self.server = smtplib.SMTP_SSL('smtp.gmail.com', self.PORT) 23 | self.server.login(self.EMAIL, self.PASS) 24 | # message to be sent 25 | SUBJECT = 'ALERT!' 26 | TEXT = f'Social distancing violations exceeded!' 27 | message = 'Subject: {}\n\n{}'.format(SUBJECT, TEXT) 28 | 29 | # sending the mail 30 | self.server.sendmail(self.EMAIL, mail, message) 31 | self.server.quit() 32 | -------------------------------------------------------------------------------- /mylib/thread.py: -------------------------------------------------------------------------------- 1 | import cv2, threading, queue 2 | 3 | class ThreadingClass: 4 | # initiate threading class 5 | def __init__(self, name): 6 | self.cap = cv2.VideoCapture(name) 7 | # define an empty queue and thread 8 | self.q = queue.Queue() 9 | t = threading.Thread(target=self._reader) 10 | t.daemon = True 11 | t.start() 12 | 13 | # read the frames as soon as they are available, discard any unprocessed frames; 14 | # this approach removes OpenCV's internal buffer and reduces the frame lag 15 | def _reader(self): 16 | while True: 17 | (ret, frame) = self.cap.read() # read the frames and --- 18 | if not ret: 19 | break 20 | if not self.q.empty(): 21 | try: 22 | self.q.get_nowait() 23 | except queue.Empty: 24 | pass 25 | self.q.put(frame) # --- store them in a queue (instead of the buffer) 26 | 27 | def read(self): 28 | return self.q.get() # fetch frames from the queue one by one 29 | -------------------------------------------------------------------------------- /mylib/videos/output.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/videos/output.gif -------------------------------------------------------------------------------- /mylib/videos/output1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/videos/output1.gif -------------------------------------------------------------------------------- /mylib/videos/test.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/videos/test.mp4 -------------------------------------------------------------------------------- /mylib/videos/test1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saimj7/Social-Distancing-Detection-in-Real-Time/121a082e5e8a3eee3d1ef70e7ed8205ac856ce1f/mylib/videos/test1.mp4 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.19.2 2 | scipy==1.4.1 3 | imutils==0.5.3 4 | opencv-python==4.2.0.32 5 | argparse==1.4.0 6 | -------------------------------------------------------------------------------- /yolo/coco.names: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorbike 5 | aeroplane 6 | bus 7 | train 8 | truck 9 | boat 10 | traffic light 11 | fire hydrant 12 | stop sign 13 | parking meter 14 | bench 15 | bird 16 | cat 17 | dog 18 | horse 19 | sheep 20 | cow 21 | elephant 22 | bear 23 | zebra 24 | giraffe 25 | backpack 26 | umbrella 27 | handbag 28 | tie 29 | suitcase 30 | frisbee 31 | skis 32 | snowboard 33 | sports ball 34 | kite 35 | baseball bat 36 | baseball glove 37 | skateboard 38 | surfboard 39 | tennis racket 40 | bottle 41 | wine glass 42 | cup 43 | fork 44 | knife 45 | spoon 46 | bowl 47 | banana 48 | apple 49 | sandwich 50 | orange 51 | broccoli 52 | carrot 53 | hot dog 54 | pizza 55 | donut 56 | cake 57 | chair 58 | sofa 59 | pottedplant 60 | bed 61 | diningtable 62 | toilet 63 | tvmonitor 64 | laptop 65 | mouse 66 | remote 67 | keyboard 68 | cell phone 69 | microwave 70 | oven 71 | toaster 72 | sink 73 | refrigerator 74 | book 75 | clock 76 | vase 77 | scissors 78 | teddy bear 79 | hair drier 80 | toothbrush -------------------------------------------------------------------------------- /yolo/yolov3.cfg: -------------------------------------------------------------------------------- 1 | [net] 2 | # Testing 3 | # batch=1 4 | # subdivisions=1 5 | # Training 6 | batch=64 7 | subdivisions=16 8 | width=608 9 | height=608 10 | channels=3 11 | momentum=0.9 12 | decay=0.0005 13 | angle=0 14 | saturation = 1.5 15 | exposure = 1.5 16 | hue=.1 17 | 18 | learning_rate=0.001 19 | burn_in=1000 20 | max_batches = 500200 21 | policy=steps 22 | steps=400000,450000 23 | scales=.1,.1 24 | 25 | [convolutional] 26 | batch_normalize=1 27 | filters=32 28 | size=3 29 | stride=1 30 | pad=1 31 | activation=leaky 32 | 33 | # Downsample 34 | 35 | [convolutional] 36 | batch_normalize=1 37 | filters=64 38 | size=3 39 | stride=2 40 | pad=1 41 | activation=leaky 42 | 43 | [convolutional] 44 | batch_normalize=1 45 | filters=32 46 | size=1 47 | stride=1 48 | pad=1 49 | activation=leaky 50 | 51 | [convolutional] 52 | batch_normalize=1 53 | filters=64 54 | size=3 55 | stride=1 56 | pad=1 57 | activation=leaky 58 | 59 | [shortcut] 60 | from=-3 61 | activation=linear 62 | 63 | # Downsample 64 | 65 | [convolutional] 66 | batch_normalize=1 67 | filters=128 68 | size=3 69 | stride=2 70 | pad=1 71 | activation=leaky 72 | 73 | [convolutional] 74 | batch_normalize=1 75 | filters=64 76 | size=1 77 | stride=1 78 | pad=1 79 | activation=leaky 80 | 81 | [convolutional] 82 | batch_normalize=1 83 | filters=128 84 | size=3 85 | stride=1 86 | pad=1 87 | activation=leaky 88 | 89 | [shortcut] 90 | from=-3 91 | activation=linear 92 | 93 | [convolutional] 94 | batch_normalize=1 95 | filters=64 96 | size=1 97 | stride=1 98 | pad=1 99 | activation=leaky 100 | 101 | [convolutional] 102 | batch_normalize=1 103 | filters=128 104 | size=3 105 | stride=1 106 | pad=1 107 | activation=leaky 108 | 109 | [shortcut] 110 | from=-3 111 | activation=linear 112 | 113 | # Downsample 114 | 115 | [convolutional] 116 | batch_normalize=1 117 | filters=256 118 | size=3 119 | stride=2 120 | pad=1 121 | activation=leaky 122 | 123 | [convolutional] 124 | batch_normalize=1 125 | filters=128 126 | size=1 127 | stride=1 128 | pad=1 129 | activation=leaky 130 | 131 | [convolutional] 132 | batch_normalize=1 133 | filters=256 134 | size=3 135 | stride=1 136 | pad=1 137 | activation=leaky 138 | 139 | [shortcut] 140 | from=-3 141 | activation=linear 142 | 143 | [convolutional] 144 | batch_normalize=1 145 | filters=128 146 | size=1 147 | stride=1 148 | pad=1 149 | activation=leaky 150 | 151 | [convolutional] 152 | batch_normalize=1 153 | filters=256 154 | size=3 155 | stride=1 156 | pad=1 157 | activation=leaky 158 | 159 | [shortcut] 160 | from=-3 161 | activation=linear 162 | 163 | [convolutional] 164 | batch_normalize=1 165 | filters=128 166 | size=1 167 | stride=1 168 | pad=1 169 | activation=leaky 170 | 171 | [convolutional] 172 | batch_normalize=1 173 | filters=256 174 | size=3 175 | stride=1 176 | pad=1 177 | activation=leaky 178 | 179 | [shortcut] 180 | from=-3 181 | activation=linear 182 | 183 | [convolutional] 184 | batch_normalize=1 185 | filters=128 186 | size=1 187 | stride=1 188 | pad=1 189 | activation=leaky 190 | 191 | [convolutional] 192 | batch_normalize=1 193 | filters=256 194 | size=3 195 | stride=1 196 | pad=1 197 | activation=leaky 198 | 199 | [shortcut] 200 | from=-3 201 | activation=linear 202 | 203 | 204 | [convolutional] 205 | batch_normalize=1 206 | filters=128 207 | size=1 208 | stride=1 209 | pad=1 210 | activation=leaky 211 | 212 | [convolutional] 213 | batch_normalize=1 214 | filters=256 215 | size=3 216 | stride=1 217 | pad=1 218 | activation=leaky 219 | 220 | [shortcut] 221 | from=-3 222 | activation=linear 223 | 224 | [convolutional] 225 | batch_normalize=1 226 | filters=128 227 | size=1 228 | stride=1 229 | pad=1 230 | activation=leaky 231 | 232 | [convolutional] 233 | batch_normalize=1 234 | filters=256 235 | size=3 236 | stride=1 237 | pad=1 238 | activation=leaky 239 | 240 | [shortcut] 241 | from=-3 242 | activation=linear 243 | 244 | [convolutional] 245 | batch_normalize=1 246 | filters=128 247 | size=1 248 | stride=1 249 | pad=1 250 | activation=leaky 251 | 252 | [convolutional] 253 | batch_normalize=1 254 | filters=256 255 | size=3 256 | stride=1 257 | pad=1 258 | activation=leaky 259 | 260 | [shortcut] 261 | from=-3 262 | activation=linear 263 | 264 | [convolutional] 265 | batch_normalize=1 266 | filters=128 267 | size=1 268 | stride=1 269 | pad=1 270 | activation=leaky 271 | 272 | [convolutional] 273 | batch_normalize=1 274 | filters=256 275 | size=3 276 | stride=1 277 | pad=1 278 | activation=leaky 279 | 280 | [shortcut] 281 | from=-3 282 | activation=linear 283 | 284 | # Downsample 285 | 286 | [convolutional] 287 | batch_normalize=1 288 | filters=512 289 | size=3 290 | stride=2 291 | pad=1 292 | activation=leaky 293 | 294 | [convolutional] 295 | batch_normalize=1 296 | filters=256 297 | size=1 298 | stride=1 299 | pad=1 300 | activation=leaky 301 | 302 | [convolutional] 303 | batch_normalize=1 304 | filters=512 305 | size=3 306 | stride=1 307 | pad=1 308 | activation=leaky 309 | 310 | [shortcut] 311 | from=-3 312 | activation=linear 313 | 314 | 315 | [convolutional] 316 | batch_normalize=1 317 | filters=256 318 | size=1 319 | stride=1 320 | pad=1 321 | activation=leaky 322 | 323 | [convolutional] 324 | batch_normalize=1 325 | filters=512 326 | size=3 327 | stride=1 328 | pad=1 329 | activation=leaky 330 | 331 | [shortcut] 332 | from=-3 333 | activation=linear 334 | 335 | 336 | [convolutional] 337 | batch_normalize=1 338 | filters=256 339 | size=1 340 | stride=1 341 | pad=1 342 | activation=leaky 343 | 344 | [convolutional] 345 | batch_normalize=1 346 | filters=512 347 | size=3 348 | stride=1 349 | pad=1 350 | activation=leaky 351 | 352 | [shortcut] 353 | from=-3 354 | activation=linear 355 | 356 | 357 | [convolutional] 358 | batch_normalize=1 359 | filters=256 360 | size=1 361 | stride=1 362 | pad=1 363 | activation=leaky 364 | 365 | [convolutional] 366 | batch_normalize=1 367 | filters=512 368 | size=3 369 | stride=1 370 | pad=1 371 | activation=leaky 372 | 373 | [shortcut] 374 | from=-3 375 | activation=linear 376 | 377 | [convolutional] 378 | batch_normalize=1 379 | filters=256 380 | size=1 381 | stride=1 382 | pad=1 383 | activation=leaky 384 | 385 | [convolutional] 386 | batch_normalize=1 387 | filters=512 388 | size=3 389 | stride=1 390 | pad=1 391 | activation=leaky 392 | 393 | [shortcut] 394 | from=-3 395 | activation=linear 396 | 397 | 398 | [convolutional] 399 | batch_normalize=1 400 | filters=256 401 | size=1 402 | stride=1 403 | pad=1 404 | activation=leaky 405 | 406 | [convolutional] 407 | batch_normalize=1 408 | filters=512 409 | size=3 410 | stride=1 411 | pad=1 412 | activation=leaky 413 | 414 | [shortcut] 415 | from=-3 416 | activation=linear 417 | 418 | 419 | [convolutional] 420 | batch_normalize=1 421 | filters=256 422 | size=1 423 | stride=1 424 | pad=1 425 | activation=leaky 426 | 427 | [convolutional] 428 | batch_normalize=1 429 | filters=512 430 | size=3 431 | stride=1 432 | pad=1 433 | activation=leaky 434 | 435 | [shortcut] 436 | from=-3 437 | activation=linear 438 | 439 | [convolutional] 440 | batch_normalize=1 441 | filters=256 442 | size=1 443 | stride=1 444 | pad=1 445 | activation=leaky 446 | 447 | [convolutional] 448 | batch_normalize=1 449 | filters=512 450 | size=3 451 | stride=1 452 | pad=1 453 | activation=leaky 454 | 455 | [shortcut] 456 | from=-3 457 | activation=linear 458 | 459 | # Downsample 460 | 461 | [convolutional] 462 | batch_normalize=1 463 | filters=1024 464 | size=3 465 | stride=2 466 | pad=1 467 | activation=leaky 468 | 469 | [convolutional] 470 | batch_normalize=1 471 | filters=512 472 | size=1 473 | stride=1 474 | pad=1 475 | activation=leaky 476 | 477 | [convolutional] 478 | batch_normalize=1 479 | filters=1024 480 | size=3 481 | stride=1 482 | pad=1 483 | activation=leaky 484 | 485 | [shortcut] 486 | from=-3 487 | activation=linear 488 | 489 | [convolutional] 490 | batch_normalize=1 491 | filters=512 492 | size=1 493 | stride=1 494 | pad=1 495 | activation=leaky 496 | 497 | [convolutional] 498 | batch_normalize=1 499 | filters=1024 500 | size=3 501 | stride=1 502 | pad=1 503 | activation=leaky 504 | 505 | [shortcut] 506 | from=-3 507 | activation=linear 508 | 509 | [convolutional] 510 | batch_normalize=1 511 | filters=512 512 | size=1 513 | stride=1 514 | pad=1 515 | activation=leaky 516 | 517 | [convolutional] 518 | batch_normalize=1 519 | filters=1024 520 | size=3 521 | stride=1 522 | pad=1 523 | activation=leaky 524 | 525 | [shortcut] 526 | from=-3 527 | activation=linear 528 | 529 | [convolutional] 530 | batch_normalize=1 531 | filters=512 532 | size=1 533 | stride=1 534 | pad=1 535 | activation=leaky 536 | 537 | [convolutional] 538 | batch_normalize=1 539 | filters=1024 540 | size=3 541 | stride=1 542 | pad=1 543 | activation=leaky 544 | 545 | [shortcut] 546 | from=-3 547 | activation=linear 548 | 549 | ###################### 550 | 551 | [convolutional] 552 | batch_normalize=1 553 | filters=512 554 | size=1 555 | stride=1 556 | pad=1 557 | activation=leaky 558 | 559 | [convolutional] 560 | batch_normalize=1 561 | size=3 562 | stride=1 563 | pad=1 564 | filters=1024 565 | activation=leaky 566 | 567 | [convolutional] 568 | batch_normalize=1 569 | filters=512 570 | size=1 571 | stride=1 572 | pad=1 573 | activation=leaky 574 | 575 | [convolutional] 576 | batch_normalize=1 577 | size=3 578 | stride=1 579 | pad=1 580 | filters=1024 581 | activation=leaky 582 | 583 | [convolutional] 584 | batch_normalize=1 585 | filters=512 586 | size=1 587 | stride=1 588 | pad=1 589 | activation=leaky 590 | 591 | [convolutional] 592 | batch_normalize=1 593 | size=3 594 | stride=1 595 | pad=1 596 | filters=1024 597 | activation=leaky 598 | 599 | [convolutional] 600 | size=1 601 | stride=1 602 | pad=1 603 | filters=255 604 | activation=linear 605 | 606 | 607 | [yolo] 608 | mask = 6,7,8 609 | anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 610 | classes=80 611 | num=9 612 | jitter=.3 613 | ignore_thresh = .7 614 | truth_thresh = 1 615 | random=1 616 | 617 | 618 | [route] 619 | layers = -4 620 | 621 | [convolutional] 622 | batch_normalize=1 623 | filters=256 624 | size=1 625 | stride=1 626 | pad=1 627 | activation=leaky 628 | 629 | [upsample] 630 | stride=2 631 | 632 | [route] 633 | layers = -1, 61 634 | 635 | 636 | 637 | [convolutional] 638 | batch_normalize=1 639 | filters=256 640 | size=1 641 | stride=1 642 | pad=1 643 | activation=leaky 644 | 645 | [convolutional] 646 | batch_normalize=1 647 | size=3 648 | stride=1 649 | pad=1 650 | filters=512 651 | activation=leaky 652 | 653 | [convolutional] 654 | batch_normalize=1 655 | filters=256 656 | size=1 657 | stride=1 658 | pad=1 659 | activation=leaky 660 | 661 | [convolutional] 662 | batch_normalize=1 663 | size=3 664 | stride=1 665 | pad=1 666 | filters=512 667 | activation=leaky 668 | 669 | [convolutional] 670 | batch_normalize=1 671 | filters=256 672 | size=1 673 | stride=1 674 | pad=1 675 | activation=leaky 676 | 677 | [convolutional] 678 | batch_normalize=1 679 | size=3 680 | stride=1 681 | pad=1 682 | filters=512 683 | activation=leaky 684 | 685 | [convolutional] 686 | size=1 687 | stride=1 688 | pad=1 689 | filters=255 690 | activation=linear 691 | 692 | 693 | [yolo] 694 | mask = 3,4,5 695 | anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 696 | classes=80 697 | num=9 698 | jitter=.3 699 | ignore_thresh = .7 700 | truth_thresh = 1 701 | random=1 702 | 703 | 704 | 705 | [route] 706 | layers = -4 707 | 708 | [convolutional] 709 | batch_normalize=1 710 | filters=128 711 | size=1 712 | stride=1 713 | pad=1 714 | activation=leaky 715 | 716 | [upsample] 717 | stride=2 718 | 719 | [route] 720 | layers = -1, 36 721 | 722 | 723 | 724 | [convolutional] 725 | batch_normalize=1 726 | filters=128 727 | size=1 728 | stride=1 729 | pad=1 730 | activation=leaky 731 | 732 | [convolutional] 733 | batch_normalize=1 734 | size=3 735 | stride=1 736 | pad=1 737 | filters=256 738 | activation=leaky 739 | 740 | [convolutional] 741 | batch_normalize=1 742 | filters=128 743 | size=1 744 | stride=1 745 | pad=1 746 | activation=leaky 747 | 748 | [convolutional] 749 | batch_normalize=1 750 | size=3 751 | stride=1 752 | pad=1 753 | filters=256 754 | activation=leaky 755 | 756 | [convolutional] 757 | batch_normalize=1 758 | filters=128 759 | size=1 760 | stride=1 761 | pad=1 762 | activation=leaky 763 | 764 | [convolutional] 765 | batch_normalize=1 766 | size=3 767 | stride=1 768 | pad=1 769 | filters=256 770 | activation=leaky 771 | 772 | [convolutional] 773 | size=1 774 | stride=1 775 | pad=1 776 | filters=255 777 | activation=linear 778 | 779 | 780 | [yolo] 781 | mask = 0,1,2 782 | anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 783 | classes=80 784 | num=9 785 | jitter=.3 786 | ignore_thresh = .7 787 | truth_thresh = 1 788 | random=1 789 | 790 | -------------------------------------------------------------------------------- /yolo/yolov3.weights: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:523e4e69e1d015393a1b0a441cef1d9c7659e3eb2d7e15f793f060a21b32f297 3 | size 248007048 4 | --------------------------------------------------------------------------------