Skip to main content

How to Make a Social Distancing Detector || social distancing detector using python || social distancing-detection using python github - Creation Code






Social Distancing:

In public health, social distancing, also called physical distancing, is a set of non-pharmaceutical interventions or measures intended to prevent the spread of a contagious disease by maintaining a physical distance between people and reducing the number of times people come into close contact with each other. It usually involves keeping a certain distance from others(the distance specified differs from country to country and can change with time) and avoiding gathering together in large groups.

Now let's build this system in python

Approach:

  1. First we will create utils for the project
  2. Second we will plot the points also known as birds view
  3. Now we will create the main program


utills.py

import cv2
import numpy as np

# Function to calculate bottom center for all bounding boxes
and transform prespective for all points.
def get_transformed_points(boxes, prespective_transform):
   
    bottom_points = []
    for box in boxes:
        pnts = np.array([[[int(box[0]+(box[2]*0.5)),
int(box[1]+box[3])]]] , dtype="float32")
        #pnts = np.array([[[int(box[0]+(box[2]*0.5)),i
nt(box[1]+(box[3]*0.5))]]] , dtype="float32")
        bd_pnt = cv2.perspectiveTransform(pnts, prespective_transform)[0][0]
        pnt = [int(bd_pnt[0]), int(bd_pnt[1])]
        bottom_points.append(pnt)
       
    return bottom_points

# Function calculates distance between two points(humans).
distance_w, distance_h represents number
# of pixels in 180cm length horizontally and vertically.
We calculate horizontal and vertical
# distance in pixels for two points and get ratio in terms
of 180 cm distance using distance_w, distance_h.
# Then we calculate how much cm distance is horizontally
and vertically and then using pythagoras
# we calculate distance between points in terms of cm.
def cal_dis(p1, p2, distance_w, distance_h):
   
    h = abs(p2[1]-p1[1])
    w = abs(p2[0]-p1[0])
   
    dis_w = float((w/distance_w)*180)
    dis_h = float((h/distance_h)*180)
   
    return int(np.sqrt(((dis_h)**2) + ((dis_w)**2)))

# Function calculates distance between all pairs and calculates closeness ratio.
def get_distances(boxes1, bottom_points, distance_w, distance_h):
   
    distance_mat = []
    bxs = []
   
    for i in range(len(bottom_points)):
        for j in range(len(bottom_points)):
            if i != j:
                dist = cal_dis(bottom_points[i],
bottom_points[j], distance_w, distance_h)
                #dist = int((dis*180)/distance)
                if dist <= 150:
                    closeness = 0
                    distance_mat.append([bottom_points[i],
bottom_points[j], closeness])
                    bxs.append([boxes1[i], boxes1[j], closeness])
                elif dist > 150 and dist <=180:
                    closeness = 1
                    distance_mat.append([bottom_points[i],
bottom_points[j], closeness])
                    bxs.append([boxes1[i], boxes1[j], closeness])      
                else:
                    closeness = 2
                    distance_mat.append([bottom_points[i],
bottom_points[j], closeness])
                    bxs.append([boxes1[i], boxes1[j], closeness])
               
    return distance_mat, bxs
 
# Function gives scale for birds eye view              
def get_scale(W, H):
   
    dis_w = 400
    dis_h = 600
   
    return float(dis_w/W),float(dis_h/H)
   
# Function gives count for humans at high risk, low risk and no risk    
def get_count(distances_mat):

    r = []
    g = []
    y = []
   
    for i in range(len(distances_mat)):

        if distances_mat[i][2] == 0:
            if (distances_mat[i][0] not in r) and
(distances_mat[i][0] not in g) and (distances_mat[i][0] not in y):
                r.append(distances_mat[i][0])
            if (distances_mat[i][1] not in r) and
(distances_mat[i][1] not in g) and (distances_mat[i][1] not in y):
                r.append(distances_mat[i][1])
               
    for i in range(len(distances_mat)):

        if distances_mat[i][2] == 1:
            if (distances_mat[i][0] not in r) and
(distances_mat[i][0] not in g) and (distances_mat[i][0] not in y):
                y.append(distances_mat[i][0])
            if (distances_mat[i][1] not in r) and
(distances_mat[i][1] not in g) and (distances_mat[i][1] not in y):
                y.append(distances_mat[i][1])
       
    for i in range(len(distances_mat)):
   
        if distances_mat[i][2] == 2:
            if (distances_mat[i][0] not in r) and
(distances_mat[i][0] not in g) and (distances_mat[i][0] not in y):
                g.append(distances_mat[i][0])
            if (distances_mat[i][1] not in r) and
(distances_mat[i][1] not in g) and (distances_mat[i][1] not in y):
                g.append(distances_mat[i][1])
   
    return (len(r),len(y),len(g))


plot.py

import cv2
import numpy as np

# Function to draw Bird Eye View for region of
interest(ROI). Red, Yellow, Green points represents risk to human.
# Red: High Risk
# Yellow: Low Risk
# Green: No Risk
def bird_eye_view(frame, distances_mat, bottom_points,
scale_w, scale_h, risk_count):
    h = frame.shape[0]
    w = frame.shape[1]

    red = (0, 0, 255)
    green = (0, 255, 0)
    yellow = (0, 255, 255)
    white = (200, 200, 200)

    blank_image = np.zeros((int(h * scale_h), int(w * scale_w), 3), np.uint8)
    blank_image[:] = white
    warped_pts = []
    r = []
    g = []
    y = []
    for i in range(len(distances_mat)):

        if distances_mat[i][2] == 0:
            if (distances_mat[i][0] not in r) and
(distances_mat[i][0] not in g) and (distances_mat[i][0] not in y):
                r.append(distances_mat[i][0])
            if (distances_mat[i][1] not in r) and
(distances_mat[i][1] not in g) and (distances_mat[i][1] not in y):
                r.append(distances_mat[i][1])

            blank_image = cv2.line(blank_image, (int(distances_mat[i][0][0] *
scale_w), int(distances_mat[i][0][1] * scale_h)), (int(distances_mat[i][1][0]
* scale_w), int(distances_mat[i][1][1]* scale_h)), red, 2)
           
    for i in range(len(distances_mat)):
               
        if distances_mat[i][2] == 1:
            if (distances_mat[i][0] not in r) and
(distances_mat[i][0] not in g) and (distances_mat[i][0] not in y):
                y.append(distances_mat[i][0])
            if (distances_mat[i][1] not in r) and
(distances_mat[i][1] not in g) and (distances_mat[i][1] not in y):
                y.append(distances_mat[i][1])
       
            blank_image = cv2.line(blank_image,
(int(distances_mat[i][0][0] * scale_w), int(distances_mat[i][0][1] * scale_h)),
(int(distances_mat[i][1][0] * scale_w), int(distances_mat[i][1][1]* scale_h)),
yellow, 2)
           
    for i in range(len(distances_mat)):
       
        if distances_mat[i][2] == 2:
            if (distances_mat[i][0] not in r) and
(distances_mat[i][0] not in g) and (distances_mat[i][0] not in y):
                g.append(distances_mat[i][0])
            if (distances_mat[i][1] not in r) and
(distances_mat[i][1] not in g) and (distances_mat[i][1] not in y):
                g.append(distances_mat[i][1])
   
    for i in bottom_points:
        blank_image = cv2.circle(blank_image,
(int(i[0]  * scale_w), int(i[1] * scale_h)), 5, green, 10)
    for i in y:
        blank_image = cv2.circle(blank_image,
(int(i[0]  * scale_w), int(i[1] * scale_h)), 5, yellow, 10)
    for i in r:
        blank_image = cv2.circle(blank_image,
(int(i[0]  * scale_w), int(i[1] * scale_h)), 5, red, 10)
       
    #pad = np.full((100,blank_image.shape[1],3),
[110, 110, 100], dtype=np.uint8)
    #cv2.putText(pad, "-- HIGH RISK : " + str(risk_count[0])
+ " people", (50, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
    #cv2.putText(pad, "-- LOW RISK : " + str(risk_count[1])
+ " people", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)
    #cv2.putText(pad, "-- SAFE : " + str(risk_count[2])
+ " people", (50,  80), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
    #blank_image = np.vstack((blank_image,pad))  
       
    return blank_image
   
# Function to draw bounding boxes according to risk
factor for humans in a frame and draw lines between
# boxes according to risk factor between two humans.
# Red: High Risk
# Yellow: Low Risk
# Green: No Risk
def social_distancing_view(frame, distances_mat, boxes, risk_count):
   
    red = (0, 0, 255)
    green = (0, 255, 0)
    yellow = (0, 255, 255)
   
    for i in range(len(boxes)):

        x,y,w,h = boxes[i][:]
        frame = cv2.rectangle(frame,(x,y),(x+w,y+h),green,2)
                           
    for i in range(len(distances_mat)):

        per1 = distances_mat[i][0]
        per2 = distances_mat[i][1]
        closeness = distances_mat[i][2]
       
        if closeness == 1:
            x,y,w,h = per1[:]
            frame = cv2.rectangle(frame,(x,y),(x+w,y+h),yellow,2)
               
            x1,y1,w1,h1 = per2[:]
            frame = cv2.rectangle(frame,(x1,y1),(x1+w1,y1+h1),yellow,2)
               
            frame = cv2.line(frame, (int(x+w/2),
int(y+h/2)), (int(x1+w1/2), int(y1+h1/2)),yellow, 2)
           
    for i in range(len(distances_mat)):

        per1 = distances_mat[i][0]
        per2 = distances_mat[i][1]
        closeness = distances_mat[i][2]
       
        if closeness == 0:
            x,y,w,h = per1[:]
            frame = cv2.rectangle(frame,(x,y),(x+w,y+h),red,2)
               
            x1,y1,w1,h1 = per2[:]
            frame = cv2.rectangle(frame,(x1,y1),(x1+w1,y1+h1),red,2)
               
            frame = cv2.line(frame, (int(x+w/2), int(y+h/2)),
(int(x1+w1/2), int(y1+h1/2)),red, 2)
           
    pad = np.full((140,frame.shape[1],3), [110, 110, 100], dtype=np.uint8)
    cv2.putText(pad, "Bounding box shows the level of risk to the person.",
(50, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (100, 100, 0), 2)
    cv2.putText(pad, "-- HIGH RISK : " + str(risk_count[0])
+ " people", (50, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 1)
    cv2.putText(pad, "-- LOW RISK : " + str(risk_count[1])
+ " people", (50, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 1)
    cv2.putText(pad, "-- SAFE : " + str(risk_count[2])
+ " people", (50,  100), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
    frame = np.vstack((frame,pad))
           
    return frame

main.py

from unittest import result
import cv2
import numpy as np
import time
import argparse

# own modules
import utills, plot

confid = 0.5
thresh = 0.5
mouse_pts = []


# Function to get points for Region of Interest(ROI) and
distance scale. It will take 8 points on first frame using mouse click    
# event.First four points will define ROI where we want to
moniter social distancing. Also these points should form parallel  
# lines in real world if seen from above(birds eye view).
Next 3 points will define 6 feet(unit length) distance in    
# horizontal and vertical direction and those should form
parallel lines with ROI. Unit length we can take based on choice.
# Points should pe in pre-defined order - bottom-left, bottom-right,
top-right, top-left, point 5 and 6 should form    
# horizontal line and point 5 and 7 should form verticle line.
Horizontal and vertical scale will be different.

# Function will be called on mouse events                                                          

def get_mouse_points(event, x, y, flags, param):

    global mouse_pts
    if event == cv2.EVENT_LBUTTONDOWN:
        if len(mouse_pts) < 4:
            cv2.circle(image, (x, y), 5, (0, 0, 255), 10)
        else:
            cv2.circle(image, (x, y), 5, (255, 0, 0), 10)
           
        if len(mouse_pts) >= 1 and len(mouse_pts) <= 3:
            cv2.line(image, (x, y), (mouse_pts[len(mouse_pts)-1][0],
mouse_pts[len(mouse_pts)-1][1]), (70, 70, 70), 2)
            if len(mouse_pts) == 3:
                cv2.line(image, (x, y), (mouse_pts[0][0], mouse_pts[0][1]),
(70, 70, 70), 2)
       
        if "mouse_pts" not in globals():
            mouse_pts = []
        mouse_pts.append((x, y))
        #print("Point detected")
        #print(mouse_pts)
       


def calculate_social_distancing(vid_path, net, output_dir, output_vid, ln1):
   
    count = 0
    vs = cv2.VideoCapture(vid_path)    

    # Get video height, width and fps
    height = int(vs.get(cv2.CAP_PROP_FRAME_HEIGHT))
    width = int(vs.get(cv2.CAP_PROP_FRAME_WIDTH))
    fps = int(vs.get(cv2.CAP_PROP_FPS))
   
    # Set scale for birds eye view
    # Bird's eye view will only show ROI
    scale_w, scale_h = utills.get_scale(width, height)

    fourcc = cv2.VideoWriter_fourcc(*"XVID")
    output_movie = cv2.VideoWriter("./output_vid/distancing.avi",
fourcc, fps, (width, height))
    bird_movie = cv2.VideoWriter("./output_vid/bird_eye_view.avi",
fourcc, fps, (int(width * scale_w), int(height * scale_h)))
       
    points = []
    global image
   
    while True:

        (grabbed, frame) = vs.read()

        if not grabbed:
            print('here')
            break
           
        (H, W) = frame.shape[:2]
       
        # first frame will be used to draw ROI and horizontal
and vertical 180 cm distance(unit length in both directions)
        if count == 0:
            while True:
                image = frame
                cv2.imshow("image", image)
                cv2.waitKey(1)
                if len(mouse_pts) == 8:
                    cv2.destroyWindow("image")
                    break
               
            points = mouse_pts      
                 
        # Using first 4 points or coordinates for perspective
transformation. The region marked by these 4 points are
        # considered ROI. This polygon shaped ROI is then
warped into a rectangle which becomes the bird eye view.
        # This bird eye view then has the property property
that points are distributed uniformally horizontally and
        # vertically(scale for horizontal and vertical direction
will be different). So for bird eye view points are
        # equally distributed, which was not case for normal view.
        src = np.float32(np.array(points[:4]))
        dst = np.float32([[0, H], [W, H], [W, 0], [0, 0]])
        prespective_transform = cv2.getPerspectiveTransform(src, dst)

        # using next 3 points for horizontal and vertical
unit length(in this case 180 cm)
        pts = np.float32(np.array([points[4:7]]))
        warped_pt = cv2.perspectiveTransform(pts, prespective_transform)[0]
       
        # since bird eye view has property that all points
are equidistant in horizontal and vertical direction.
        # distance_w and distance_h will give us 180 cm
distance in both horizontal and vertical directions
        # (how many pixels will be there in 180cm length in
horizontal and vertical direction of birds eye view),
        # which we can use to calculate distance between two
humans in transformed view or bird eye view
        distance_w = np.sqrt((warped_pt[0][0] - warped_pt[1][0])
** 2 + (warped_pt[0][1] - warped_pt[1][1]) ** 2)
        distance_h = np.sqrt((warped_pt[0][0] - warped_pt[2][0])
** 2 + (warped_pt[0][1] - warped_pt[2][1]) ** 2)
        pnts = np.array(points[:4], np.int32)
        cv2.polylines(frame, [pnts], True, (70, 70, 70), thickness=2)
   
    ####################################################
#########
#######################
   
        # YOLO v3
        blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416),
swapRB=True, crop=False)
        net.setInput(blob)
        start = time.time()
        layerOutputs = net.forward(ln1)
        end = time.time()
        boxes = []
        confidences = []
        classIDs = []  
   
        for output in layerOutputs:
            for detection in output:
                scores = detection[5:]
                classID = np.argmax(scores)
                confidence = scores[classID]
                # detecting humans in frame
                if classID == 0:

                    if confidence > confid:

                        box = detection[0:4] * np.array([W, H, W, H])
                        (centerX, centerY, width, height) = box.astype("int")

                        x = int(centerX - (width / 2))
                        y = int(centerY - (height / 2))

                        boxes.append([x, y, int(width), int(height)])
                        confidences.append(float(confidence))
                        classIDs.append(classID)
                   
        idxs = cv2.dnn.NMSBoxes(boxes, confidences, confid, thresh)
        font = cv2.FONT_HERSHEY_PLAIN
        boxes1 = []
        for i in range(len(boxes)):
            if i in idxs:
                boxes1.append(boxes[i])
                x,y,w,h = boxes[i]
               
        if len(boxes1) == 0:
            count = count + 1
            continue
           
        # Here we will be using bottom center point of
bounding box for all boxes and will transform all those
        # bottom center points to bird eye view
        person_points = utills.get_transformed_points(boxes1,
prespective_transform)
       
        # Here we will calculate distance between transformed points(humans)
        distances_mat, bxs_mat = utills.get_distances(boxes1,
person_points, distance_w, distance_h)
        risk_count = utills.get_count(distances_mat)
   
        frame1 = np.copy(frame)
       
        # Draw bird eye view and frame with bouding boxes
around humans according to risk factor    
        bird_image = plot.bird_eye_view(frame, distances_mat,
person_points, scale_w, scale_h, risk_count)
        img = plot.social_distancing_view(frame1, bxs_mat, boxes1, risk_count)
       
        # Show/write image and videos
        if count != 0:
            output_movie.write(img)
            bird_movie.write(bird_image)
   
            cv2.imshow('Bird Eye View', img)
            cv2.imwrite(output_dir+"frame%d.jpg" % count, img)
            cv2.imwrite(output_dir+"bird_eye_view/frame%d.jpg" % count,
bird_image)
   
        count = count + 1
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
     
    vs.release()
    cv2.destroyAllWindows()
       

if __name__== "__main__":

    # Receives arguements specified by user
    parser = argparse.ArgumentParser()
   
    parser.add_argument('-v', '--video_path', action='store',
dest='video_path', default='./data/example.mp4' ,
                    help='Path for input video')
                   
    parser.add_argument('-o', '--output_dir', action='store',
dest='output_dir', default='./output/' ,
                    help='Path for Output images')
   
    parser.add_argument('-O', '--output_vid', action='store',
dest='output_vid', default='./output_vid/' ,
                    help='Path for Output videos')

    parser.add_argument('-m', '--model', action='store',
dest='model', default='./models/',
                    help='Path for models directory')
                   
    parser.add_argument('-u', '--uop', action='store',
dest='uop', default='NO',
                    help='Use open pose or not (YES/NO)')
                   
    values = parser.parse_args()
   
    model_path = values.model
    if model_path[len(model_path) - 1] != '/':
        model_path = model_path + '/'
       
    output_dir = values.output_dir
    if output_dir[len(output_dir) - 1] != '/':
        output_dir = output_dir + '/'
   
    output_vid = values.output_vid
    if output_vid[len(output_vid) - 1] != '/':
        output_vid = output_vid + '/'


    # load Yolov3 weights
   
    weightsPath = model_path + "yolov3.weights"
    configPath = model_path + "yolov3.cfg"

    net_yl = cv2.dnn.readNetFromDarknet(configPath, weightsPath)
    ln = net_yl.getLayerNames()
    ln1 = [ln[i- 1] for i in net_yl.getUnconnectedOutLayers()]
    # ln1 = [result.append(...,i) for i in net_yl.getUnconnectedOutLayers]

    # set mouse callback

    cv2.namedWindow("image")
    cv2.setMouseCallback("image", get_mouse_points)
    np.random.seed(42)
   
    calculate_social_distancing(values.video_path, net_yl,
output_dir, output_vid, ln1)

Follow my socials:

Youtube:https://www.youtube.com/channel/UCU1qNFntn7dCi9uqyvrGKOg

Instagram: https://www.instagram.com/python.math/

Twitter:https://twitter.com/Pritish369


For any question or coding discussion, you can join my discord or telegram:

Discord:https://discord.gg/be7MmSuV

Telegram:https://t.me/Python_Math_Community

Comments

Popular posts from this blog

Create Ping Pong Game in Python

  Ping Pong Game: Table tennis , also known as  ping-pong  and  whiff-whaff , is a sport in which two or four players hit a lightweight ball, also known as the ping-pong ball, back and forth across a table using small rackets. The game takes place on a hard table divided by a net. Except for the initial serve, the rules are generally as follows: players must allow a ball played toward them to bounce once on their side of the table and must return it so that it bounces on the opposite side at least once. A point is scored when a player fails to return the ball within the rules. Play is fast and demands quick reactions. Spinning the ball alters its trajectory and limits an opponent's options, giving the hitter a great advantage. We can make it using pygame but I keep it more simple we will create this game using only the turtle module so let's drive into the code without wasting any time Code: # Import required library import turtle # Create screen sc = turtle . Screen () sc .

Draw Minecraft Charater in Python

  Minecraft  is a  sandbox video game  developed by the Swedish video game developer  Mojang Studios . The game was created by  Markus "Notch" Persson  in the  Java programming language . Following several early private testing versions, it was first made public in May 2009 before fully releasing in November 2011, with  Jens Bergensten  then taking over development.  Minecraft  has since been ported to several other platforms and is the  best-selling video game of all time , with over 238 million copies sold and nearly 140 million  monthly active users  as of 2021 . We gonna build a character of Minecraft using our creativity and coding skills so let's drive into the code: Code: import turtle as t def eye ( r , a ):     t . fillcolor ( 'brown' )     t . begin_fill ()     t . circle ( r , a )     t . end_fill () t . begin_fill () t . fillcolor ( '#8a00e6' ) t . penup () t . goto ( 0 ,- 50 ) t . pendown () t . right ( 90 ) t . f

How To Draw BMW Logo - In Python

 I know I don't need to introduce BMW as it is a very popular luxury car. Today we gonna draw the BMW logo in python. I know that you can draw it using a pencil and other tools like AutoCAD etc. But we are programmers we talk with computers so let's tell our computer to draw this logo for use with the help of python. Module The only module we will use is that turtle Code: import turtle as t t.begin_fill() t.fillcolor( '#008ac9' ) for i in range ( 50 ):     t.forward( 4 )     t.left( 2 ) t.right(- 80 ) t.forward( 116 ) t.right(- 90 ) t.forward( 132 ) t.end_fill() t.penup() t.pendown() t.right( 90 ) for i in range ( 50 ):     t.forward( 4 )     t.left(- 2 ) t.right( 80 ) t.forward( 116 ) t.forward(- 116 ) t.right( 90 ) t.begin_fill() t.fillcolor( '#008ac9' ) for j in range ( 45 ):     t.forward(- 4 )     t.left(- 2 ) t.right(- 90 ) t.forward( 116 ) t.end_fill() t.right( 180 ) t.forward( 116 ) t.right( 90 ) for i in range ( 47 ):     t.forward( 4 )     t.