• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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