1# Copyright 2020 Huawei Technologies Co., Ltd 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================ 15"""Preprocess dataset.""" 16import random 17import threading 18import copy 19 20import numpy as np 21from PIL import Image 22import cv2 23 24 25def _rand(a=0., b=1.): 26 return np.random.rand() * (b - a) + a 27 28 29def bbox_iou(bbox_a, bbox_b, offset=0): 30 """Calculate Intersection-Over-Union(IOU) of two bounding boxes. 31 32 Parameters 33 ---------- 34 bbox_a : numpy.ndarray 35 An ndarray with shape :math:`(N, 4)`. 36 bbox_b : numpy.ndarray 37 An ndarray with shape :math:`(M, 4)`. 38 offset : float or int, default is 0 39 The ``offset`` is used to control the whether the width(or height) is computed as 40 (right - left + ``offset``). 41 Note that the offset must be 0 for normalized bboxes, whose ranges are in ``[0, 1]``. 42 43 Returns 44 ------- 45 numpy.ndarray 46 An ndarray with shape :math:`(N, M)` indicates IOU between each pairs of 47 bounding boxes in `bbox_a` and `bbox_b`. 48 49 """ 50 if bbox_a.shape[1] < 4 or bbox_b.shape[1] < 4: 51 raise IndexError("Bounding boxes axis 1 must have at least length 4") 52 53 tl = np.maximum(bbox_a[:, None, :2], bbox_b[:, :2]) 54 br = np.minimum(bbox_a[:, None, 2:4], bbox_b[:, 2:4]) 55 56 area_i = np.prod(br - tl + offset, axis=2) * (tl < br).all(axis=2) 57 area_a = np.prod(bbox_a[:, 2:4] - bbox_a[:, :2] + offset, axis=1) 58 area_b = np.prod(bbox_b[:, 2:4] - bbox_b[:, :2] + offset, axis=1) 59 return area_i / (area_a[:, None] + area_b - area_i) 60 61 62def statistic_normalize_img(img, statistic_norm): 63 """Statistic normalize images.""" 64 # img: RGB 65 if isinstance(img, Image.Image): 66 img = np.array(img) 67 img = img/255. 68 mean = np.array([0.485, 0.456, 0.406]) 69 std = np.array([0.229, 0.224, 0.225]) 70 if statistic_norm: 71 img = (img - mean) / std 72 return img 73 74 75def get_interp_method(interp, sizes=()): 76 """ 77 Get the interpolation method for resize functions. 78 The major purpose of this function is to wrap a random interp method selection 79 and a auto-estimation method. 80 81 Note: 82 When shrinking an image, it will generally look best with AREA-based 83 interpolation, whereas, when enlarging an image, it will generally look best 84 with Bicubic or Bilinear. 85 86 Args: 87 interp (int): Interpolation method for all resizing operations. 88 89 - 0: Nearest Neighbors Interpolation. 90 - 1: Bilinear interpolation. 91 - 2: Bicubic interpolation over 4x4 pixel neighborhood. 92 - 3: Nearest Neighbors. Originally it should be Area-based, as we cannot find Area-based, 93 so we use NN instead. Area-based (resampling using pixel area relation). 94 It may be a preferred method for image decimation, as it gives moire-free results. 95 But when the image is zoomed, it is similar to the Nearest Neighbors method. (used by default). 96 - 4: Lanczos interpolation over 8x8 pixel neighborhood. 97 - 9: Cubic for enlarge, area for shrink, bilinear for others. 98 - 10: Random select from interpolation method mentioned above. 99 100 sizes (tuple): Format should like (old_height, old_width, new_height, new_width), 101 if None provided, auto(9) will return Area(2) anyway. Default: () 102 103 Returns: 104 int, interp method from 0 to 4. 105 """ 106 if interp == 9: 107 if sizes: 108 assert len(sizes) == 4 109 oh, ow, nh, nw = sizes 110 if nh > oh and nw > ow: 111 return 2 112 if nh < oh and nw < ow: 113 return 0 114 return 1 115 return 2 116 if interp == 10: 117 return random.randint(0, 4) 118 if interp not in (0, 1, 2, 3, 4): 119 raise ValueError('Unknown interp method %d' % interp) 120 return interp 121 122 123def pil_image_reshape(interp): 124 """Reshape pil image.""" 125 reshape_type = { 126 0: Image.NEAREST, 127 1: Image.BILINEAR, 128 2: Image.BICUBIC, 129 3: Image.NEAREST, 130 4: Image.LANCZOS, 131 } 132 return reshape_type[interp] 133 134 135def _preprocess_true_boxes(true_boxes, anchors, in_shape, num_classes, 136 max_boxes, label_smooth, label_smooth_factor=0.1): 137 """Preprocess annotation boxes.""" 138 anchors = np.array(anchors) 139 num_layers = anchors.shape[0] // 3 140 anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] 141 true_boxes = np.array(true_boxes, dtype='float32') 142 input_shape = np.array(in_shape, dtype='int32') 143 boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2. 144 # trans to box center point 145 boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2] 146 # input_shape is [h, w] 147 true_boxes[..., 0:2] = boxes_xy / input_shape[::-1] 148 true_boxes[..., 2:4] = boxes_wh / input_shape[::-1] 149 # true_boxes [x, y, w, h] 150 151 grid_shapes = [input_shape // 32, input_shape // 16, input_shape // 8] 152 # grid_shape [h, w] 153 y_true = [np.zeros((grid_shapes[l][0], grid_shapes[l][1], len(anchor_mask[l]), 154 5 + num_classes), dtype='float32') for l in range(num_layers)] 155 # y_true [gridy, gridx] 156 anchors = np.expand_dims(anchors, 0) 157 anchors_max = anchors / 2. 158 anchors_min = -anchors_max 159 valid_mask = boxes_wh[..., 0] > 0 160 161 wh = boxes_wh[valid_mask] 162 if wh.size > 0: 163 wh = np.expand_dims(wh, -2) 164 boxes_max = wh / 2. 165 boxes_min = -boxes_max 166 167 intersect_min = np.maximum(boxes_min, anchors_min) 168 intersect_max = np.minimum(boxes_max, anchors_max) 169 intersect_wh = np.maximum(intersect_max - intersect_min, 0.) 170 intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1] 171 box_area = wh[..., 0] * wh[..., 1] 172 anchor_area = anchors[..., 0] * anchors[..., 1] 173 iou = intersect_area / (box_area + anchor_area - intersect_area) 174 175 best_anchor = np.argmax(iou, axis=-1) 176 for t, n in enumerate(best_anchor): 177 for l in range(num_layers): 178 if n in anchor_mask[l]: 179 i = np.floor(true_boxes[t, 0] * grid_shapes[l][1]).astype('int32') # grid_y 180 j = np.floor(true_boxes[t, 1] * grid_shapes[l][0]).astype('int32') # grid_x 181 182 k = anchor_mask[l].index(n) 183 c = true_boxes[t, 4].astype('int32') 184 y_true[l][j, i, k, 0:4] = true_boxes[t, 0:4] 185 y_true[l][j, i, k, 4] = 1. 186 187 # lable-smooth 188 if label_smooth: 189 sigma = label_smooth_factor/(num_classes-1) 190 y_true[l][j, i, k, 5:] = sigma 191 y_true[l][j, i, k, 5+c] = 1-label_smooth_factor 192 else: 193 y_true[l][j, i, k, 5 + c] = 1. 194 195 # pad_gt_boxes for avoiding dynamic shape 196 pad_gt_box0 = np.zeros(shape=[max_boxes, 4], dtype=np.float32) 197 pad_gt_box1 = np.zeros(shape=[max_boxes, 4], dtype=np.float32) 198 pad_gt_box2 = np.zeros(shape=[max_boxes, 4], dtype=np.float32) 199 200 mask0 = np.reshape(y_true[0][..., 4:5], [-1]) 201 gt_box0 = np.reshape(y_true[0][..., 0:4], [-1, 4]) 202 # gt_box [boxes, [x,y,w,h]] 203 gt_box0 = gt_box0[mask0 == 1] 204 # gt_box0: get all boxes which have object 205 pad_gt_box0[:gt_box0.shape[0]] = gt_box0 206 # gt_box0.shape[0]: total number of boxes in gt_box0 207 # top N of pad_gt_box0 is real box, and after are pad by zero 208 209 mask1 = np.reshape(y_true[1][..., 4:5], [-1]) 210 gt_box1 = np.reshape(y_true[1][..., 0:4], [-1, 4]) 211 gt_box1 = gt_box1[mask1 == 1] 212 pad_gt_box1[:gt_box1.shape[0]] = gt_box1 213 214 mask2 = np.reshape(y_true[2][..., 4:5], [-1]) 215 gt_box2 = np.reshape(y_true[2][..., 0:4], [-1, 4]) 216 217 gt_box2 = gt_box2[mask2 == 1] 218 pad_gt_box2[:gt_box2.shape[0]] = gt_box2 219 return y_true[0], y_true[1], y_true[2], pad_gt_box0, pad_gt_box1, pad_gt_box2 220 221 222def _reshape_data(image, image_size): 223 """Reshape image.""" 224 if not isinstance(image, Image.Image): 225 image = Image.fromarray(image) 226 ori_w, ori_h = image.size 227 ori_image_shape = np.array([ori_w, ori_h], np.int32) 228 # original image shape fir:H sec:W 229 h, w = image_size 230 interp = get_interp_method(interp=9, sizes=(ori_h, ori_w, h, w)) 231 image = image.resize((w, h), pil_image_reshape(interp)) 232 image_data = statistic_normalize_img(image, statistic_norm=True) 233 if len(image_data.shape) == 2: 234 image_data = np.expand_dims(image_data, axis=-1) 235 image_data = np.concatenate([image_data, image_data, image_data], axis=-1) 236 image_data = image_data.astype(np.float32) 237 return image_data, ori_image_shape 238 239 240def color_distortion(img, hue, sat, val, device_num): 241 """Color distortion.""" 242 hue = _rand(-hue, hue) 243 sat = _rand(1, sat) if _rand() < .5 else 1 / _rand(1, sat) 244 val = _rand(1, val) if _rand() < .5 else 1 / _rand(1, val) 245 if device_num != 1: 246 cv2.setNumThreads(1) 247 x = cv2.cvtColor(img, cv2.COLOR_RGB2HSV_FULL) 248 x = x / 255. 249 x[..., 0] += hue 250 x[..., 0][x[..., 0] > 1] -= 1 251 x[..., 0][x[..., 0] < 0] += 1 252 x[..., 1] *= sat 253 x[..., 2] *= val 254 x[x > 1] = 1 255 x[x < 0] = 0 256 x = x * 255. 257 x = x.astype(np.uint8) 258 image_data = cv2.cvtColor(x, cv2.COLOR_HSV2RGB_FULL) 259 return image_data 260 261 262def filp_pil_image(img): 263 return img.transpose(Image.FLIP_LEFT_RIGHT) 264 265 266def convert_gray_to_color(img): 267 if len(img.shape) == 2: 268 img = np.expand_dims(img, axis=-1) 269 img = np.concatenate([img, img, img], axis=-1) 270 return img 271 272 273def _is_iou_satisfied_constraint(min_iou, max_iou, box, crop_box): 274 iou = bbox_iou(box, crop_box) 275 return min_iou <= iou.min() and max_iou >= iou.max() 276 277 278def _choose_candidate_by_constraints(max_trial, input_w, input_h, image_w, image_h, jitter, box, use_constraints): 279 """Choose candidate by constraints.""" 280 if use_constraints: 281 constraints = ( 282 (0.1, None), 283 (0.3, None), 284 (0.5, None), 285 (0.7, None), 286 (0.9, None), 287 (None, 1), 288 ) 289 else: 290 constraints = ( 291 (None, None), 292 ) 293 # add default candidate 294 candidates = [(0, 0, input_w, input_h)] 295 for constraint in constraints: 296 min_iou, max_iou = constraint 297 min_iou = -np.inf if min_iou is None else min_iou 298 max_iou = np.inf if max_iou is None else max_iou 299 300 for _ in range(max_trial): 301 # box_data should have at least one box 302 new_ar = float(input_w) / float(input_h) * _rand(1 - jitter, 1 + jitter) / _rand(1 - jitter, 1 + jitter) 303 scale = _rand(0.25, 2) 304 305 if new_ar < 1: 306 nh = int(scale * input_h) 307 nw = int(nh * new_ar) 308 else: 309 nw = int(scale * input_w) 310 nh = int(nw / new_ar) 311 312 dx = int(_rand(0, input_w - nw)) 313 dy = int(_rand(0, input_h - nh)) 314 315 if box.size > 0: 316 t_box = copy.deepcopy(box) 317 t_box[:, [0, 2]] = t_box[:, [0, 2]] * float(nw) / float(image_w) + dx 318 t_box[:, [1, 3]] = t_box[:, [1, 3]] * float(nh) / float(image_h) + dy 319 320 crop_box = np.array((0, 0, input_w, input_h)) 321 if not _is_iou_satisfied_constraint(min_iou, max_iou, t_box, crop_box[np.newaxis]): 322 continue 323 else: 324 candidates.append((dx, dy, nw, nh)) 325 else: 326 raise Exception("!!! annotation box is less than 1") 327 return candidates 328 329 330def _correct_bbox_by_candidates(candidates, input_w, input_h, image_w, 331 image_h, flip, box, box_data, allow_outside_center): 332 """Calculate correct boxes.""" 333 while candidates: 334 if len(candidates) > 1: 335 # ignore default candidate which do not crop 336 candidate = candidates.pop(np.random.randint(1, len(candidates))) 337 else: 338 candidate = candidates.pop(np.random.randint(0, len(candidates))) 339 dx, dy, nw, nh = candidate 340 t_box = copy.deepcopy(box) 341 t_box[:, [0, 2]] = t_box[:, [0, 2]] * float(nw) / float(image_w) + dx 342 t_box[:, [1, 3]] = t_box[:, [1, 3]] * float(nh) / float(image_h) + dy 343 if flip: 344 t_box[:, [0, 2]] = input_w - t_box[:, [2, 0]] 345 346 if allow_outside_center: 347 pass 348 else: 349 t_box = t_box[np.logical_and((t_box[:, 0] + t_box[:, 2])/2. >= 0., (t_box[:, 1] + t_box[:, 3])/2. >= 0.)] 350 t_box = t_box[np.logical_and((t_box[:, 0] + t_box[:, 2]) / 2. <= input_w, 351 (t_box[:, 1] + t_box[:, 3]) / 2. <= input_h)] 352 353 # recorrect x, y for case x,y < 0 reset to zero, after dx and dy, some box can smaller than zero 354 t_box[:, 0:2][t_box[:, 0:2] < 0] = 0 355 # recorrect w,h not higher than input size 356 t_box[:, 2][t_box[:, 2] > input_w] = input_w 357 t_box[:, 3][t_box[:, 3] > input_h] = input_h 358 box_w = t_box[:, 2] - t_box[:, 0] 359 box_h = t_box[:, 3] - t_box[:, 1] 360 # discard invalid box: w or h smaller than 1 pixel 361 t_box = t_box[np.logical_and(box_w > 1, box_h > 1)] 362 363 if t_box.shape[0] > 0: 364 # break if number of find t_box 365 box_data[: len(t_box)] = t_box 366 return box_data, candidate 367 raise Exception('all candidates can not satisfied re-correct bbox') 368 369 370def _data_aug(image, box, jitter, hue, sat, val, image_input_size, max_boxes, 371 anchors, num_classes, max_trial=10, device_num=1): 372 """Crop an image randomly with bounding box constraints. 373 374 This data augmentation is used in training of 375 Single Shot Multibox Detector [#]_. More details can be found in 376 data augmentation section of the original paper. 377 .. [#] Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, 378 Scott Reed, Cheng-Yang Fu, Alexander C. Berg. 379 SSD: Single Shot MultiBox Detector. ECCV 2016.""" 380 381 if not isinstance(image, Image.Image): 382 image = Image.fromarray(image) 383 384 image_w, image_h = image.size 385 input_h, input_w = image_input_size 386 387 np.random.shuffle(box) 388 if len(box) > max_boxes: 389 box = box[:max_boxes] 390 flip = _rand() < .5 391 box_data = np.zeros((max_boxes, 5)) 392 393 candidates = _choose_candidate_by_constraints(use_constraints=False, 394 max_trial=max_trial, 395 input_w=input_w, 396 input_h=input_h, 397 image_w=image_w, 398 image_h=image_h, 399 jitter=jitter, 400 box=box) 401 box_data, candidate = _correct_bbox_by_candidates(candidates=candidates, 402 input_w=input_w, 403 input_h=input_h, 404 image_w=image_w, 405 image_h=image_h, 406 flip=flip, 407 box=box, 408 box_data=box_data, 409 allow_outside_center=True) 410 dx, dy, nw, nh = candidate 411 interp = get_interp_method(interp=10) 412 image = image.resize((nw, nh), pil_image_reshape(interp)) 413 # place image, gray color as back graoud 414 new_image = Image.new('RGB', (input_w, input_h), (128, 128, 128)) 415 new_image.paste(image, (dx, dy)) 416 image = new_image 417 418 if flip: 419 image = filp_pil_image(image) 420 421 image = np.array(image) 422 423 image = convert_gray_to_color(image) 424 425 image_data = color_distortion(image, hue, sat, val, device_num) 426 image_data = statistic_normalize_img(image_data, statistic_norm=True) 427 428 image_data = image_data.astype(np.float32) 429 430 return image_data, box_data 431 432 433def preprocess_fn(image, box, config, input_size, device_num): 434 """Preprocess data function.""" 435 config_anchors = config.anchor_scales 436 anchors = np.array([list(x) for x in config_anchors]) 437 max_boxes = config.max_box 438 num_classes = config.num_classes 439 jitter = config.jitter 440 hue = config.hue 441 sat = config.saturation 442 val = config.value 443 image, anno = _data_aug(image, box, jitter=jitter, hue=hue, sat=sat, val=val, 444 image_input_size=input_size, max_boxes=max_boxes, 445 num_classes=num_classes, anchors=anchors, device_num=device_num) 446 return image, anno 447 448 449def reshape_fn(image, img_id, config): 450 input_size = config.test_img_shape 451 image, ori_image_shape = _reshape_data(image, image_size=input_size) 452 return image, ori_image_shape, img_id 453 454 455class MultiScaleTrans: 456 """Multi scale transform.""" 457 def __init__(self, config, device_num): 458 self.config = config 459 self.seed = 0 460 self.size_list = [] 461 self.resize_rate = config.resize_rate 462 self.dataset_size = config.dataset_size 463 self.size_dict = {} 464 self.seed_num = int(1e6) 465 self.seed_list = self.generate_seed_list(seed_num=self.seed_num) 466 self.resize_count_num = int(np.ceil(self.dataset_size / self.resize_rate)) 467 self.device_num = device_num 468 self.anchor_scales = config.anchor_scales 469 self.num_classes = config.num_classes 470 self.max_box = config.max_box 471 self.label_smooth = config.label_smooth 472 self.label_smooth_factor = config.label_smooth_factor 473 474 def generate_seed_list(self, init_seed=1234, seed_num=int(1e6), seed_range=(1, 1000)): 475 seed_list = [] 476 random.seed(init_seed) 477 for _ in range(seed_num): 478 seed = random.randint(seed_range[0], seed_range[1]) 479 seed_list.append(seed) 480 return seed_list 481 482 def __call__(self, imgs, annos, x1, x2, x3, x4, x5, x6, batchInfo): 483 epoch_num = batchInfo.get_epoch_num() 484 size_idx = int(batchInfo.get_batch_num() / self.resize_rate) 485 seed_key = self.seed_list[(epoch_num * self.resize_count_num + size_idx) % self.seed_num] 486 ret_imgs = [] 487 ret_annos = [] 488 489 bbox1 = [] 490 bbox2 = [] 491 bbox3 = [] 492 gt1 = [] 493 gt2 = [] 494 gt3 = [] 495 496 if self.size_dict.get(seed_key, None) is None: 497 random.seed(seed_key) 498 new_size = random.choice(self.config.multi_scale) 499 self.size_dict[seed_key] = new_size 500 seed = seed_key 501 502 input_size = self.size_dict[seed] 503 for img, anno in zip(imgs, annos): 504 img, anno = preprocess_fn(img, anno, self.config, input_size, self.device_num) 505 ret_imgs.append(img.transpose(2, 0, 1).copy()) 506 bbox_true_1, bbox_true_2, bbox_true_3, gt_box1, gt_box2, gt_box3 = \ 507 _preprocess_true_boxes(true_boxes=anno, anchors=self.anchor_scales, in_shape=img.shape[0:2], 508 num_classes=self.num_classes, max_boxes=self.max_box, 509 label_smooth=self.label_smooth, label_smooth_factor=self.label_smooth_factor) 510 bbox1.append(bbox_true_1) 511 bbox2.append(bbox_true_2) 512 bbox3.append(bbox_true_3) 513 gt1.append(gt_box1) 514 gt2.append(gt_box2) 515 gt3.append(gt_box3) 516 ret_annos.append(0) 517 return np.array(ret_imgs), np.array(ret_annos), np.array(bbox1), np.array(bbox2), np.array(bbox3), \ 518 np.array(gt1), np.array(gt2), np.array(gt3) 519 520 521def thread_batch_preprocess_true_box(annos, config, input_shape, result_index, batch_bbox_true_1, batch_bbox_true_2, 522 batch_bbox_true_3, batch_gt_box1, batch_gt_box2, batch_gt_box3): 523 """Preprocess true box for multi-thread.""" 524 i = 0 525 for anno in annos: 526 bbox_true_1, bbox_true_2, bbox_true_3, gt_box1, gt_box2, gt_box3 = \ 527 _preprocess_true_boxes(true_boxes=anno, anchors=config.anchor_scales, in_shape=input_shape, 528 num_classes=config.num_classes, max_boxes=config.max_box, 529 label_smooth=config.label_smooth, label_smooth_factor=config.label_smooth_factor) 530 batch_bbox_true_1[result_index + i] = bbox_true_1 531 batch_bbox_true_2[result_index + i] = bbox_true_2 532 batch_bbox_true_3[result_index + i] = bbox_true_3 533 batch_gt_box1[result_index + i] = gt_box1 534 batch_gt_box2[result_index + i] = gt_box2 535 batch_gt_box3[result_index + i] = gt_box3 536 i = i + 1 537 538 539def batch_preprocess_true_box(annos, config, input_shape): 540 """Preprocess true box with multi-thread.""" 541 batch_bbox_true_1 = [] 542 batch_bbox_true_2 = [] 543 batch_bbox_true_3 = [] 544 batch_gt_box1 = [] 545 batch_gt_box2 = [] 546 batch_gt_box3 = [] 547 threads = [] 548 549 step = 4 550 for index in range(0, len(annos), step): 551 for _ in range(step): 552 batch_bbox_true_1.append(None) 553 batch_bbox_true_2.append(None) 554 batch_bbox_true_3.append(None) 555 batch_gt_box1.append(None) 556 batch_gt_box2.append(None) 557 batch_gt_box3.append(None) 558 step_anno = annos[index: index + step] 559 t = threading.Thread(target=thread_batch_preprocess_true_box, 560 args=(step_anno, config, input_shape, index, batch_bbox_true_1, batch_bbox_true_2, 561 batch_bbox_true_3, batch_gt_box1, batch_gt_box2, batch_gt_box3)) 562 t.start() 563 threads.append(t) 564 565 for t in threads: 566 t.join() 567 568 return np.array(batch_bbox_true_1), np.array(batch_bbox_true_2), np.array(batch_bbox_true_3), \ 569 np.array(batch_gt_box1), np.array(batch_gt_box2), np.array(batch_gt_box3) 570 571 572def batch_preprocess_true_box_single(annos, config, input_shape): 573 """Preprocess true boxes.""" 574 batch_bbox_true_1 = [] 575 batch_bbox_true_2 = [] 576 batch_bbox_true_3 = [] 577 batch_gt_box1 = [] 578 batch_gt_box2 = [] 579 batch_gt_box3 = [] 580 for anno in annos: 581 bbox_true_1, bbox_true_2, bbox_true_3, gt_box1, gt_box2, gt_box3 = \ 582 _preprocess_true_boxes(true_boxes=anno, anchors=config.anchor_scales, in_shape=input_shape, 583 num_classes=config.num_classes, max_boxes=config.max_box, 584 label_smooth=config.label_smooth, label_smooth_factor=config.label_smooth_factor) 585 batch_bbox_true_1.append(bbox_true_1) 586 batch_bbox_true_2.append(bbox_true_2) 587 batch_bbox_true_3.append(bbox_true_3) 588 batch_gt_box1.append(gt_box1) 589 batch_gt_box2.append(gt_box2) 590 batch_gt_box3.append(gt_box3) 591 592 return np.array(batch_bbox_true_1), np.array(batch_bbox_true_2), np.array(batch_bbox_true_3), \ 593 np.array(batch_gt_box1), np.array(batch_gt_box2), np.array(batch_gt_box3) 594