From e320db492c462a74f3993cd17211f25d23e6273f Mon Sep 17 00:00:00 2001 From: Rafael Levy <66163270+HinePo@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:13:15 -0300 Subject: [PATCH 1/5] adding heatmap example I haven't figured out how to add the video output file, but I can link the file below: https://www.kaggle.com/code/hinepo/yolov8-supervision-heatmap-track-count/output?select=heatmap_and_track.mp4 --- examples/heatmap_and_track/README.md | 76 +++++++++ examples/heatmap_and_track/requirements.txt | Bin 0 -> 54 bytes examples/heatmap_and_track/script.py | 176 ++++++++++++++++++++ 3 files changed, 252 insertions(+) create mode 100644 examples/heatmap_and_track/README.md create mode 100644 examples/heatmap_and_track/requirements.txt create mode 100644 examples/heatmap_and_track/script.py diff --git a/examples/heatmap_and_track/README.md b/examples/heatmap_and_track/README.md new file mode 100644 index 00000000..70f53249 --- /dev/null +++ b/examples/heatmap_and_track/README.md @@ -0,0 +1,76 @@ +# heatmap and tracking + +## 👋 hello + +This script performs heatmap and tracking analysis using YOLOv8, an object-detection method and +ByteTrack, a simple yet effective online multi-object tracking method. It uses the +supervision package for multiple tasks such as drawing heatmap annotations, tracking objects, etc. + + +## 💻 install + +- clone repository and navigate to example directory + + ```bash + git clone https://github.com/roboflow/supervision.git + cd supervision/examples/heatmap_and_track + ``` + +- setup python environment and activate it [optional] + + ```bash + python3 -m venv venv + source venv/bin/activate + ``` + +- install required dependencies + + ```bash + pip install -r requirements.txt + ``` + +## 🛠️ script arguments + +- `--source_weights_path`: Required. Specifies the path to the weights file for the +YOLO model. This file contains the trained model data necessary for object detection. +- `--source_video_path`: Required. The path to the source video file that will be +analyzed. This is the input video on which traffic flow analysis will be performed. +- `--target_video_path` (optional): The path to save the output video with annotations. +If not specified, the processed video will be displayed in real-time without being +saved. +- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model +to filter detections. Default is `0.3`. This determines how confident the model should +be to recognize an object in the video. +- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold +for the model. Default is 0.7. This value is used to manage object detection accuracy, +particularly in distinguishing between different objects. +- `heatmap_alpha` (optional): Opacity of the overlay mask, between 0 and 1. +- `radius` (optional): Radius of the heat circle. +- `track_threshold` (optional): Detection confidence threshold for track activation. +- `track_seconds` (optional): Number of seconds to buffer when a track is lost. +- `match_threshold` (optional): Threshold for matching tracks with detections. + +## ⚙️ run + +```bash +python script.py \ +--source_weights_path data/traffic_analysis.pt \ +--source_video_path data/traffic_analysis.mov \ +--confidence_threshold 0.3 \ +--iou_threshold 0.5 \ +--target_video_path data/traffic_analysis_result.mov +``` + +## © license + +This demo integrates two main components, each with its own licensing: + +YOLOv8: The object detection model used in this demo, YOLOv8, is distributed under the +[AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE). You +can find more details about this license here. + +Supervision: The analytics code that powers the zone-based analysis in this demo is +based on the Supervision library, which is licensed under the +[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This +makes the Supervision part of the code fully open source and freely usable in your +projects. diff --git a/examples/heatmap_and_track/requirements.txt b/examples/heatmap_and_track/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..07f88578f6b617a74185027ddef591011bdf3166 GIT binary patch literal 54 zcmezWub82fp@1Qkp@^Z3A(NpPi1Qip7 None: + + ### instantiate model + model = YOLO(source_weights_path) + + ### heatmap config + heat_map_annotator = sv.HeatMapAnnotator( + position = sv.Position.BOTTOM_CENTER, + opacity = heatmap_alpha, + radius = radius, + kernel_size = 25, + top_hue = 0, + low_hue = 125, + ) + + ### annotation config + label_annotator = sv.LabelAnnotator(text_position = sv.Position.CENTER) + + ### get the video fps + cap = cv2.VideoCapture(source_video_path) + fps = int(cap.get(cv2.CAP_PROP_FPS)) + cap.release() + + ### tracker config + byte_tracker = sv.ByteTrack( + track_thresh = track_threshold, + track_buffer = track_seconds * fps, + match_thresh = match_threshold, + frame_rate = fps + ) + + ### video config + video_info = sv.VideoInfo.from_video_path(video_path = source_video_path) + frames_generator = sv.get_video_frames_generator(source_path = source_video_path, stride = 1) + + ### Detect, track, annotate, save + with sv.VideoSink(target_path = target_video_path, video_info = video_info) as sink: + + for frame in frames_generator: + result = model( + source = frame, + classes = [0], # only person class + conf = confidence_threshold, + iou = iou_threshold, + # show_conf = True, + # save_txt = True, + # save_conf = True, + # save = True, + device = None, # use None for CPU, 0 for single GPU, or [0,1] for dual GPU + )[0] + + detections = sv.Detections.from_ultralytics(result) # get detections + + detections = byte_tracker.update_with_detections(detections) # update tracker + + ### draw heatmap + annotated_frame = heat_map_annotator.annotate( + scene = frame.copy(), + detections = detections + ) + + ### draw other attributes from `detections` object + labels = [ + f"#{tracker_id}" + for class_id, tracker_id + in zip(detections.class_id, detections.tracker_id) + ] + + label_annotator.annotate( + scene = annotated_frame, + detections = detections, + labels = labels + ) + + sink.write_frame(frame = annotated_frame) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Heatmap and Tracking with Supervision" + ) + parser.add_argument( + "--source_weights_path", + required=True, + help="Path to the source weights file", + type=str, + ) + parser.add_argument( + "--source_video_path", + required=True, + help="Path to the source video file", + type=str, + ) + parser.add_argument( + "--target_video_path", + required=True, + help="Path to the target video file (output)", + type=str, + ) + parser.add_argument( + "--confidence_threshold", + default=0.35, + help="Confidence threshold for the model", + type=float, + ) + parser.add_argument( + "--iou_threshold", + default=0.5, + help="IOU threshold for the model", + type=float, + ) + parser.add_argument( + "--heatmap_alpha", + default=0.5, + help="Opacity of the overlay mask, between 0 and 1", + type=float, + ) + parser.add_argument( + "--radius", + default=25, + help="Radius of the heat circle", + type=float, + ) + parser.add_argument( + "--track_threshold", + default=0.35, + help="Detection confidence threshold for track activation", + type=float, + ) + parser.add_argument( + "--track_seconds", + default=5, + help="Number of seconds to buffer when a track is lost", + type=int, + ) + parser.add_argument( + "--match_threshold", + default=0.99, + help="Threshold for matching tracks with detections", + type=float, + ) + + args = parser.parse_args() + + heatmap_and_track( + source_weights_path=args.source_weights_path, + source_video_path=args.source_video_path, + target_video_path=args.target_video_path, + confidence_threshold=args.confidence_threshold, + iou_threshold=args.iou_threshold, + heatmap_alpha=args.heatmap_alpha, + radius=args.radius, + track_threshold=args.track_threshold, + track_seconds=args.track_seconds, + match_threshold=args.match_threshold, + ) + +### usage +# python script.py --source_weights_path yolov8s.pt --source_video_path input.mp4 --target_video_path results.mp4 \ No newline at end of file From 670e9c5afc30fe5e4769a0a6e8e9d75edc7dc1d0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:18:55 +0000 Subject: [PATCH 2/5] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto=20?= =?UTF-8?q?format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/heatmap_and_track/requirements.txt | Bin 54 -> 55 bytes examples/heatmap_and_track/script.py | 94 ++++++++++---------- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/examples/heatmap_and_track/requirements.txt b/examples/heatmap_and_track/requirements.txt index 07f88578f6b617a74185027ddef591011bdf3166..7633c75ce300e69239b27e1ae74d9fbb27053445 100644 GIT binary patch delta 36 mcmXprpCBR6#lXc-%8_%fQ7@%8 None: - ### instantiate model model = YOLO(source_weights_path) ### heatmap config heat_map_annotator = sv.HeatMapAnnotator( - position = sv.Position.BOTTOM_CENTER, - opacity = heatmap_alpha, - radius = radius, - kernel_size = 25, - top_hue = 0, - low_hue = 125, + position=sv.Position.BOTTOM_CENTER, + opacity=heatmap_alpha, + radius=radius, + kernel_size=25, + top_hue=0, + low_hue=125, ) ### annotation config - label_annotator = sv.LabelAnnotator(text_position = sv.Position.CENTER) + label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER) ### get the video fps cap = cv2.VideoCapture(source_video_path) @@ -40,56 +41,57 @@ def heatmap_and_track( ### tracker config byte_tracker = sv.ByteTrack( - track_thresh = track_threshold, - track_buffer = track_seconds * fps, - match_thresh = match_threshold, - frame_rate = fps + track_thresh=track_threshold, + track_buffer=track_seconds * fps, + match_thresh=match_threshold, + frame_rate=fps, ) ### video config - video_info = sv.VideoInfo.from_video_path(video_path = source_video_path) - frames_generator = sv.get_video_frames_generator(source_path = source_video_path, stride = 1) + video_info = sv.VideoInfo.from_video_path(video_path=source_video_path) + frames_generator = sv.get_video_frames_generator( + source_path=source_video_path, stride=1 + ) ### Detect, track, annotate, save - with sv.VideoSink(target_path = target_video_path, video_info = video_info) as sink: - + with sv.VideoSink(target_path=target_video_path, video_info=video_info) as sink: for frame in frames_generator: result = model( - source = frame, - classes = [0], # only person class - conf = confidence_threshold, - iou = iou_threshold, + source=frame, + classes=[0], # only person class + conf=confidence_threshold, + iou=iou_threshold, # show_conf = True, # save_txt = True, # save_conf = True, # save = True, - device = None, # use None for CPU, 0 for single GPU, or [0,1] for dual GPU + device=None, # use None for CPU, 0 for single GPU, or [0,1] for dual GPU )[0] - - detections = sv.Detections.from_ultralytics(result) # get detections - - detections = byte_tracker.update_with_detections(detections) # update tracker - + + detections = sv.Detections.from_ultralytics(result) # get detections + + detections = byte_tracker.update_with_detections( + detections + ) # update tracker + ### draw heatmap annotated_frame = heat_map_annotator.annotate( - scene = frame.copy(), - detections = detections + scene=frame.copy(), detections=detections ) - + ### draw other attributes from `detections` object labels = [ f"#{tracker_id}" - for class_id, tracker_id - in zip(detections.class_id, detections.tracker_id) - ] - + for class_id, tracker_id in zip( + detections.class_id, detections.tracker_id + ) + ] + label_annotator.annotate( - scene = annotated_frame, - detections = detections, - labels = labels + scene=annotated_frame, detections=detections, labels=labels ) - - sink.write_frame(frame = annotated_frame) + + sink.write_frame(frame=annotated_frame) if __name__ == "__main__": @@ -131,13 +133,13 @@ if __name__ == "__main__": default=0.5, help="Opacity of the overlay mask, between 0 and 1", type=float, - ) + ) parser.add_argument( "--radius", default=25, help="Radius of the heat circle", type=float, - ) + ) parser.add_argument( "--track_threshold", default=0.35, @@ -149,13 +151,13 @@ if __name__ == "__main__": default=5, help="Number of seconds to buffer when a track is lost", type=int, - ) + ) parser.add_argument( "--match_threshold", default=0.99, help="Threshold for matching tracks with detections", type=float, - ) + ) args = parser.parse_args() @@ -173,4 +175,4 @@ if __name__ == "__main__": ) ### usage -# python script.py --source_weights_path yolov8s.pt --source_video_path input.mp4 --target_video_path results.mp4 \ No newline at end of file +# python script.py --source_weights_path yolov8s.pt --source_video_path input.mp4 --target_video_path results.mp4 From c18f9bf625023a3eb1a70f38ea1e3e25e1d4bbf5 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Mon, 18 Dec 2023 20:01:04 +0300 Subject: [PATCH 3/5] =?UTF-8?q?chore(formatter):=20=F0=9F=A7=B9=20files=20?= =?UTF-8?q?formatting=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- examples/heatmap_and_track/script.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/heatmap_and_track/script.py b/examples/heatmap_and_track/script.py index 120d91ae..d00b3d58 100644 --- a/examples/heatmap_and_track/script.py +++ b/examples/heatmap_and_track/script.py @@ -65,7 +65,7 @@ def heatmap_and_track( # save_txt = True, # save_conf = True, # save = True, - device=None, # use None for CPU, 0 for single GPU, or [0,1] for dual GPU + device=None, # use None = CPU, 0 = single GPU, or [0,1] = dual GPU )[0] detections = sv.Detections.from_ultralytics(result) # get detections @@ -173,6 +173,3 @@ if __name__ == "__main__": track_seconds=args.track_seconds, match_threshold=args.match_threshold, ) - -### usage -# python script.py --source_weights_path yolov8s.pt --source_video_path input.mp4 --target_video_path results.mp4 From 263d88bf7733b6f8c36b822232800bb6f1f2354f Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Mon, 18 Dec 2023 20:06:15 +0300 Subject: [PATCH 4/5] =?UTF-8?q?docs:=20=F0=9F=93=9D=20typos=20fixed=20in?= =?UTF-8?q?=20example=20documentation=20and=20new=20person=20added=20into?= =?UTF-8?q?=20example=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Onuralp SEZER --- examples/README.md | 1 + examples/heatmap_and_track/README.md | 18 +++++++++--------- examples/heatmap_and_track/requirements.txt | Bin 55 -> 51 bytes 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/README.md b/examples/README.md index 20bc3d7d..f3f04c71 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,6 +7,7 @@ interfaces with diverse applications. - [tracking](./tracking) by [@SkalskiP](https://github.com/SkalskiP) - [count people in zone](./count_people_in_zone) by [@SkalskiP](https://github.com/SkalskiP) - [traffic analysis](./traffic_analysis) by [@SkalskiP](https://github.com/SkalskiP) +- [heatmap and track](./heatmap_and_track/) by [@HinePo](https://github.com/HinePo) ## contributing diff --git a/examples/heatmap_and_track/README.md b/examples/heatmap_and_track/README.md index 70f53249..4cf1f2bc 100644 --- a/examples/heatmap_and_track/README.md +++ b/examples/heatmap_and_track/README.md @@ -34,7 +34,7 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac - `--source_weights_path`: Required. Specifies the path to the weights file for the YOLO model. This file contains the trained model data necessary for object detection. - `--source_video_path`: Required. The path to the source video file that will be -analyzed. This is the input video on which traffic flow analysis will be performed. +analyzed. This is the input video on which crowd analysis will be performed. - `--target_video_path` (optional): The path to save the output video with annotations. If not specified, the processed video will be displayed in real-time without being saved. @@ -44,21 +44,21 @@ be to recognize an object in the video. - `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold for the model. Default is 0.7. This value is used to manage object detection accuracy, particularly in distinguishing between different objects. -- `heatmap_alpha` (optional): Opacity of the overlay mask, between 0 and 1. -- `radius` (optional): Radius of the heat circle. -- `track_threshold` (optional): Detection confidence threshold for track activation. -- `track_seconds` (optional): Number of seconds to buffer when a track is lost. -- `match_threshold` (optional): Threshold for matching tracks with detections. +- `--heatmap_alpha` (optional): Opacity of the overlay mask, between 0 and 1. +- `--radius` (optional): Radius of the heat circle. +- `--track_threshold` (optional): Detection confidence threshold for track activation. +- `--track_seconds` (optional): Number of seconds to buffer when a track is lost. +- `--match_threshold` (optional): Threshold for matching tracks with detections. ## ⚙️ run ```bash python script.py \ ---source_weights_path data/traffic_analysis.pt \ ---source_video_path data/traffic_analysis.mov \ +--source_weights_path data/crowd_analysis.pt \ +--source_video_path data/crowd_analysis.mov \ --confidence_threshold 0.3 \ --iou_threshold 0.5 \ ---target_video_path data/traffic_analysis_result.mov +--target_video_path data/crowd_analysis_result.mov ``` ## © license diff --git a/examples/heatmap_and_track/requirements.txt b/examples/heatmap_and_track/requirements.txt index 7633c75ce300e69239b27e1ae74d9fbb27053445..73963c75320eef14509a4138e36ce4c0fa5f302a 100644 GIT binary patch delta 7 OcmXpvo**?*MiKxA_5s@f delta 34 kcmXpupCBd6#ZbzS!%)Id#E=N2D}k&`hGd3f1}+c;0C_D28UO$Q From a2dbe9e53a0d59bd33e2b1e54f67257329e40b41 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 29 Dec 2023 00:24:40 +0300 Subject: [PATCH 5/5] docs(example): readme.md usage fixes feat: default video set to people-walking.mp4 output to output.mp4 fix: re-create requirements.txt(fix end of line corruption) Signed-off-by: Onuralp SEZER --- examples/heatmap_and_track/README.md | 13 ++++++------- examples/heatmap_and_track/requirements.txt | Bin 51 -> 32 bytes examples/heatmap_and_track/script.py | 10 ++++++++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/examples/heatmap_and_track/README.md b/examples/heatmap_and_track/README.md index 4cf1f2bc..c2339956 100644 --- a/examples/heatmap_and_track/README.md +++ b/examples/heatmap_and_track/README.md @@ -33,11 +33,10 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac - `--source_weights_path`: Required. Specifies the path to the weights file for the YOLO model. This file contains the trained model data necessary for object detection. -- `--source_video_path`: Required. The path to the source video file that will be +- `--source_video_path` (optional): The path to the source video file that will be analyzed. This is the input video on which crowd analysis will be performed. -- `--target_video_path` (optional): The path to save the output video with annotations. -If not specified, the processed video will be displayed in real-time without being -saved. +If not specified default is `people-walking.mp4` from supervision assets +- `--target_video_path` (optional): The path to save the output.mp4 video with annotations. - `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model to filter detections. Default is `0.3`. This determines how confident the model should be to recognize an object in the video. @@ -54,11 +53,11 @@ particularly in distinguishing between different objects. ```bash python script.py \ ---source_weights_path data/crowd_analysis.pt \ ---source_video_path data/crowd_analysis.mov \ +--source_weights_path weight.pt \ +--source_video_path input_video.mp4 \ --confidence_threshold 0.3 \ --iou_threshold 0.5 \ ---target_video_path data/crowd_analysis_result.mov +--target_video_path output_video.mp4 ``` ## © license diff --git a/examples/heatmap_and_track/requirements.txt b/examples/heatmap_and_track/requirements.txt index 73963c75320eef14509a4138e36ce4c0fa5f302a..ffc73e1f2a44fa4e96aab1e284c578d306d89b2d 100644 GIT binary patch literal 32 ncmXReEl4dY%Ph{!&x=khE>0~ej^!%NDJe?KsVvD%F6IIN+`$az literal 51 ycmezWub82fp@1Qkp@^Z3A(NpPi1Qip7`T9 str: + download_assets(VideoAssets.PEOPLE_WALKING) + return VideoAssets.PEOPLE_WALKING.value def heatmap_and_track( @@ -106,13 +112,13 @@ if __name__ == "__main__": ) parser.add_argument( "--source_video_path", - required=True, + default=download_video(), help="Path to the source video file", type=str, ) parser.add_argument( "--target_video_path", - required=True, + default="output.mp4", help="Path to the target video file (output)", type=str, )