Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

line counter is not working anymore? #126

Closed
1 task done
uzumakinaruto19 opened this issue Jun 8, 2023 · 23 comments
Closed
1 task done

line counter is not working anymore? #126

uzumakinaruto19 opened this issue Jun 8, 2023 · 23 comments
Assignees
Labels
question Further information is requested

Comments

@uzumakinaruto19
Copy link

uzumakinaruto19 commented Jun 8, 2023

Search before asking

  • I have searched the Supervision issues and found no similar feature requests.

Question

this is my code I wanted to add multiple lines and count the objects, but the line counter is not working anymore with the latest supervision.

import os
import torch
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="0"
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
import numpy as np
import json
from ultralytics import YOLO
import supervision as sv
# from supervision.tools.line_counter import LineCounter
# from supervision.geometry.dataclasses import Point


# LINE_START = Point(50, 1500)
# LINE_END = Point(3790, 1500)

# line_counter = LineCounter(start=LINE_START, end=LINE_END)

model = YOLO('yolov8n.pt')

def main_func(input_data,frame):

    global model

    if 'ignore' in input_data:
        return "ignore"
    else:

        input_data = json.loads(input_data)

        results= model.track(frame, imgsz=640, classes=0,tracker="bytetrack.yaml")[0]
        detections = sv.Detections.from_yolov8(results)

        if results.boxes.id is not None:
            detections.tracker_id = results.boxes.id.cpu().numpy().astype(int)
            detections.conf = results.boxes.conf.cpu().numpy().astype(float)
            detections.xyxy = results.boxes.xyxy.cpu().numpy().astype(int)
            
            # print(detections.tracker_id)
            # print(detections.conf)
            # print(detections.xyxy)
        else:
            detections.tracker_id = np.array([])
            detections.conf = np.array([])
            detections.xyxy = np.array([])
 
        new_box=detections.xyxy
        new_box = new_box.astype("int").tolist()
        new_ids = detections.tracker_id.astype("int").tolist()
        new_confs = detections.conf.astype("float").tolist()    



        print("count",count)

        output = {
                  'frame_index':input_data['frame_index'],
                  'time_stamp':input_data['time_stamp'],
                  'detections':new_box,
                  'demo_run':input_data['demo_run'],
                  'ids':new_ids,
                  'confs':new_confs
                  }
        
        print(output)
        return output

except for the polygon , how to have multiple lines and update the counts?

Additional

No response

@uzumakinaruto19 uzumakinaruto19 added the question Further information is requested label Jun 8, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Jun 8, 2023

Hello there, thank you for opening an Issue ! 🙏🏻 The team was notified and they will get back to you asap.

@SkalskiP
Copy link
Collaborator

SkalskiP commented Jun 8, 2023

Hi, @uzumakinaruto19 👋🏻! Could you be a bit more specific? What do you mean by "but the line counter is not working anymore with the latest supervision"?

@sceddd
Copy link

sceddd commented Jun 9, 2023

@uzumakinaruto19 did you use trigger() yet? I don't see it in your code.

@SkalskiP SkalskiP self-assigned this Jun 9, 2023
@uzumakinaruto19
Copy link
Author

uzumakinaruto19 commented Jun 9, 2023

Hi, @uzumakinaruto19 👋🏻! Could you be a bit more specific? What do you mean by "but the line counter is not working anymore with the latest supervision"?

hey sorry I'll provide the updated code - this my function for detection and tracking

def process_frame(input_data, frame, line_counter_1, line_counter_2, line_counter_3, line_annotator, box_annotator):

    
    results = model.track(frame, imgsz=640, classes=0, tracker="bytetrack.yaml")[0]
    detections = sv.Detections.from_yolov8(results)

    if results.boxes.id is not None:
        detections.tracker_id = results.boxes.id.cpu().numpy().astype(int)
        detections.conf = results.boxes.conf.cpu().numpy().astype(float)
        detections.xyxy = results.boxes.xyxy.cpu().numpy().astype(int)
    else:
        detections.tracker_id = np.array([])
        detections.conf = np.array([])
        detections.xyxy = np.array([])

    frame = box_annotator.annotate(scene=frame, detections=detections, labels=[str(id) for id in detections.tracker_id])
    line_counter_1.trigger(detections=detections)
    line_counter_2.trigger(detections=detections)
    line_counter_3.trigger(detections=detections)

    line_annotator.annotate(frame=frame, line_counter=line_counter_1)
    line_annotator.annotate(frame=frame, line_counter=line_counter_2)
    line_annotator.annotate(frame=frame, line_counter=line_counter_3)

    new_ids = detections.tracker_id.astype("int").tolist()
    new_box = detections.xyxy.astype("int").tolist()
    new_confs = detections.conf.astype("float").tolist()

    output = {
        'frame_index': input_data['frame_index'],
        'time_stamp': input_data['time_stamp'],
        'detections': new_box,
        'demo_run': input_data['demo_run'],
        'ids': new_ids,
        'confs': new_confs,
        'line_1_out_count': line_counter_1.out_count,
        'line_2_out_count': line_counter_2.out_count,
        'line_3_out_count': line_counter_3.out_count
    }

    print(output)
    return output




def display_frame(output, frame):
    cv2.imshow("Output", frame)
    cv2.waitKey(1)


# Provide the path to your video file
video_path = './test.mp4'
main_func(video_path)

even after using the trigger and the object crosses the line its not getting counted?

@uzumakinaruto19
Copy link
Author

uzumakinaruto19 commented Jun 9, 2023

@uzumakinaruto19 did you use trigger() yet? I don't see it in your code.

i have used , I provided the wrong code, my bad

I think it had to do something with my tracking? well the IDs getting generated are not updating,
I have some issue with the tracker so the ID is getting resued every time, ie, when an object enters and that is assigned with an ID and and when that leaves the frame and a new object comes it is reusing that the ID
it should assign a new ID but it uses the number 1 again and start from there

i know tracking doesn't comes under this repo still , may that be an issue and how to tackle that, because the video might be an RTSP stream and id shouldn't be repeated just continuously and resued for at least sometime say like after 10 mins or something

@SkalskiP any help will be really appreciated

@SkalskiP
Copy link
Collaborator

SkalskiP commented Jun 9, 2023

Hi, @uzumakinaruto19 👋🏻

I'm not sure you can use YOLOv8 ByteTrack this way. Here is a snippet of code I used in the past, and it worked. Important note supervision API may differ a bit. It is pretty old code, but the YOLOv8 tracking part worked 100%.

import cv2

from ultralytics import YOLO
import supervision as sv
import numpy as np


LINE_START = sv.Point(320, 0)
LINE_END = sv.Point(320, 480)


def main():
    line_counter = sv.LineZone(start=LINE_START, end=LINE_END)
    line_annotator = sv.LineZoneAnnotator(thickness=2, text_thickness=1, text_scale=0.5)
    box_annotator = sv.BoxAnnotator(
        thickness=2,
        text_thickness=1,
        text_scale=0.5
    )

    model = YOLO("yolov8l.pt")
    for result in model.track(source=0, show=True, stream=True, agnostic_nms=True):
        
        frame = result.orig_img
        detections = sv.Detections.from_yolov8(result)

        if result.boxes.id is not None:
            detections.tracker_id = result.boxes.id.cpu().numpy().astype(int)
        
        detections = detections[(detections.class_id != 60) & (detections.class_id != 0)]

        labels = [
            f"{tracker_id} {model.model.names[class_id]} {confidence:0.2f}"
            for _, confidence, class_id, tracker_id
            in detections
        ]

        frame = box_annotator.annotate(
            scene=frame, 
            detections=detections,
            labels=labels
        )

        line_counter.trigger(detections=detections)
        line_annotator.annotate(frame=frame, line_counter=line_counter)

        cv2.imshow("yolov8", frame)

        if (cv2.waitKey(30) == 27):
            break


if __name__ == "__main__":
    main()

@uzumakinaruto19
Copy link
Author

uzumakinaruto19 commented Jun 9, 2023

Hi, @uzumakinaruto19 👋🏻

I'm not sure you can use YOLOv8 ByteTrack this way. Here is a snippet of code I used in the past, and it worked. Important note supervision API may differ a bit. It is pretty old code, but the YOLOv8 tracking part worked 100%.

import cv2

from ultralytics import YOLO
import supervision as sv
import numpy as np


LINE_START = sv.Point(320, 0)
LINE_END = sv.Point(320, 480)


def main():
    line_counter = sv.LineZone(start=LINE_START, end=LINE_END)
    line_annotator = sv.LineZoneAnnotator(thickness=2, text_thickness=1, text_scale=0.5)
    box_annotator = sv.BoxAnnotator(
        thickness=2,
        text_thickness=1,
        text_scale=0.5
    )

    model = YOLO("yolov8l.pt")
    for result in model.track(source=0, show=True, stream=True, agnostic_nms=True):
        
        frame = result.orig_img
        detections = sv.Detections.from_yolov8(result)

        if result.boxes.id is not None:
            detections.tracker_id = result.boxes.id.cpu().numpy().astype(int)
        
        detections = detections[(detections.class_id != 60) & (detections.class_id != 0)]

        labels = [
            f"{tracker_id} {model.model.names[class_id]} {confidence:0.2f}"
            for _, confidence, class_id, tracker_id
            in detections
        ]

        frame = box_annotator.annotate(
            scene=frame, 
            detections=detections,
            labels=labels
        )

        line_counter.trigger(detections=detections)
        line_annotator.annotate(frame=frame, line_counter=line_counter)

        cv2.imshow("yolov8", frame)

        if (cv2.waitKey(30) == 27):
            break


if __name__ == "__main__":
    main()

@SkalskiP thanks, I've referred while doing it

well the detection is working fine aswell as the tracking

below shown is the output

WARNING ⚠️ imgsz=[740] must be multiple of max stride 32, updating to [768] {'frame_index': 10, 'time_stamp': 0, 'detections': [[275, 147, 317, 271], [330, 162, 347, 221]], 'demo_run': False, 'ids': [1, 2], 'confs': [0.8728579878807068, 0.731636643409729], 'line_1_out_count': 0} Line 1 Out Count: 0

I'm having issues while counting alone ids what I'm guessing

I have drawn the line but even after the object crossing it is not getting counted

@SkalskiP
Copy link
Collaborator

SkalskiP commented Jun 9, 2023

With the current implementation, for the object to be counted, all four corners of the bounding box must cross the line.

@uzumakinaruto19
Copy link
Author

So u are suggesting extending the line more (I guess I tried that aswell, it is not getting counted at all, anyway I'll try once more), should a go with a polygon(idk whether that will make any sense with that)

@SkalskiP
Copy link
Collaborator

SkalskiP commented Jun 9, 2023

I actually suggest putting the camera further away.

@uzumakinaruto19
Copy link
Author

With the current implementation, for the object to be counted, all four corners of the bounding box must cross the line.

Instead of that can't u use the centroid of the Box, more accurate maybe?

@SkalskiP
Copy link
Collaborator

SkalskiP commented Jun 9, 2023

Using single point with models like YOLOv8 is not reliable solution unfortunately.

@sceddd
Copy link

sceddd commented Jun 9, 2023

Maybe you put the line in the wrong position. The logic is that if all the bounding boxes cross the line, the count will increase. In your case, you should either turn the camera to a higher position or detect the shoes and count if those shoes cross the line or you could take a look at #107 with the @maddust issue by choose the point at bottom center of the bounding box for counting.

@uzumakinaruto19
Copy link
Author

uzumakinaruto19 commented Jun 12, 2023

Maybe you put the line in the wrong position. The logic is that if all the bounding boxes cross the line, the count will increase. In your case, you should either turn the camera to a higher position or detect the shoes and count if those shoes cross the line or you could take a look at #107 with the @maddust issue by choose the point at bottom center of the bounding box for counting.

This actually worked in my case with @maddust suggestion, thanks a ton !! @SkalskiP and @sceddd

when using the polygon zone I'm getting this error, it's running for some seconds and throwing this error

result[:, [0, 2]] = result[:, [0, 2]].clip(0, width)
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed

And is it possible to get how much time an object spends in that zone alone?

Thanks in advance

@SkalskiP
Copy link
Collaborator

This actually worked in my case

Awesome! 🎉

when using the polygon zone I'm getting this error, it's running for some seconds and throwing this error

Could you create a separate bug report where we could address that issue? Please make sure to provide the following:

  1. Version of supervision you use.
  2. Minimal reproducible example.
  3. Full stack trace.
  4. If you can video/image you use for tests.

And is it possible to get how much time an object spends in that zone alone?

It is but you would need a tracker for that. We also do not have ready-to-use tools you could use, but we can guide you and help you build your code.

@uzumakinaruto19
Copy link
Author

Could you create a separate bug report where we could address that issue?

I have raised issue #131 for that

It is but you would need a tracker for that. We also do not have ready-to-use tools you could use, but we can guide you and help you build your code.

I'm already having a tracker(ByteTrack) with yolov8

Really appreciate this repo and the help you guys giving!!

@SkalskiP
Copy link
Collaborator

@uzumakinaruto19 As for your in-zone time counter. Here is a sketch of the python logic that you will need.

# dictionary that stores information when given detection starts to be in the zone
in_zone_state: Dict[int, int] = {}

# loop over frames
for frame_index, frame in ...:

    # trigger your zone with detections
    in_zone = polygon_zone.trigger(detections)
    # filter detections to get only those in the zone
    detections_in_zone = detections[in_zone]
    
    # tracker ids that were in the zone in the current frame
    curr_in_zone = set(detections_in_zone.tracker_id)
    # tracker ids that were in the zone in the previous frame
    prev_in_zone = set(in_zone_state.keys())
    # tracker ids that just entered the zone
    new = curr_in_zone.difference(prev_in_zone)
    # tracker ids that just left the zone
    old = prev_in_zone.difference(curr_in_zone)

    # adding new tracker ids to in_zone_state with current frame_index
    for tracker_id in new:
        in_zone_state[tracker_id] = frame_index

    # removing old tracker ids from in_zone_state and calculating how long it was in zone
    for tracker_id in old:
        in_zone_frame_count = frame_index - in_zone_state[tracker_id]
        in_zone_time = in_zone_frame_count * 1 / VIDEO_FPS
        del in_zone_state[tracker_id]

@uzumakinaruto19
Copy link
Author

One more doubt!

If I want to exclusively call out each of the in/out count values, how do I do that?
like I wanted to store it in an output dictionary like this for an example
output = { 'frame_index': frame_index, 'time_stamp': input_data['time_stamp'], 'detections': new_boxes, 'demo_run': input_data['demo_run'], 'ids': new_ids, 'in_zone_time': in_zone_time, 'avg_wait_time': avg_wait_time, 'l1_in': l1, 'l1_out': l1 }

below is my trigger function

l1 =line_counter_1.trigger(detections=detections)
l2 =line_counter_2.trigger(detections=detections)
l3 =line_counter_3.trigger(detections=detections)

like I need to get whatever that's getting printed on the UI as in/out for each line

@sceddd
Copy link

sceddd commented Jun 13, 2023

@uzumakinaruto19 In this case, I recommend overriding the linezone class. You'll need to add in, and out dicts to store the value you want to take. Take a look at #87 for my issue, for taking the frame index, use VideoInfo to take fps info for taking the sec. more info in #116.

@uzumakinaruto19
Copy link
Author

@uzumakinaruto19 In this case, I recommend overriding the linezone class. You'll need to add in, and out dicts to store the value you want to take. Take a look at #87 for my issue, for taking the frame index, use VideoInfo to take fps info for taking the sec. more info in #116.

Done!! Thanks

@uzumakinaruto19
Copy link
Author

Hey, hope everyone is doing good, is there a way I can reset the counters say after 20 mins? i tried the below code but not really working

def reset_counters():
    global line_counter_values, in_zone_time, frame_index, model, start_time
    print("Resetting counters...")
    model = YOLO("yolov8n.pt")
    in_zone_time = {}
    frame_index = 0
    start_time = time.time()  # Reset the start time
    return {
        "line_counter_1": {"in": 0, "out": 0},
        "line_counter_2": {"in": 0, "out": 0},
        "line_counter_3": {"in": 0, "out": 0}
    }, {}, 0



def main(input_data, frame, line_counter_1, line_counter_2, line_counter_3, zone):
    global model, frame_index, in_zone_state, in_zone_time, start_time, reset_interval
    
    line_annotator = sv.LineZoneAnnotator(thickness=2, text_thickness=1, text_scale=0.5)
    zone_annotator = sv.PolygonZoneAnnotator(zone=zone, color=sv.Color.white(), thickness=1, text_thickness=1, text_scale=0.5)

    # Get the current time
    current_time = time.time()

    # Calculate the elapsed time in seconds
    elapsed_time = current_time - start_time
    print(elapsed_time)

    # Check if the reset interval has passed
    if elapsed_time >= reset_interval:
        #print("Resetting counters...")
        reset_counters()
        print("Resetting counters...")
        # Reset the tracking IDs by setting persist=False
        persist = False
        result = model.track(frame, classes=0, imgsz=480, agnostic_nms=True, tracker="bytetrack.yaml", persist=persist)
        #print("Resetting counters...")


        # Reset the start time for the next interval
        start_time = current_time
    else:
        # Keep the tracking IDs by setting persist=True (default)       
        
        persist = True
        print("Keeping counters...")
        

        result = model.track(frame, classes=0, imgsz=480, agnostic_nms=True ,tracker="bytetrack.yaml", persist=persist)
    result = result[0]
    
 
    detections = sv.Detections.from_yolov8(result)

Thanks in advance!!!

@SkalskiP
Copy link
Collaborator

Hi @uzumakinaruto19 👋🏻 ! If you want to reset line counter counts, all you need to do is:

# line counter
line_zone = sv.LineZone(...)

# line counter reset
counter.tracker_state = {}
counter.in_count = 0
counter.out_count = 0

@SkalskiP
Copy link
Collaborator

Closing the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants