content: '''
  A data generator for 2D object detection.
  
  Copyright (C) 2018 Pierluigi Ferrari
  
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  
     http://www.apache.org/licenses/LICENSE-2.0
  
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  '''
  
  from __future__ import division
  import numpy as np
  import inspect
  from collections import defaultdict
  import warnings
  import sklearn.utils
  from copy import deepcopy
  from PIL import Image
  import cv2
  import csv
  import os
  import sys
  from tqdm import tqdm, trange
  try:
      import h5py
  except ImportError:
      warnings.warn("'h5py' module is missing. The fast HDF5 dataset option will be unavailable.")
  try:
      import json
  except ImportError:
      warnings.warn("'json' module is missing. The JSON-parser will be unavailable.")
  try:
      from bs4 import BeautifulSoup
  except ImportError:
      warnings.warn("'BeautifulSoup' module is missing. The XML-parser will be unavailable.")
  try:
      import pickle
  except ImportError:
      warnings.warn("'pickle' module is missing. You won't be able to save parsed file lists and annotations as pickled files.")
  
  from ssd_encoder_decoder.ssd_input_encoder import SSDInputEncoder
  from data_generator.object_detection_2d_image_boxes_validation_utils import BoxFilter
  
  class DegenerateBatchError(Exception):
      '''
      An exception class to be raised if a generated batch ends up being degenerate,
      e.g. if a generated batch is empty.
      '''
      pass
  
  class DatasetError(Exception):
      '''
      An exception class to be raised if a anything is wrong with the dataset,
      in particular if you try to generate batches when no dataset was loaded.
      '''
      pass
  
  class DataGenerator:
      '''
      A generator to generate batches of samples and corresponding labels indefinitely.
  
      Can shuffle the dataset consistently after each complete pass.
  
      Currently provides three methods to parse annotation data: A general-purpose CSV parser,
      an XML parser for the Pascal VOC datasets, and a JSON parser for the MS COCO datasets.
      If the annotations of your dataset are in a format that is not supported by these parsers,
      you could just add another parser method and still use this generator.
  
      Can perform image transformations for data conversion and data augmentation,
      for details please refer to the documentation of the `generate()` method.
      '''
  
      def __init__(self,
                   load_images_into_memory=False,
                   hdf5_dataset_path=None,
                   filenames=None,
                   filenames_type='text',
                   images_dir=None,
                   labels=None,
                   image_ids=None,
                   eval_neutral=None,
                   labels_output_format=('class_id', 'xmin', 'ymin', 'xmax', 'ymax'),
                   verbose=True):
          '''
          Initializes the data generator. You can either load a dataset directly here in the constructor,
          e.g. an HDF5 dataset, or you can use one of the parser methods to read in a dataset.
  
          Arguments:
              load_images_into_memory (bool, optional): If `True`, the entire dataset will be loaded into memory.
                  This enables noticeably faster data generation than loading batches of images into memory ad hoc.
                  Be sure that you have enough memory before you activate this option.
              hdf5_dataset_path (str, optional): The full file path of an HDF5 file that contains a dataset in the
                  format that the `create_hdf5_dataset()` method produces. If you load such an HDF5 dataset, you
                  don't need to use any of the parser methods anymore, the HDF5 dataset already contains all relevant
                  data.
              filenames (string or list, optional): `None` or either a Python list/tuple or a string representing
                  a filepath. If a list/tuple is passed, it must contain the file names (full paths) of the
                  images to be used. Note that the list/tuple must contain the paths to the images,
                  not the images themselves. If a filepath string is passed, it must point either to
                  (1) a pickled file containing a list/tuple as described above. In this case the `filenames_type`
                  argument must be set to `pickle`.
                  Or
                  (2) a text file. Each line of the text file contains the file name (basename of the file only,
                  not the full directory path) to one image and nothing else. In this case the `filenames_type`
                  argument must be set to `text` and you must pass the path to the directory that contains the
                  images in `images_dir`.
              filenames_type (string, optional): In case a string is passed for `filenames`, this indicates what
                  type of file `filenames` is. It can be either 'pickle' for a pickled file or 'text' for a
                  plain text file.
              images_dir (string, optional): In case a text file is passed for `filenames`, the full paths to
                  the images will be composed from `images_dir` and the names in the text file, i.e. this
                  should be the directory that contains the images to which the text file refers.
                  If `filenames_type` is not 'text', then this argument is irrelevant.
              labels (string or list, optional): `None` or either a Python list/tuple or a string representing
                  the path to a pickled file containing a list/tuple. The list/tuple must contain Numpy arrays
                  that represent the labels of the dataset.
              image_ids (string or list, optional): `None` or either a Python list/tuple or a string representing
                  the path to a pickled file containing a list/tuple. The list/tuple must contain the image
                  IDs of the images in the dataset.
              eval_neutral (string or list, optional): `None` or either a Python list/tuple or a string representing
                  the path to a pickled file containing a list/tuple. The list/tuple must contain for each image
                  a list that indicates for each ground truth object in the image whether that object is supposed
                  to be treated as neutral during an evaluation.
              labels_output_format (list, optional): A list of five strings representing the desired order of the five
                  items class ID, xmin, ymin, xmax, ymax in the generated ground truth data (if any). The expected
                  strings are 'xmin', 'ymin', 'xmax', 'ymax', 'class_id'.
              verbose (bool, optional): If `True`, prints out the progress for some constructor operations that may
                  take a bit longer.
          '''
          self.labels_output_format = labels_output_format
          self.labels_format={'class_id': labels_output_format.index('class_id'),
                              'xmin': labels_output_format.index('xmin'),
                              'ymin': labels_output_format.index('ymin'),
                              'xmax': labels_output_format.index('xmax'),
                              'ymax': labels_output_format.index('ymax')} # This dictionary is for internal use.
  
          self.dataset_size = 0 # As long as we haven't loaded anything yet, the dataset size is zero.
          self.load_images_into_memory = load_images_into_memory
          self.images = None # The only way that this list will not stay `None` is if `load_images_into_memory == True`.
  
          # `self.filenames` is a list containing all file names of the image samples (full paths).
          # Note that it does not contain the actual image files themselves. This list is one of the outputs of the parser methods.
          # In case you are loading an HDF5 dataset, this list will be `None`.
          if not filenames is None:
              if isinstance(filenames, (list, tuple)):
                  self.filenames = filenames
              elif isinstance(filenames, str):
                  with open(filenames, 'rb') as f:
                      if filenames_type == 'pickle':
                          self.filenames = pickle.load(f)
                      elif filenames_type == 'text':
                          self.filenames = [os.path.join(images_dir, line.strip()) for line in f]
                      else:
                          raise ValueError("`filenames_type` can be either 'text' or 'pickle'.")
              else:
                  raise ValueError("`filenames` must be either a Python list/tuple or a string representing a filepath (to a pickled or text file). The value you passed is neither of the two.")
              self.dataset_size = len(self.filenames)
              self.dataset_indices = np.arange(self.dataset_size, dtype=np.int32)
              if load_images_into_memory:
                  self.images = []
                  if verbose: it = tqdm(self.filenames, desc='Loading images into memory', file=sys.stdout)
                  else: it = self.filenames
                  for filename in it:
                      with Image.open(filename) as image:
                          self.images.append(np.array(image, dtype=np.uint8))
          else:
              self.filenames = None
  
          # In case ground truth is available, `self.labels` is a list containing for each image a list (or NumPy array)
          # of ground truth bounding boxes for that image.
          if not labels is None:
              if isinstance(labels, str):
                  with open(labels, 'rb') as f:
                      self.labels = pickle.load(f)
              elif isinstance(labels, (list, tuple)):
                  self.labels = labels
              else:
                  raise ValueError("`labels` must be either a Python list/tuple or a string representing the path to a pickled file containing a list/tuple. The value you passed is neither of the two.")
          else:
              self.labels = None
  
          if not image_ids is None:
              if isinstance(image_ids, str):
                  with open(image_ids, 'rb') as f:
                      self.image_ids = pickle.load(f)
              elif isinstance(image_ids, (list, tuple)):
                  self.image_ids = image_ids
              else:
                  raise ValueError("`image_ids` must be either a Python list/tuple or a string representing the path to a pickled file containing a list/tuple. The value you passed is neither of the two.")
          else:
              self.image_ids = None
  
          if not eval_neutral is None:
              if isinstance(eval_neutral, str):
                  with open(eval_neutral, 'rb') as f:
                      self.eval_neutral = pickle.load(f)
              elif isinstance(eval_neutral, (list, tuple)):
                  self.eval_neutral = eval_neutral
              else:
                  raise ValueError("`image_ids` must be either a Python list/tuple or a string representing the path to a pickled file containing a list/tuple. The value you passed is neither of the two.")
          else:
              self.eval_neutral = None
  
          if not hdf5_dataset_path is None:
              self.hdf5_dataset_path = hdf5_dataset_path
              self.load_hdf5_dataset(verbose=verbose)
          else:
              self.hdf5_dataset = None
  
      def load_hdf5_dataset(self, verbose=True):
          '''
          Loads an HDF5 dataset that is in the format that the `create_hdf5_dataset()` method
          produces.
  
          Arguments:
              verbose (bool, optional): If `True`, prints out the progress while loading
                  the dataset.
  
          Returns:
              None.
          '''
  
          self.hdf5_dataset = h5py.File(self.hdf5_dataset_path, 'r')
          self.dataset_size = len(self.hdf5_dataset['images'])
          self.dataset_indices = np.arange(self.dataset_size, dtype=np.int32) # Instead of shuffling the HDF5 dataset or images in memory, we will shuffle this index list.
  
          if self.load_images_into_memory:
              self.images = []
              if verbose: tr = trange(self.dataset_size, desc='Loading images into memory', file=sys.stdout)
              else: tr = range(self.dataset_size)
              for i in tr:
                  self.images.append(self.hdf5_dataset['images'][i].reshape(self.hdf5_dataset['image_shapes'][i]))
  
          if self.hdf5_dataset.attrs['has_labels']:
              self.labels = []
              labels = self.hdf5_dataset['labels']
              label_shapes = self.hdf5_dataset['label_shapes']
              if verbose: tr = trange(self.dataset_size, desc='Loading labels', file=sys.stdout)
              else: tr = range(self.dataset_size)
              for i in tr:
                  self.labels.append(labels[i].reshape(label_shapes[i]))
  
          if self.hdf5_dataset.attrs['has_image_ids']:
              self.image_ids = []
              image_ids = self.hdf5_dataset['image_ids']
              if verbose: tr = trange(self.dataset_size, desc='Loading image IDs', file=sys.stdout)
              else: tr = range(self.dataset_size)
              for i in tr:
                  self.image_ids.append(image_ids[i])
  
          if self.hdf5_dataset.attrs['has_eval_neutral']:
              self.eval_neutral = []
              eval_neutral = self.hdf5_dataset['eval_neutral']
              if verbose: tr = trange(self.dataset_size, desc='Loading evaluation-neutrality annotations', file=sys.stdout)
              else: tr = range(self.dataset_size)
              for i in tr:
                  self.eval_neutral.append(eval_neutral[i])
  
      def parse_csv(self,
                    images_dir,
                    labels_filename,
                    input_format,
                    include_classes='all',
                    random_sample=False,
                    ret=False,
                    verbose=True):
          '''
          Arguments:
              images_dir (str): The path to the directory that contains the images.
              labels_filename (str): The filepath to a CSV file that contains one ground truth bounding box per line
                  and each line contains the following six items: image file name, class ID, xmin, xmax, ymin, ymax.
                  The six items do not have to be in a specific order, but they must be the first six columns of
                  each line. The order of these items in the CSV file must be specified in `input_format`.
                  The class ID is an integer greater than zero. Class ID 0 is reserved for the background class.
                  `xmin` and `xmax` are the left-most and right-most absolute horizontal coordinates of the box,
                  `ymin` and `ymax` are the top-most and bottom-most absolute vertical coordinates of the box.
                  The image name is expected to be just the name of the image file without the directory path
                  at which the image is located.
              input_format (list): A list of six strings representing the order of the six items
                  image file name, class ID, xmin, xmax, ymin, ymax in the input CSV file. The expected strings
                  are 'image_name', 'xmin', 'xmax', 'ymin', 'ymax', 'class_id'.
              include_classes (list, optional): Either 'all' or a list of integers containing the class IDs that
                  are to be included in the dataset. If 'all', all ground truth boxes will be included in the dataset.
              random_sample (float, optional): Either `False` or a float in `[0,1]`. If this is `False`, the
                  full dataset will be used by the generator. If this is a float in `[0,1]`, a randomly sampled
                  fraction of the dataset will be used, where `random_sample` is the fraction of the dataset
                  to be used. For example, if `random_sample = 0.2`, 20 precent of the dataset will be randomly selected,
                  the rest will be ommitted. The fraction refers to the number of images, not to the number
                  of boxes, i.e. each image that will be added to the dataset will always be added with all
                  of its boxes.
              ret (bool, optional): Whether or not to return the outputs of the parser.
              verbose (bool, optional): If `True`, prints out the progress for operations that may take a bit longer.
  
          Returns:
              None by default, optionally lists for whichever are available of images, image filenames, labels, and image IDs.
          '''
  
          # Set class members.
          self.images_dir = images_dir
          self.labels_filename = labels_filename
          self.input_format = input_format
          self.include_classes = include_classes
  
          # Before we begin, make sure that we have a labels_filename and an input_format
          if self.labels_filename is None or self.input_format is None:
              raise ValueError("`labels_filename` and/or `input_format` have not been set yet. You need to pass them as arguments.")
  
          # Erase data that might have been parsed before
          self.filenames = []
          self.image_ids = []
          self.labels = []
  
          # First, just read in the CSV file lines and sort them.
  
          data = []
  
          with open(self.labels_filename, newline='') as csvfile:
              csvread = csv.reader(csvfile, delimiter=',')
              next(csvread) # Skip the header row.
              for row in csvread: # For every line (i.e for every bounding box) in the CSV file...
                  if self.include_classes == 'all' or int(row[self.input_format.index('class_id')].strip()) in self.include_classes: # If the class_id is among the classes that are to be included in the dataset...
                      box = [] # Store the box class and coordinates here
                      box.append(row[self.input_format.index('image_name')].strip()) # Select the image name column in the input format and append its content to `box`
                      for element in self.labels_output_format: # For each element in the output format (where the elements are the class ID and the four box coordinates)...
                          box.append(int(row[self.input_format.index(element)].strip())) # ...select the respective column in the input format and append it to `box`.
                      data.append(box)
  
          data = sorted(data) # The data needs to be sorted, otherwise the next step won't give the correct result
  
          # Now that we've made sure that the data is sorted by file names,
          # we can compile the actual samples and labels lists
  
          current_file = data[0][0] # The current image for which we're collecting the ground truth boxes
          current_image_id = data[0][0].split('.')[0] # The image ID will be the portion of the image name before the first dot.
          current_labels = [] # The list where we collect all ground truth boxes for a given image
          add_to_dataset = False
          for i, box in enumerate(data):
  
              if box[0] == current_file: # If this box (i.e. this line of the CSV file) belongs to the current image file
                  current_labels.append(box[1:])
                  if i == len(data)-1: # If this is the last line of the CSV file
                      if random_sample: # In case we're not using the full dataset, but a random sample of it.
                          p = np.random.uniform(0,1)
                          if p >= (1-random_sample):
                              self.labels.append(np.stack(current_labels, axis=0))
                              self.filenames.append(os.path.join(self.images_dir, current_file))
                              self.image_ids.append(current_image_id)
                      else:
                          self.labels.append(np.stack(current_labels, axis=0))
                          self.filenames.append(os.path.join(self.images_dir, current_file))
                          self.image_ids.append(current_image_id)
              else: # If this box belongs to a new image file
                  if random_sample: # In case we're not using the full dataset, but a random sample of it.
                      p = np.random.uniform(0,1)
                      if p >= (1-random_sample):
                          self.labels.append(np.stack(current_labels, axis=0))
                          self.filenames.append(os.path.join(self.images_dir, current_file))
                          self.image_ids.append(current_image_id)
                  else:
                      self.labels.append(np.stack(current_labels, axis=0))
                      self.filenames.append(os.path.join(self.images_dir, current_file))
                      self.image_ids.append(current_image_id)
                  current_labels = [] # Reset the labels list because this is a new file.
                  current_file = box[0]
                  current_image_id = box[0].split('.')[0]
                  current_labels.append(box[1:])
                  if i == len(data)-1: # If this is the last line of the CSV file
                      if random_sample: # In case we're not using the full dataset, but a random sample of it.
                          p = np.random.uniform(0,1)
                          if p >= (1-random_sample):
                              self.labels.append(np.stack(current_labels, axis=0))
                              self.filenames.append(os.path.join(self.images_dir, current_file))
                              self.image_ids.append(current_image_id)
                      else:
                          self.labels.append(np.stack(current_labels, axis=0))
                          self.filenames.append(os.path.join(self.images_dir, current_file))
                          self.image_ids.append(current_image_id)
  
          self.dataset_size = len(self.filenames)
          self.dataset_indices = np.arange(self.dataset_size, dtype=np.int32)
          if self.load_images_into_memory:
              self.images = []
              if verbose: it = tqdm(self.filenames, desc='Loading images into memory', file=sys.stdout)
              else: it = self.filenames
              for filename in it:
                  with Image.open(filename) as image:
                      self.images.append(np.array(image, dtype=np.uint8))
  
          if ret: # In case we want to return these
              return self.images, self.filenames, self.labels, self.image_ids
  
      def parse_xml(self,
                    images_dirs,
                    image_set_filenames,
                    annotations_dirs=[],
                    classes=['background',
                             'aeroplane', 'bicycle', 'bird', 'boat',
                             'bottle', 'bus', 'car', 'cat',
                             'chair', 'cow', 'diningtable', 'dog',
                             'horse', 'motorbike', 'person', 'pottedplant',
                             'sheep', 'sofa', 'train', 'tvmonitor'],
                    include_classes = 'all',
                    exclude_truncated=False,
                    exclude_difficult=False,
                    ret=False,
                    verbose=True):
          '''
          This is an XML parser for the Pascal VOC datasets. It might be applicable to other datasets with minor changes to
          the code, but in its current form it expects the data format and XML tags of the Pascal VOC datasets.
  
          Arguments:
              images_dirs (list): A list of strings, where each string is the path of a directory that
                  contains images that are to be part of the dataset. This allows you to aggregate multiple datasets
                  into one (e.g. one directory that contains the images for Pascal VOC 2007, another that contains
                  the images for Pascal VOC 2012, etc.).
              image_set_filenames (list): A list of strings, where each string is the path of the text file with the image
                  set to be loaded. Must be one file per image directory given. These text files define what images in the
                  respective image directories are to be part of the dataset and simply contains one image ID per line
                  and nothing else.
              annotations_dirs (list, optional): A list of strings, where each string is the path of a directory that
                  contains the annotations (XML files) that belong to the images in the respective image directories given.
                  The directories must contain one XML file per image and the name of an XML file must be the image ID
                  of the image it belongs to. The content of the XML files must be in the Pascal VOC format.
              classes (list, optional): A list containing the names of the object classes as found in the
                  `name` XML tags. Must include the class `background` as the first list item. The order of this list
                  defines the class IDs.
              include_classes (list, optional): Either 'all' or a list of integers containing the class IDs that
                  are to be included in the dataset. If 'all', all ground truth boxes will be included in the dataset.
              exclude_truncated (bool, optional): If `True`, excludes boxes that are labeled as 'truncated'.
              exclude_difficult (bool, optional): If `True`, excludes boxes that are labeled as 'difficult'.
              ret (bool, optional): Whether or not to return the outputs of the parser.
              verbose (bool, optional): If `True`, prints out the progress for operations that may take a bit longer.
  
          Returns:
              None by default, optionally lists for whichever are available of images, image filenames, labels, image IDs,
              and a list indicating which boxes are annotated with the label "difficult".
          '''
          # Set class members.
          self.images_dirs = images_dirs
          self.annotations_dirs = annotations_dirs
          self.image_set_filenames = image_set_filenames
          self.classes = classes
          self.include_classes = include_classes
  
          # Erase data that might have been parsed before.
          self.filenames = []
          self.image_ids = []
          self.labels = []
          self.eval_neutral = []
          if not annotations_dirs:
              self.labels = None
              self.eval_neutral = None
              annotations_dirs = [None] * len(images_dirs)
  
          for images_dir, image_set_filename, annotations_dir in zip(images_dirs, image_set_filenames, annotations_dirs):
              # Read the image set file that so that we know all the IDs of all the images to be included in the dataset.
              with open(image_set_filename) as f:
                  image_ids = [line.strip() for line in f] # Note: These are strings, not integers.
                  self.image_ids += image_ids
  
              if verbose: it = tqdm(image_ids, desc="Processing image set '{}'".format(os.path.basename(image_set_filename)), file=sys.stdout)
              else: it = image_ids
  
              # Loop over all images in this dataset.
              for image_id in it:
  
                  filename = '{}'.format(image_id) + '.jpg'
                  self.filenames.append(os.path.join(images_dir, filename))
  
                  if not annotations_dir is None:
                      # Parse the XML file for this image.
                      with open(os.path.join(annotations_dir, image_id + '.xml')) as f:
                          soup = BeautifulSoup(f, 'xml')
  
                      folder = soup.folder.text # In case we want to return the folder in addition to the image file name. Relevant for determining which dataset an image belongs to.
                      #filename = soup.filename.text
  
                      boxes = [] # We'll store all boxes for this image here.
                      eval_neutr = [] # We'll store whether a box is annotated as "difficult" here.
                      objects = soup.find_all('object') # Get a list of all objects in this image.
  
                      # Parse the data for each object.
                      for obj in objects:
                          class_name = obj.find('name', recursive=False).text
                          class_id = self.classes.index(class_name)
                          # Check whether this class is supposed to be included in the dataset.
                          if (not self.include_classes == 'all') and (not class_id in self.include_classes): continue
                          pose = obj.find('pose', recursive=False).text
                          truncated = int(obj.find('truncated', recursive=False).text)
                          if exclude_truncated and (truncated == 1): continue
                          difficult = int(obj.find('difficult', recursive=False).text)
                          if exclude_difficult and (difficult == 1): continue
                          # Get the bounding box coordinates.
                          bndbox = obj.find('bndbox', recursive=False)
                          xmin = int(bndbox.xmin.text)
                          ymin = int(bndbox.ymin.text)
                          xmax = int(bndbox.xmax.text)
                          ymax = int(bndbox.ymax.text)
                          item_dict = {'folder': folder,
                                       'image_name': filename,
                                       'image_id': image_id,
                                       'class_name': class_name,
                                       'class_id': class_id,
                                       'pose': pose,
                                       'truncated': truncated,
                                       'difficult': difficult,
                                       'xmin': xmin,
                                       'ymin': ymin,
                                       'xmax': xmax,
                                       'ymax': ymax}
                          box = []
                          for item in self.labels_output_format:
                              box.append(item_dict[item])
                          boxes.append(box)
                          if difficult: eval_neutr.append(True)
                          else: eval_neutr.append(False)
  
                      self.labels.append(boxes)
                      self.eval_neutral.append(eval_neutr)
  
          self.dataset_size = len(self.filenames)
          self.dataset_indices = np.arange(self.dataset_size, dtype=np.int32)
          if self.load_images_into_memory:
              self.images = []
              if verbose: it = tqdm(self.filenames, desc='Loading images into memory', file=sys.stdout)
              else: it = self.filenames
              for filename in it:
                  with Image.open(filename) as image:
                      self.images.append(np.array(image, dtype=np.uint8))
  
          if ret:
              return self.images, self.filenames, self.labels, self.image_ids, self.eval_neutral
  
      def parse_json(self,
                     images_dirs,
                     annotations_filenames,
                     ground_truth_available=False,
                     include_classes='all',
                     ret=False,
                     verbose=True):
          '''
          This is an JSON parser for the MS COCO datasets. It might be applicable to other datasets with minor changes to
          the code, but in its current form it expects the JSON format of the MS COCO datasets.
  
          Arguments:
              images_dirs (list, optional): A list of strings, where each string is the path of a directory that
                  contains images that are to be part of the dataset. This allows you to aggregate multiple datasets
                  into one (e.g. one directory that contains the images for MS COCO Train 2014, another one for MS COCO
                  Val 2014, another one for MS COCO Train 2017 etc.).
              annotations_filenames (list): A list of strings, where each string is the path of the JSON file
                  that contains the annotations for the images in the respective image directories given, i.e. one
                  JSON file per image directory that contains the annotations for all images in that directory.
                  The content of the JSON files must be in MS COCO object detection format. Note that these annotations
                  files do not necessarily need to contain ground truth information. MS COCO also provides annotations
                  files without ground truth information for the test datasets, called `image_info_[...].json`.
              ground_truth_available (bool, optional): Set `True` if the annotations files contain ground truth information.
              include_classes (list, optional): Either 'all' or a list of integers containing the class IDs that
                  are to be included in the dataset. If 'all', all ground truth boxes will be included in the dataset.
  