01304 827609 info@use-ip.co.uk Find us

FAQ Vehicle Counting / Person Counting - Any camera model (Tested on DS-2CD2387G2H-LIU) - yoloV8

trempa92

Centurion!
Trusted Member
Messages
181
Points
28
Greetings everyone,

Sharing some code which also a lot of parts were taken from Object Detection Course - Computer Vision Zone

This version is modified to suit my needs at home camera, so it wont be exactly the same. As well you will have to edit the line coordinates/mask.png yourself as well
Code:
import numpy as np
from ultralytics import YOLO
import cv2
import cvzone
import math
from sort import *

cap = cv2.VideoCapture(r"rtsp://admin:*****@192.168.100.25:554/Streaming/channels/102")  # For Video 1280x720 substream

model = YOLO("../Yolo-Weights/yolov8l.pt")

classNames = ["person", "bicycle", "car", "motorbike", "aeroplane", "bus", "train", "truck", "boat",
              "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat",
              "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella",
              "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
              "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup",
              "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli",
              "carrot", "hot dog", "pizza", "donut", "cake", "chair", "sofa", "pottedplant", "bed",
              "diningtable", "toilet", "tvmonitor", "laptop", "mouse", "remote", "keyboard", "cell phone",
              "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors",
              "teddy bear", "hair drier", "toothbrush"
              ]

mask = cv2.imread("mask.png")

# Tracking
tracker = Sort(max_age=30, min_hits=3, iou_threshold=0.3)

# Line coordinates for counting
limits = [442, 138, 693, 426]

# Counters for different categories and directions
cars_left, cars_right = 0, 0
persons_left, persons_right = 0, 0

# To track previous positions for extrapolation and object count status
previous_positions = {}
counted_objects = {}

while True:
    success, img = cap.read()
    if not success:
        break
   
    imgRegion = cv2.bitwise_and(img, mask)
    results = model(imgRegion, stream=True)

    detections = np.empty((0, 5))

    # Gather detections from the YOLO model
    for r in results:
        boxes = r.boxes
        for box in boxes:
            # Bounding Box
            x1, y1, x2, y2 = box.xyxy[0]
            x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
            w, h = x2 - x1, y2 - y1

            # Confidence
            conf = math.ceil((box.conf[0] * 100)) / 100
            # Class Name
            cls = int(box.cls[0])
            currentClass = classNames[cls]

            # Consider persons and bicycles together, and vehicles separately
            if currentClass in ["car", "truck", "motorbike", "person"] and conf > 0.3:
                currentArray = np.array([x1, y1, x2, y2, conf])
                detections = np.vstack((detections, currentArray))

    # Update the tracker with detections
    resultsTracker = tracker.update(detections)

    # Draw the counting line
    cv2.line(img, (limits[0], limits[1]), (limits[2], limits[3]), (0, 0, 255), 5)

    # Process tracked objects
    for result in resultsTracker:
        x1, y1, x2, y2, id = result
        x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
        w, h = x2 - x1, y2 - y1
        cx, cy = x1 + w // 2, y1 + h // 2  # Calculate object center

        # Draw the object bounding box and ID
        cvzone.cornerRect(img, (x1, y1, w, h), l=9, rt=2, colorR=(255, 0, 255))
        cvzone.putTextRect(img, f' {int(id)}', (max(0, x1), max(35, y1)), scale=2, thickness=3, offset=10)

        # Initialize counted status for this object if not already tracked
        if id not in counted_objects:
            counted_objects[id] = {"counted_left": False, "counted_right": False}

        # Determine if the object is moving left or right
        if id in previous_positions:
            prev_cx, prev_cy = previous_positions[id]

            # If the object crosses the line, track its direction and type
            if ((prev_cy < limits[1] and cy > limits[1]) or (prev_cy > limits[1] and cy < limits[1])
                or (y1 < limits[1] < y2)):  # Check if bounding box crosses the line

                direction = "left" if cx < prev_cx else "right"  # Determine the direction of movement

                # Count cars, trucks, motorbikes
                if currentClass in ["car", "truck", "motorbike"]:
                    if direction == "left" and not counted_objects[id]["counted_left"]:
                        cars_left += 1
                        counted_objects[id]["counted_left"] = True
                    elif direction == "right" and not counted_objects[id]["counted_right"]:
                        cars_right += 1
                        counted_objects[id]["counted_right"] = True

                # Count persons and bicycles together
                elif currentClass in ["person"]:
                    if direction == "left" and not counted_objects[id]["counted_left"]:
                        persons_left += 1
                        counted_objects[id]["counted_left"] = True
                    elif direction == "right" and not counted_objects[id]["counted_right"]:
                        persons_right += 1
                        counted_objects[id]["counted_right"] = True

                print(f"Object {id} crossed the line! Direction: {direction}, Class: {currentClass}")

        # Update the previous position for this object
        previous_positions[id] = (cx, cy)

    # Display counters for all categories
    cv2.putText(img, f"Car <--: {cars_left}", (50, 100), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 0), 2)
    cv2.putText(img, f"Car -->: {cars_right}", (50, 140), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 0), 2)
   
    cv2.putText(img, f"Person <--: {persons_left}", (50, 180), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 0), 2)
    cv2.putText(img, f"Person -->: {persons_right}", (50, 220), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 0), 2)

    # Show the resulting image
    cv2.imshow("Image", img)

    # Break if 'q' is pressed
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# Release the video capture and close windows
cap.release()
cv2.destroyAllWindows()


Translate: Automobili = Cars/ Ljudi = Persons

1725696281107.png



TODO:
-Whenever object trigger linecrossing, send PUT request to textOverlay and change OSD on camera to show actual data instead of watching it on python frame.
-Update database with values to have history of crossing
-Option to make a report depending on time interval
-Separating Bicycle from persons, so it does not count both(when bicycle is added to detection)
-Fix wrong way counter for slow moving objects.

As i am polishing for my scene, i will be updating here the results.

Cheers.
 
Back
Top