vidformer.supervision
vidformer.supervision is the supervision frontend for vidformer.
1""" 2vidformer.supervision is the [supervision](https://supervision.roboflow.com/) frontend for [vidformer](https://github.com/ixlab/vidformer). 3""" 4 5from math import sqrt 6 7import numpy as np 8import supervision as _sv 9from supervision import Color, ColorLookup, ColorPalette, Detections 10from supervision.annotators.utils import resolve_color, resolve_text_background_xyxy 11from supervision.config import CLASS_NAME_DATA_FIELD 12from supervision.detection.utils import spread_out_boxes 13from supervision.geometry.core import Position 14 15import vidformer.cv2 as vf_cv2 16 17CV2_FONT = vf_cv2.FONT_HERSHEY_SIMPLEX 18 19 20class BoxAnnotator: 21 def __init__( 22 self, 23 color=ColorPalette.DEFAULT, 24 thickness=2, 25 color_lookup=ColorLookup.CLASS, 26 ): 27 self.color = color 28 self.thickness = thickness 29 self.color_lookup = color_lookup 30 31 def annotate( 32 self, 33 scene: vf_cv2.Frame, 34 detections: Detections, 35 custom_color_lookup=None, 36 ): 37 for detection_idx in range(len(detections)): 38 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 39 color = resolve_color( 40 color=self.color, 41 detections=detections, 42 detection_idx=detection_idx, 43 color_lookup=( 44 self.color_lookup 45 if custom_color_lookup is None 46 else custom_color_lookup 47 ), 48 ) 49 vf_cv2.rectangle( 50 img=scene, 51 pt1=(x1, y1), 52 pt2=(x2, y2), 53 color=color.as_rgb(), 54 thickness=self.thickness, 55 ) 56 return scene 57 58 59class RoundBoxAnnotator: 60 def __init__( 61 self, 62 color=ColorPalette.DEFAULT, 63 thickness: int = 2, 64 color_lookup: ColorLookup = ColorLookup.CLASS, 65 roundness: float = 0.6, 66 ): 67 self.color = color 68 self.thickness = thickness 69 self.color_lookup = color_lookup 70 if not 0 < roundness <= 1.0: 71 raise ValueError("roundness attribute must be float between (0, 1.0]") 72 self.roundness = roundness 73 74 def annotate( 75 self, 76 scene: vf_cv2.Frame, 77 detections: _sv.Detections, 78 custom_color_lookup=None, 79 ): 80 for detection_idx in range(len(detections)): 81 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 82 color = resolve_color( 83 color=self.color, 84 detections=detections, 85 detection_idx=detection_idx, 86 color_lookup=( 87 self.color_lookup 88 if custom_color_lookup is None 89 else custom_color_lookup 90 ), 91 ) 92 radius = ( 93 int((x2 - x1) // 2 * self.roundness) 94 if abs(x1 - x2) < abs(y1 - y2) 95 else int((y2 - y1) // 2 * self.roundness) 96 ) 97 circle_coordinates = [ 98 ((x1 + radius), (y1 + radius)), 99 ((x2 - radius), (y1 + radius)), 100 ((x2 - radius), (y2 - radius)), 101 ((x1 + radius), (y2 - radius)), 102 ] 103 line_coordinates = [ 104 ((x1 + radius, y1), (x2 - radius, y1)), 105 ((x2, y1 + radius), (x2, y2 - radius)), 106 ((x1 + radius, y2), (x2 - radius, y2)), 107 ((x1, y1 + radius), (x1, y2 - radius)), 108 ] 109 start_angles = (180, 270, 0, 90) 110 end_angles = (270, 360, 90, 180) 111 for center_coordinates, line, start_angle, end_angle in zip( 112 circle_coordinates, line_coordinates, start_angles, end_angles 113 ): 114 vf_cv2.ellipse( 115 img=scene, 116 center=center_coordinates, 117 axes=(radius, radius), 118 angle=0, 119 startAngle=start_angle, 120 endAngle=end_angle, 121 color=color.as_rgb(), 122 thickness=self.thickness, 123 ) 124 vf_cv2.line( 125 img=scene, 126 pt1=line[0], 127 pt2=line[1], 128 color=color.as_rgb(), 129 thickness=self.thickness, 130 ) 131 return scene 132 133 134class BoxCornerAnnotator: 135 def __init__( 136 self, 137 color=ColorPalette.DEFAULT, 138 thickness=4, 139 corner_length=15, 140 color_lookup=ColorLookup.CLASS, 141 ): 142 self.color = color 143 self.thickness: int = thickness 144 self.corner_length: int = corner_length 145 self.color_lookup: ColorLookup = color_lookup 146 147 def annotate( 148 self, 149 scene: vf_cv2.Frame, 150 detections: Detections, 151 custom_color_lookup=None, 152 ): 153 for detection_idx in range(len(detections)): 154 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 155 color = resolve_color( 156 color=self.color, 157 detections=detections, 158 detection_idx=detection_idx, 159 color_lookup=( 160 self.color_lookup 161 if custom_color_lookup is None 162 else custom_color_lookup 163 ), 164 ) 165 corners = [(x1, y1), (x2, y1), (x1, y2), (x2, y2)] 166 for x, y in corners: 167 x_end = x + self.corner_length if x == x1 else x - self.corner_length 168 vf_cv2.line( 169 scene, (x, y), (x_end, y), color.as_rgb(), thickness=self.thickness 170 ) 171 172 y_end = y + self.corner_length if y == y1 else y - self.corner_length 173 vf_cv2.line( 174 scene, (x, y), (x, y_end), color.as_rgb(), thickness=self.thickness 175 ) 176 return scene 177 178 179class ColorAnnotator: 180 def __init__( 181 self, 182 color=ColorPalette.DEFAULT, 183 opacity: float = 0.5, 184 color_lookup: ColorLookup = ColorLookup.CLASS, 185 ): 186 self.color = color 187 self.color_lookup: ColorLookup = color_lookup 188 self.opacity = opacity 189 190 def annotate( 191 self, 192 scene: vf_cv2.Frame, 193 detections: Detections, 194 custom_color_lookup=None, 195 ): 196 scene_with_boxes = scene.copy() 197 for detection_idx in range(len(detections)): 198 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 199 color = resolve_color( 200 color=self.color, 201 detections=detections, 202 detection_idx=detection_idx, 203 color_lookup=( 204 self.color_lookup 205 if custom_color_lookup is None 206 else custom_color_lookup 207 ), 208 ) 209 vf_cv2.rectangle( 210 img=scene_with_boxes, 211 pt1=(x1, y1), 212 pt2=(x2, y2), 213 color=color.as_rgb(), 214 thickness=-1, 215 ) 216 217 vf_cv2.addWeighted( 218 scene_with_boxes, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene 219 ) 220 return scene 221 222 223class CircleAnnotator: 224 def __init__( 225 self, 226 color=ColorPalette.DEFAULT, 227 thickness: int = 2, 228 color_lookup: ColorLookup = ColorLookup.CLASS, 229 ): 230 self.color = color 231 self.thickness: int = thickness 232 self.color_lookup: ColorLookup = color_lookup 233 234 def annotate( 235 self, 236 scene: vf_cv2.Frame, 237 detections: Detections, 238 custom_color_lookup=None, 239 ): 240 for detection_idx in range(len(detections)): 241 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 242 center = ((x1 + x2) // 2, (y1 + y2) // 2) 243 distance = sqrt((x1 - center[0]) ** 2 + (y1 - center[1]) ** 2) 244 color = resolve_color( 245 color=self.color, 246 detections=detections, 247 detection_idx=detection_idx, 248 color_lookup=( 249 self.color_lookup 250 if custom_color_lookup is None 251 else custom_color_lookup 252 ), 253 ) 254 vf_cv2.circle( 255 img=scene, 256 center=center, 257 radius=int(distance), 258 color=color.as_rgb(), 259 thickness=self.thickness, 260 ) 261 262 return scene 263 264 265class DotAnnotator: 266 def __init__( 267 self, 268 color=ColorPalette.DEFAULT, 269 radius: int = 4, 270 position: Position = Position.CENTER, 271 color_lookup: ColorLookup = ColorLookup.CLASS, 272 outline_thickness: int = 0, 273 outline_color=Color.BLACK, 274 ): 275 276 self.color = color 277 self.radius: int = radius 278 self.position: Position = position 279 self.color_lookup: ColorLookup = color_lookup 280 self.outline_thickness = outline_thickness 281 self.outline_color = outline_color 282 283 def annotate( 284 self, 285 scene: vf_cv2.Frame, 286 detections: Detections, 287 custom_color_lookup=None, 288 ): 289 xy = detections.get_anchors_coordinates(anchor=self.position) 290 for detection_idx in range(len(detections)): 291 color = resolve_color( 292 color=self.color, 293 detections=detections, 294 detection_idx=detection_idx, 295 color_lookup=( 296 self.color_lookup 297 if custom_color_lookup is None 298 else custom_color_lookup 299 ), 300 ) 301 center = (int(xy[detection_idx, 0]), int(xy[detection_idx, 1])) 302 303 vf_cv2.circle(scene, center, self.radius, color.as_rgb(), -1) 304 if self.outline_thickness: 305 outline_color = resolve_color( 306 color=self.outline_color, 307 detections=detections, 308 detection_idx=detection_idx, 309 color_lookup=( 310 self.color_lookup 311 if custom_color_lookup is None 312 else custom_color_lookup 313 ), 314 ) 315 vf_cv2.circle( 316 scene, 317 center, 318 self.radius, 319 outline_color.as_rgb(), 320 self.outline_thickness, 321 ) 322 return scene 323 324 325class LabelAnnotator: 326 def __init__( 327 self, 328 color=ColorPalette.DEFAULT, 329 text_color=Color.WHITE, 330 text_scale: float = 0.5, 331 text_thickness: int = 1, 332 text_padding: int = 10, 333 text_position: Position = Position.TOP_LEFT, 334 color_lookup: ColorLookup = ColorLookup.CLASS, 335 border_radius: int = 0, 336 smart_position: bool = False, 337 ): 338 self.border_radius: int = border_radius 339 self.color = color 340 self.text_color = text_color 341 self.text_scale: float = text_scale 342 self.text_thickness: int = text_thickness 343 self.text_padding: int = text_padding 344 self.text_anchor: Position = text_position 345 self.color_lookup: ColorLookup = color_lookup 346 self.smart_position = smart_position 347 348 def annotate( 349 self, 350 scene, 351 detections: Detections, 352 labels, 353 custom_color_lookup=None, 354 ): 355 self._validate_labels(labels, detections) 356 357 labels = self._get_labels_text(detections, labels) 358 label_properties = self._get_label_properties(detections, labels) 359 360 if self.smart_position: 361 xyxy = label_properties[:, :4] 362 xyxy = spread_out_boxes(xyxy) 363 label_properties[:, :4] = xyxy 364 365 self._draw_labels( 366 scene=scene, 367 labels=labels, 368 label_properties=label_properties, 369 detections=detections, 370 custom_color_lookup=custom_color_lookup, 371 ) 372 373 return scene 374 375 def _validate_labels(self, labels, detections: Detections): 376 if labels is not None and len(labels) != len(detections): 377 raise ValueError( 378 f"The number of labels ({len(labels)}) does not match the " 379 f"number of detections ({len(detections)}). Each detection " 380 f"should have exactly 1 label." 381 ) 382 383 def _get_label_properties( 384 self, 385 detections: Detections, 386 labels, 387 ): 388 label_properties = [] 389 anchors_coordinates = detections.get_anchors_coordinates( 390 anchor=self.text_anchor 391 ).astype(int) 392 393 for label, center_coords in zip(labels, anchors_coordinates): 394 (text_w, text_h) = vf_cv2.getTextSize( 395 text=label, 396 fontFace=CV2_FONT, 397 fontScale=self.text_scale, 398 thickness=self.text_thickness, 399 )[0] 400 401 width_padded = text_w + 2 * self.text_padding 402 height_padded = text_h + 2 * self.text_padding 403 404 text_background_xyxy = resolve_text_background_xyxy( 405 center_coordinates=tuple(center_coords), 406 text_wh=(width_padded, height_padded), 407 position=self.text_anchor, 408 ) 409 410 label_properties.append( 411 [ 412 *text_background_xyxy, 413 text_h, 414 ] 415 ) 416 417 return np.array(label_properties).reshape(-1, 5) 418 419 @staticmethod 420 def _get_labels_text(detections: Detections, custom_labels): 421 if custom_labels is not None: 422 return custom_labels 423 424 labels = [] 425 for idx in range(len(detections)): 426 if CLASS_NAME_DATA_FIELD in detections.data: 427 labels.append(detections.data[CLASS_NAME_DATA_FIELD][idx]) 428 elif detections.class_id is not None: 429 labels.append(str(detections.class_id[idx])) 430 else: 431 labels.append(str(idx)) 432 return labels 433 434 def _draw_labels( 435 self, 436 scene, 437 labels, 438 label_properties, 439 detections, 440 custom_color_lookup, 441 ) -> None: 442 assert len(labels) == len(label_properties) == len(detections), ( 443 f"Number of label properties ({len(label_properties)}), " 444 f"labels ({len(labels)}) and detections ({len(detections)}) " 445 "do not match." 446 ) 447 448 color_lookup = ( 449 custom_color_lookup 450 if custom_color_lookup is not None 451 else self.color_lookup 452 ) 453 454 for idx, label_property in enumerate(label_properties): 455 background_color = resolve_color( 456 color=self.color, 457 detections=detections, 458 detection_idx=idx, 459 color_lookup=color_lookup, 460 ) 461 text_color = resolve_color( 462 color=self.text_color, 463 detections=detections, 464 detection_idx=idx, 465 color_lookup=color_lookup, 466 ) 467 468 box_xyxy = label_property[:4] 469 text_height_padded = label_property[4] 470 self.draw_rounded_rectangle( 471 scene=scene, 472 xyxy=box_xyxy, 473 color=background_color.as_rgb(), 474 border_radius=self.border_radius, 475 ) 476 477 text_x = box_xyxy[0] + self.text_padding 478 text_y = box_xyxy[1] + self.text_padding + text_height_padded 479 vf_cv2.putText( 480 img=scene, 481 text=labels[idx], 482 org=(text_x, text_y), 483 fontFace=CV2_FONT, 484 fontScale=self.text_scale, 485 color=text_color.as_rgb(), 486 thickness=self.text_thickness, 487 lineType=vf_cv2.LINE_AA, 488 ) 489 490 @staticmethod 491 def draw_rounded_rectangle( 492 scene: np.ndarray, 493 xyxy, 494 color, 495 border_radius: int, 496 ) -> np.ndarray: 497 x1, y1, x2, y2 = xyxy 498 width = x2 - x1 499 height = y2 - y1 500 501 border_radius = min(border_radius, min(width, height) // 2) 502 503 if border_radius <= 0: 504 vf_cv2.rectangle( 505 img=scene, 506 pt1=(x1, y1), 507 pt2=(x2, y2), 508 color=color, 509 thickness=-1, 510 ) 511 else: 512 rectangle_coordinates = [ 513 ((x1 + border_radius, y1), (x2 - border_radius, y2)), 514 ((x1, y1 + border_radius), (x2, y2 - border_radius)), 515 ] 516 circle_centers = [ 517 (x1 + border_radius, y1 + border_radius), 518 (x2 - border_radius, y1 + border_radius), 519 (x1 + border_radius, y2 - border_radius), 520 (x2 - border_radius, y2 - border_radius), 521 ] 522 523 for coordinates in rectangle_coordinates: 524 vf_cv2.rectangle( 525 img=scene, 526 pt1=coordinates[0], 527 pt2=coordinates[1], 528 color=color, 529 thickness=-1, 530 ) 531 for center in circle_centers: 532 vf_cv2.circle( 533 img=scene, 534 center=center, 535 radius=border_radius, 536 color=color, 537 thickness=-1, 538 ) 539 return scene
CV2_FONT =
0
class
BoxAnnotator:
21class BoxAnnotator: 22 def __init__( 23 self, 24 color=ColorPalette.DEFAULT, 25 thickness=2, 26 color_lookup=ColorLookup.CLASS, 27 ): 28 self.color = color 29 self.thickness = thickness 30 self.color_lookup = color_lookup 31 32 def annotate( 33 self, 34 scene: vf_cv2.Frame, 35 detections: Detections, 36 custom_color_lookup=None, 37 ): 38 for detection_idx in range(len(detections)): 39 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 40 color = resolve_color( 41 color=self.color, 42 detections=detections, 43 detection_idx=detection_idx, 44 color_lookup=( 45 self.color_lookup 46 if custom_color_lookup is None 47 else custom_color_lookup 48 ), 49 ) 50 vf_cv2.rectangle( 51 img=scene, 52 pt1=(x1, y1), 53 pt2=(x2, y2), 54 color=color.as_rgb(), 55 thickness=self.thickness, 56 ) 57 return scene
BoxAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness=2, color_lookup=<ColorLookup.CLASS: 'class'>)
def
annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
32 def annotate( 33 self, 34 scene: vf_cv2.Frame, 35 detections: Detections, 36 custom_color_lookup=None, 37 ): 38 for detection_idx in range(len(detections)): 39 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 40 color = resolve_color( 41 color=self.color, 42 detections=detections, 43 detection_idx=detection_idx, 44 color_lookup=( 45 self.color_lookup 46 if custom_color_lookup is None 47 else custom_color_lookup 48 ), 49 ) 50 vf_cv2.rectangle( 51 img=scene, 52 pt1=(x1, y1), 53 pt2=(x2, y2), 54 color=color.as_rgb(), 55 thickness=self.thickness, 56 ) 57 return scene
class
RoundBoxAnnotator:
60class RoundBoxAnnotator: 61 def __init__( 62 self, 63 color=ColorPalette.DEFAULT, 64 thickness: int = 2, 65 color_lookup: ColorLookup = ColorLookup.CLASS, 66 roundness: float = 0.6, 67 ): 68 self.color = color 69 self.thickness = thickness 70 self.color_lookup = color_lookup 71 if not 0 < roundness <= 1.0: 72 raise ValueError("roundness attribute must be float between (0, 1.0]") 73 self.roundness = roundness 74 75 def annotate( 76 self, 77 scene: vf_cv2.Frame, 78 detections: _sv.Detections, 79 custom_color_lookup=None, 80 ): 81 for detection_idx in range(len(detections)): 82 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 83 color = resolve_color( 84 color=self.color, 85 detections=detections, 86 detection_idx=detection_idx, 87 color_lookup=( 88 self.color_lookup 89 if custom_color_lookup is None 90 else custom_color_lookup 91 ), 92 ) 93 radius = ( 94 int((x2 - x1) // 2 * self.roundness) 95 if abs(x1 - x2) < abs(y1 - y2) 96 else int((y2 - y1) // 2 * self.roundness) 97 ) 98 circle_coordinates = [ 99 ((x1 + radius), (y1 + radius)), 100 ((x2 - radius), (y1 + radius)), 101 ((x2 - radius), (y2 - radius)), 102 ((x1 + radius), (y2 - radius)), 103 ] 104 line_coordinates = [ 105 ((x1 + radius, y1), (x2 - radius, y1)), 106 ((x2, y1 + radius), (x2, y2 - radius)), 107 ((x1 + radius, y2), (x2 - radius, y2)), 108 ((x1, y1 + radius), (x1, y2 - radius)), 109 ] 110 start_angles = (180, 270, 0, 90) 111 end_angles = (270, 360, 90, 180) 112 for center_coordinates, line, start_angle, end_angle in zip( 113 circle_coordinates, line_coordinates, start_angles, end_angles 114 ): 115 vf_cv2.ellipse( 116 img=scene, 117 center=center_coordinates, 118 axes=(radius, radius), 119 angle=0, 120 startAngle=start_angle, 121 endAngle=end_angle, 122 color=color.as_rgb(), 123 thickness=self.thickness, 124 ) 125 vf_cv2.line( 126 img=scene, 127 pt1=line[0], 128 pt2=line[1], 129 color=color.as_rgb(), 130 thickness=self.thickness, 131 ) 132 return scene
RoundBoxAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness: int = 2, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>, roundness: float = 0.6)
61 def __init__( 62 self, 63 color=ColorPalette.DEFAULT, 64 thickness: int = 2, 65 color_lookup: ColorLookup = ColorLookup.CLASS, 66 roundness: float = 0.6, 67 ): 68 self.color = color 69 self.thickness = thickness 70 self.color_lookup = color_lookup 71 if not 0 < roundness <= 1.0: 72 raise ValueError("roundness attribute must be float between (0, 1.0]") 73 self.roundness = roundness
def
annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
75 def annotate( 76 self, 77 scene: vf_cv2.Frame, 78 detections: _sv.Detections, 79 custom_color_lookup=None, 80 ): 81 for detection_idx in range(len(detections)): 82 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 83 color = resolve_color( 84 color=self.color, 85 detections=detections, 86 detection_idx=detection_idx, 87 color_lookup=( 88 self.color_lookup 89 if custom_color_lookup is None 90 else custom_color_lookup 91 ), 92 ) 93 radius = ( 94 int((x2 - x1) // 2 * self.roundness) 95 if abs(x1 - x2) < abs(y1 - y2) 96 else int((y2 - y1) // 2 * self.roundness) 97 ) 98 circle_coordinates = [ 99 ((x1 + radius), (y1 + radius)), 100 ((x2 - radius), (y1 + radius)), 101 ((x2 - radius), (y2 - radius)), 102 ((x1 + radius), (y2 - radius)), 103 ] 104 line_coordinates = [ 105 ((x1 + radius, y1), (x2 - radius, y1)), 106 ((x2, y1 + radius), (x2, y2 - radius)), 107 ((x1 + radius, y2), (x2 - radius, y2)), 108 ((x1, y1 + radius), (x1, y2 - radius)), 109 ] 110 start_angles = (180, 270, 0, 90) 111 end_angles = (270, 360, 90, 180) 112 for center_coordinates, line, start_angle, end_angle in zip( 113 circle_coordinates, line_coordinates, start_angles, end_angles 114 ): 115 vf_cv2.ellipse( 116 img=scene, 117 center=center_coordinates, 118 axes=(radius, radius), 119 angle=0, 120 startAngle=start_angle, 121 endAngle=end_angle, 122 color=color.as_rgb(), 123 thickness=self.thickness, 124 ) 125 vf_cv2.line( 126 img=scene, 127 pt1=line[0], 128 pt2=line[1], 129 color=color.as_rgb(), 130 thickness=self.thickness, 131 ) 132 return scene
class
BoxCornerAnnotator:
135class BoxCornerAnnotator: 136 def __init__( 137 self, 138 color=ColorPalette.DEFAULT, 139 thickness=4, 140 corner_length=15, 141 color_lookup=ColorLookup.CLASS, 142 ): 143 self.color = color 144 self.thickness: int = thickness 145 self.corner_length: int = corner_length 146 self.color_lookup: ColorLookup = color_lookup 147 148 def annotate( 149 self, 150 scene: vf_cv2.Frame, 151 detections: Detections, 152 custom_color_lookup=None, 153 ): 154 for detection_idx in range(len(detections)): 155 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 156 color = resolve_color( 157 color=self.color, 158 detections=detections, 159 detection_idx=detection_idx, 160 color_lookup=( 161 self.color_lookup 162 if custom_color_lookup is None 163 else custom_color_lookup 164 ), 165 ) 166 corners = [(x1, y1), (x2, y1), (x1, y2), (x2, y2)] 167 for x, y in corners: 168 x_end = x + self.corner_length if x == x1 else x - self.corner_length 169 vf_cv2.line( 170 scene, (x, y), (x_end, y), color.as_rgb(), thickness=self.thickness 171 ) 172 173 y_end = y + self.corner_length if y == y1 else y - self.corner_length 174 vf_cv2.line( 175 scene, (x, y), (x, y_end), color.as_rgb(), thickness=self.thickness 176 ) 177 return scene
BoxCornerAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness=4, corner_length=15, color_lookup=<ColorLookup.CLASS: 'class'>)
def
annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
148 def annotate( 149 self, 150 scene: vf_cv2.Frame, 151 detections: Detections, 152 custom_color_lookup=None, 153 ): 154 for detection_idx in range(len(detections)): 155 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 156 color = resolve_color( 157 color=self.color, 158 detections=detections, 159 detection_idx=detection_idx, 160 color_lookup=( 161 self.color_lookup 162 if custom_color_lookup is None 163 else custom_color_lookup 164 ), 165 ) 166 corners = [(x1, y1), (x2, y1), (x1, y2), (x2, y2)] 167 for x, y in corners: 168 x_end = x + self.corner_length if x == x1 else x - self.corner_length 169 vf_cv2.line( 170 scene, (x, y), (x_end, y), color.as_rgb(), thickness=self.thickness 171 ) 172 173 y_end = y + self.corner_length if y == y1 else y - self.corner_length 174 vf_cv2.line( 175 scene, (x, y), (x, y_end), color.as_rgb(), thickness=self.thickness 176 ) 177 return scene
class
ColorAnnotator:
180class ColorAnnotator: 181 def __init__( 182 self, 183 color=ColorPalette.DEFAULT, 184 opacity: float = 0.5, 185 color_lookup: ColorLookup = ColorLookup.CLASS, 186 ): 187 self.color = color 188 self.color_lookup: ColorLookup = color_lookup 189 self.opacity = opacity 190 191 def annotate( 192 self, 193 scene: vf_cv2.Frame, 194 detections: Detections, 195 custom_color_lookup=None, 196 ): 197 scene_with_boxes = scene.copy() 198 for detection_idx in range(len(detections)): 199 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 200 color = resolve_color( 201 color=self.color, 202 detections=detections, 203 detection_idx=detection_idx, 204 color_lookup=( 205 self.color_lookup 206 if custom_color_lookup is None 207 else custom_color_lookup 208 ), 209 ) 210 vf_cv2.rectangle( 211 img=scene_with_boxes, 212 pt1=(x1, y1), 213 pt2=(x2, y2), 214 color=color.as_rgb(), 215 thickness=-1, 216 ) 217 218 vf_cv2.addWeighted( 219 scene_with_boxes, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene 220 ) 221 return scene
ColorAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), opacity: float = 0.5, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>)
def
annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
191 def annotate( 192 self, 193 scene: vf_cv2.Frame, 194 detections: Detections, 195 custom_color_lookup=None, 196 ): 197 scene_with_boxes = scene.copy() 198 for detection_idx in range(len(detections)): 199 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 200 color = resolve_color( 201 color=self.color, 202 detections=detections, 203 detection_idx=detection_idx, 204 color_lookup=( 205 self.color_lookup 206 if custom_color_lookup is None 207 else custom_color_lookup 208 ), 209 ) 210 vf_cv2.rectangle( 211 img=scene_with_boxes, 212 pt1=(x1, y1), 213 pt2=(x2, y2), 214 color=color.as_rgb(), 215 thickness=-1, 216 ) 217 218 vf_cv2.addWeighted( 219 scene_with_boxes, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene 220 ) 221 return scene
class
CircleAnnotator:
224class CircleAnnotator: 225 def __init__( 226 self, 227 color=ColorPalette.DEFAULT, 228 thickness: int = 2, 229 color_lookup: ColorLookup = ColorLookup.CLASS, 230 ): 231 self.color = color 232 self.thickness: int = thickness 233 self.color_lookup: ColorLookup = color_lookup 234 235 def annotate( 236 self, 237 scene: vf_cv2.Frame, 238 detections: Detections, 239 custom_color_lookup=None, 240 ): 241 for detection_idx in range(len(detections)): 242 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 243 center = ((x1 + x2) // 2, (y1 + y2) // 2) 244 distance = sqrt((x1 - center[0]) ** 2 + (y1 - center[1]) ** 2) 245 color = resolve_color( 246 color=self.color, 247 detections=detections, 248 detection_idx=detection_idx, 249 color_lookup=( 250 self.color_lookup 251 if custom_color_lookup is None 252 else custom_color_lookup 253 ), 254 ) 255 vf_cv2.circle( 256 img=scene, 257 center=center, 258 radius=int(distance), 259 color=color.as_rgb(), 260 thickness=self.thickness, 261 ) 262 263 return scene
CircleAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), thickness: int = 2, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>)
def
annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
235 def annotate( 236 self, 237 scene: vf_cv2.Frame, 238 detections: Detections, 239 custom_color_lookup=None, 240 ): 241 for detection_idx in range(len(detections)): 242 x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int) 243 center = ((x1 + x2) // 2, (y1 + y2) // 2) 244 distance = sqrt((x1 - center[0]) ** 2 + (y1 - center[1]) ** 2) 245 color = resolve_color( 246 color=self.color, 247 detections=detections, 248 detection_idx=detection_idx, 249 color_lookup=( 250 self.color_lookup 251 if custom_color_lookup is None 252 else custom_color_lookup 253 ), 254 ) 255 vf_cv2.circle( 256 img=scene, 257 center=center, 258 radius=int(distance), 259 color=color.as_rgb(), 260 thickness=self.thickness, 261 ) 262 263 return scene
class
DotAnnotator:
266class DotAnnotator: 267 def __init__( 268 self, 269 color=ColorPalette.DEFAULT, 270 radius: int = 4, 271 position: Position = Position.CENTER, 272 color_lookup: ColorLookup = ColorLookup.CLASS, 273 outline_thickness: int = 0, 274 outline_color=Color.BLACK, 275 ): 276 277 self.color = color 278 self.radius: int = radius 279 self.position: Position = position 280 self.color_lookup: ColorLookup = color_lookup 281 self.outline_thickness = outline_thickness 282 self.outline_color = outline_color 283 284 def annotate( 285 self, 286 scene: vf_cv2.Frame, 287 detections: Detections, 288 custom_color_lookup=None, 289 ): 290 xy = detections.get_anchors_coordinates(anchor=self.position) 291 for detection_idx in range(len(detections)): 292 color = resolve_color( 293 color=self.color, 294 detections=detections, 295 detection_idx=detection_idx, 296 color_lookup=( 297 self.color_lookup 298 if custom_color_lookup is None 299 else custom_color_lookup 300 ), 301 ) 302 center = (int(xy[detection_idx, 0]), int(xy[detection_idx, 1])) 303 304 vf_cv2.circle(scene, center, self.radius, color.as_rgb(), -1) 305 if self.outline_thickness: 306 outline_color = resolve_color( 307 color=self.outline_color, 308 detections=detections, 309 detection_idx=detection_idx, 310 color_lookup=( 311 self.color_lookup 312 if custom_color_lookup is None 313 else custom_color_lookup 314 ), 315 ) 316 vf_cv2.circle( 317 scene, 318 center, 319 self.radius, 320 outline_color.as_rgb(), 321 self.outline_thickness, 322 ) 323 return scene
DotAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), radius: int = 4, position: supervision.geometry.core.Position = <Position.CENTER: 'CENTER'>, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>, outline_thickness: int = 0, outline_color=Color(r=0, g=0, b=0))
267 def __init__( 268 self, 269 color=ColorPalette.DEFAULT, 270 radius: int = 4, 271 position: Position = Position.CENTER, 272 color_lookup: ColorLookup = ColorLookup.CLASS, 273 outline_thickness: int = 0, 274 outline_color=Color.BLACK, 275 ): 276 277 self.color = color 278 self.radius: int = radius 279 self.position: Position = position 280 self.color_lookup: ColorLookup = color_lookup 281 self.outline_thickness = outline_thickness 282 self.outline_color = outline_color
def
annotate( self, scene: vidformer.cv2.Frame, detections: supervision.detection.core.Detections, custom_color_lookup=None):
284 def annotate( 285 self, 286 scene: vf_cv2.Frame, 287 detections: Detections, 288 custom_color_lookup=None, 289 ): 290 xy = detections.get_anchors_coordinates(anchor=self.position) 291 for detection_idx in range(len(detections)): 292 color = resolve_color( 293 color=self.color, 294 detections=detections, 295 detection_idx=detection_idx, 296 color_lookup=( 297 self.color_lookup 298 if custom_color_lookup is None 299 else custom_color_lookup 300 ), 301 ) 302 center = (int(xy[detection_idx, 0]), int(xy[detection_idx, 1])) 303 304 vf_cv2.circle(scene, center, self.radius, color.as_rgb(), -1) 305 if self.outline_thickness: 306 outline_color = resolve_color( 307 color=self.outline_color, 308 detections=detections, 309 detection_idx=detection_idx, 310 color_lookup=( 311 self.color_lookup 312 if custom_color_lookup is None 313 else custom_color_lookup 314 ), 315 ) 316 vf_cv2.circle( 317 scene, 318 center, 319 self.radius, 320 outline_color.as_rgb(), 321 self.outline_thickness, 322 ) 323 return scene
class
LabelAnnotator:
326class LabelAnnotator: 327 def __init__( 328 self, 329 color=ColorPalette.DEFAULT, 330 text_color=Color.WHITE, 331 text_scale: float = 0.5, 332 text_thickness: int = 1, 333 text_padding: int = 10, 334 text_position: Position = Position.TOP_LEFT, 335 color_lookup: ColorLookup = ColorLookup.CLASS, 336 border_radius: int = 0, 337 smart_position: bool = False, 338 ): 339 self.border_radius: int = border_radius 340 self.color = color 341 self.text_color = text_color 342 self.text_scale: float = text_scale 343 self.text_thickness: int = text_thickness 344 self.text_padding: int = text_padding 345 self.text_anchor: Position = text_position 346 self.color_lookup: ColorLookup = color_lookup 347 self.smart_position = smart_position 348 349 def annotate( 350 self, 351 scene, 352 detections: Detections, 353 labels, 354 custom_color_lookup=None, 355 ): 356 self._validate_labels(labels, detections) 357 358 labels = self._get_labels_text(detections, labels) 359 label_properties = self._get_label_properties(detections, labels) 360 361 if self.smart_position: 362 xyxy = label_properties[:, :4] 363 xyxy = spread_out_boxes(xyxy) 364 label_properties[:, :4] = xyxy 365 366 self._draw_labels( 367 scene=scene, 368 labels=labels, 369 label_properties=label_properties, 370 detections=detections, 371 custom_color_lookup=custom_color_lookup, 372 ) 373 374 return scene 375 376 def _validate_labels(self, labels, detections: Detections): 377 if labels is not None and len(labels) != len(detections): 378 raise ValueError( 379 f"The number of labels ({len(labels)}) does not match the " 380 f"number of detections ({len(detections)}). Each detection " 381 f"should have exactly 1 label." 382 ) 383 384 def _get_label_properties( 385 self, 386 detections: Detections, 387 labels, 388 ): 389 label_properties = [] 390 anchors_coordinates = detections.get_anchors_coordinates( 391 anchor=self.text_anchor 392 ).astype(int) 393 394 for label, center_coords in zip(labels, anchors_coordinates): 395 (text_w, text_h) = vf_cv2.getTextSize( 396 text=label, 397 fontFace=CV2_FONT, 398 fontScale=self.text_scale, 399 thickness=self.text_thickness, 400 )[0] 401 402 width_padded = text_w + 2 * self.text_padding 403 height_padded = text_h + 2 * self.text_padding 404 405 text_background_xyxy = resolve_text_background_xyxy( 406 center_coordinates=tuple(center_coords), 407 text_wh=(width_padded, height_padded), 408 position=self.text_anchor, 409 ) 410 411 label_properties.append( 412 [ 413 *text_background_xyxy, 414 text_h, 415 ] 416 ) 417 418 return np.array(label_properties).reshape(-1, 5) 419 420 @staticmethod 421 def _get_labels_text(detections: Detections, custom_labels): 422 if custom_labels is not None: 423 return custom_labels 424 425 labels = [] 426 for idx in range(len(detections)): 427 if CLASS_NAME_DATA_FIELD in detections.data: 428 labels.append(detections.data[CLASS_NAME_DATA_FIELD][idx]) 429 elif detections.class_id is not None: 430 labels.append(str(detections.class_id[idx])) 431 else: 432 labels.append(str(idx)) 433 return labels 434 435 def _draw_labels( 436 self, 437 scene, 438 labels, 439 label_properties, 440 detections, 441 custom_color_lookup, 442 ) -> None: 443 assert len(labels) == len(label_properties) == len(detections), ( 444 f"Number of label properties ({len(label_properties)}), " 445 f"labels ({len(labels)}) and detections ({len(detections)}) " 446 "do not match." 447 ) 448 449 color_lookup = ( 450 custom_color_lookup 451 if custom_color_lookup is not None 452 else self.color_lookup 453 ) 454 455 for idx, label_property in enumerate(label_properties): 456 background_color = resolve_color( 457 color=self.color, 458 detections=detections, 459 detection_idx=idx, 460 color_lookup=color_lookup, 461 ) 462 text_color = resolve_color( 463 color=self.text_color, 464 detections=detections, 465 detection_idx=idx, 466 color_lookup=color_lookup, 467 ) 468 469 box_xyxy = label_property[:4] 470 text_height_padded = label_property[4] 471 self.draw_rounded_rectangle( 472 scene=scene, 473 xyxy=box_xyxy, 474 color=background_color.as_rgb(), 475 border_radius=self.border_radius, 476 ) 477 478 text_x = box_xyxy[0] + self.text_padding 479 text_y = box_xyxy[1] + self.text_padding + text_height_padded 480 vf_cv2.putText( 481 img=scene, 482 text=labels[idx], 483 org=(text_x, text_y), 484 fontFace=CV2_FONT, 485 fontScale=self.text_scale, 486 color=text_color.as_rgb(), 487 thickness=self.text_thickness, 488 lineType=vf_cv2.LINE_AA, 489 ) 490 491 @staticmethod 492 def draw_rounded_rectangle( 493 scene: np.ndarray, 494 xyxy, 495 color, 496 border_radius: int, 497 ) -> np.ndarray: 498 x1, y1, x2, y2 = xyxy 499 width = x2 - x1 500 height = y2 - y1 501 502 border_radius = min(border_radius, min(width, height) // 2) 503 504 if border_radius <= 0: 505 vf_cv2.rectangle( 506 img=scene, 507 pt1=(x1, y1), 508 pt2=(x2, y2), 509 color=color, 510 thickness=-1, 511 ) 512 else: 513 rectangle_coordinates = [ 514 ((x1 + border_radius, y1), (x2 - border_radius, y2)), 515 ((x1, y1 + border_radius), (x2, y2 - border_radius)), 516 ] 517 circle_centers = [ 518 (x1 + border_radius, y1 + border_radius), 519 (x2 - border_radius, y1 + border_radius), 520 (x1 + border_radius, y2 - border_radius), 521 (x2 - border_radius, y2 - border_radius), 522 ] 523 524 for coordinates in rectangle_coordinates: 525 vf_cv2.rectangle( 526 img=scene, 527 pt1=coordinates[0], 528 pt2=coordinates[1], 529 color=color, 530 thickness=-1, 531 ) 532 for center in circle_centers: 533 vf_cv2.circle( 534 img=scene, 535 center=center, 536 radius=border_radius, 537 color=color, 538 thickness=-1, 539 ) 540 return scene
LabelAnnotator( color=ColorPalette(colors=[Color(r=163, g=81, b=251), Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), Color(r=255, g=118, b=51), Color(r=255, g=182, b=51), Color(r=209, g=212, b=53), Color(r=76, g=251, b=18), Color(r=148, g=207, b=26), Color(r=64, g=222, b=138), Color(r=27, g=150, b=64), Color(r=0, g=214, b=193), Color(r=46, g=156, b=170), Color(r=0, g=196, b=255), Color(r=54, g=71, b=151), Color(r=102, g=117, b=255), Color(r=0, g=25, b=239), Color(r=134, g=58, b=255), Color(r=83, g=0, b=135), Color(r=205, g=58, b=255), Color(r=255, g=151, b=202), Color(r=255, g=57, b=201)]), text_color=Color(r=255, g=255, b=255), text_scale: float = 0.5, text_thickness: int = 1, text_padding: int = 10, text_position: supervision.geometry.core.Position = <Position.TOP_LEFT: 'TOP_LEFT'>, color_lookup: supervision.annotators.utils.ColorLookup = <ColorLookup.CLASS: 'class'>, border_radius: int = 0, smart_position: bool = False)
327 def __init__( 328 self, 329 color=ColorPalette.DEFAULT, 330 text_color=Color.WHITE, 331 text_scale: float = 0.5, 332 text_thickness: int = 1, 333 text_padding: int = 10, 334 text_position: Position = Position.TOP_LEFT, 335 color_lookup: ColorLookup = ColorLookup.CLASS, 336 border_radius: int = 0, 337 smart_position: bool = False, 338 ): 339 self.border_radius: int = border_radius 340 self.color = color 341 self.text_color = text_color 342 self.text_scale: float = text_scale 343 self.text_thickness: int = text_thickness 344 self.text_padding: int = text_padding 345 self.text_anchor: Position = text_position 346 self.color_lookup: ColorLookup = color_lookup 347 self.smart_position = smart_position
def
annotate( self, scene, detections: supervision.detection.core.Detections, labels, custom_color_lookup=None):
349 def annotate( 350 self, 351 scene, 352 detections: Detections, 353 labels, 354 custom_color_lookup=None, 355 ): 356 self._validate_labels(labels, detections) 357 358 labels = self._get_labels_text(detections, labels) 359 label_properties = self._get_label_properties(detections, labels) 360 361 if self.smart_position: 362 xyxy = label_properties[:, :4] 363 xyxy = spread_out_boxes(xyxy) 364 label_properties[:, :4] = xyxy 365 366 self._draw_labels( 367 scene=scene, 368 labels=labels, 369 label_properties=label_properties, 370 detections=detections, 371 custom_color_lookup=custom_color_lookup, 372 ) 373 374 return scene
@staticmethod
def
draw_rounded_rectangle(scene: numpy.ndarray, xyxy, color, border_radius: int) -> numpy.ndarray:
491 @staticmethod 492 def draw_rounded_rectangle( 493 scene: np.ndarray, 494 xyxy, 495 color, 496 border_radius: int, 497 ) -> np.ndarray: 498 x1, y1, x2, y2 = xyxy 499 width = x2 - x1 500 height = y2 - y1 501 502 border_radius = min(border_radius, min(width, height) // 2) 503 504 if border_radius <= 0: 505 vf_cv2.rectangle( 506 img=scene, 507 pt1=(x1, y1), 508 pt2=(x2, y2), 509 color=color, 510 thickness=-1, 511 ) 512 else: 513 rectangle_coordinates = [ 514 ((x1 + border_radius, y1), (x2 - border_radius, y2)), 515 ((x1, y1 + border_radius), (x2, y2 - border_radius)), 516 ] 517 circle_centers = [ 518 (x1 + border_radius, y1 + border_radius), 519 (x2 - border_radius, y1 + border_radius), 520 (x1 + border_radius, y2 - border_radius), 521 (x2 - border_radius, y2 - border_radius), 522 ] 523 524 for coordinates in rectangle_coordinates: 525 vf_cv2.rectangle( 526 img=scene, 527 pt1=coordinates[0], 528 pt2=coordinates[1], 529 color=color, 530 thickness=-1, 531 ) 532 for center in circle_centers: 533 vf_cv2.circle( 534 img=scene, 535 center=center, 536 radius=border_radius, 537 color=color, 538 thickness=-1, 539 ) 540 return scene