-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathdetector_yolov8.py
138 lines (105 loc) · 4.71 KB
/
detector_yolov8.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
from ultralytics import YOLO
import numpy
import cv2
import torch
import random
from ultralytics.utils.ops import scale_coords, xyxy2xywh
from configurator import config
class Detector:
# yolov7 params
weights = config["yolov8"]["weights"] # model.pt path(s)
names = []
def __init__(self, names):
# set names
self.names = names
# generate random colors
self.colors = [[random.randint(0, 255) for _ in range(3)] for _ in self.names]
# load model
self.model = model = YOLO(config["yolov8"]["weights"])
# set device
self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
self.model.to(self.device)
def set_colors(self, new_colors):
self.colors = new_colors
def get_cls_label(self, cls):
return self.names[cls]
def detect(self, img: numpy.ndarray, verbose = False, half = False, apply_nms = True, nms_config = {}):
"""
Make an inference (prediction) of a given image.
Image size must be multiple of 32 (ex. 640).
And the channels count must be 3 (RGB).
:param img: Input image, must be numpy.ndarray with shape of (samples, channels, height, width).
:return: Detected bounding boxes in a dict format, where's the key is a class.
"""
# get some img data
img_height, img_width, img_channels = img.shape
if img_channels > 3:
return False
# img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) # leave 3 channels
# reshape for PyTorch (samples, channels, height, width)
# img = numpy.moveaxis(img, -1, 0)
result = {}
presults = self.model.predict(
source = img,
verbose = verbose,
half = half,
nms = apply_nms,
conf = nms_config["conf_thres"],
iou = nms_config["iou_thres"],
)
for presult in presults:
for idx, cls in enumerate(presult.boxes.cls):
result.setdefault(self.names[int(cls)], [])
result[self.names[int(cls)]].append({
"cls": int(cls),
"conf": presult.boxes.conf[idx].item(),
"xyxy": presult.boxes[idx].xyxy.cpu().numpy()[0].tolist()
})
return result
def filter_rects(self, bbox_list: dict, e_classes: list):
filtered_rects = []
for i, (c, ds) in enumerate(bbox_list.items()):
if c in e_classes:
for d in ds:
aim_box = {
"tcls": c,
"cls": d["cls"],
"conf": d["conf"],
"xyxy": d["xyxy"]
}
filtered_rects.append(aim_box)
return filtered_rects
def plot_one_box(self, x, img, color=None, label=None, line_thickness=3):
# Plots one bounding box on image img
tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness
color = color or [random.randint(0, 255) for _ in range(3)]
c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
if label:
tf = max(tl - 1, 1) # font thickness
t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled
cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
def paint_boxes(self, img: numpy.ndarray, bbox_list: dict, min_conf: float) -> numpy.ndarray:
"""
Paint predicted bounding boxes to a given image.
:param img: Input image.
:param bbox_list: Detected bounding boxes (expected output from detect method).
:return:
"""
for i, (c, ds) in enumerate(bbox_list.items()):
for d in ds:
if float(d['conf']) > min_conf:
self.plot_one_box(d["xyxy"], img, label=f"{self.get_cls_label(d['cls'])} {d['conf']:.2f}", color=self.colors[int(d["cls"])], line_thickness=2)
return img
def paint_aim_boxes(self, img: numpy.ndarray, aims_list: list) -> numpy.ndarray:
"""
Paint aims bounding boxes to a given image.
:param img: Input image.
:param aims_list: Detected aim boxes (expected output from filter_rects method).
:return:
"""
for aim in aims_list:
self.plot_one_box(aim["xyxy"], img, label=f"{aim['tcls']} {aim['conf']:.2f}", color=self.colors[int(aim["cls"])], line_thickness=2)
return img