supervision
supervision copied to clipboard
line counter is not working anymore?
Search before asking
- [X] 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
Hello there, thank you for opening an Issue ! 🙏🏻 The team was notified and they will get back to you asap.
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"?
@uzumakinaruto19 did you use trigger() yet? I don't see it in your code.
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 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
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()
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
With the current implementation, for the object to be counted, all four corners of the bounding box must cross the line.
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)
I actually suggest putting the camera further away.
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?
Using single point with models like YOLOv8 is not reliable solution unfortunately.
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.
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
This actually worked in my case
Awesome! :tada:
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:
- Version of
supervision
you use. - Minimal reproducible example.
- Full stack trace.
- 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.
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!!
@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]
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
@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 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
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!!!
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
Closing the issue.