text: ############################################################
  #  Dataset
  ############################################################
  
  import numpy as np
  import scipy.misc
  import skimage.color
  import skimage.io
  import scipy.ndimage
  
  class Dataset(object):
      """The base class for dataset classes.
      To use it, create a new class that adds functions specific to the dataset
      you want to use. For example:
  
      class CatsAndDogsDataset(Dataset):
          def load_cats_and_dogs(self):
              ...
          def load_mask(self, image_id):
              ...
          def image_reference(self, image_id):
              ...
  
      See COCODataset and ShapesDataset as examples.
      """
  
      def __init__(self, class_map=None):
          self._image_ids = []
          self.image_info = []
          # Background is always the first class
          self.class_info = [{"source": "", "id": 0, "name": "BG"}]
          self.source_class_ids = {}
  
      def add_class(self, source, class_id, class_name):
          assert "." not in source, "Source name cannot contain a dot"
          # Does the class exist already?
          for info in self.class_info:
              if info['source'] == source and info["id"] == class_id:
                  # source.class_id combination already available, skip
                  return
          # Add the class
          self.class_info.append({
              "source": source,
              "id": class_id,
              "name": class_name,
          })
  
      def add_image(self, source, image_id, path, **kwargs):
          image_info = {
              "id": image_id,
              "source": source,
              "path": path,
          }
          image_info.update(kwargs)
          self.image_info.append(image_info)
  
      def image_reference(self, image_id):
          """Return a link to the image in its source Website or details about
          the image that help looking it up or debugging it.
  
          Override for your dataset, but pass to this function
          if you encounter images not in your dataset.
          """
          return ""
  
      def prepare(self, class_map=None):
          """Prepares the Dataset class for use.
  
          TODO: class map is not supported yet. When done, it should handle mapping
                classes from different datasets to the same class ID.
          """
  
          def clean_name(name):
              """Returns a shorter version of object names for cleaner display."""
              return ",".join(name.split(",")[:1])
  
          # Build (or rebuild) everything else from the info dicts.
          self.num_classes = len(self.class_info)
          self.class_ids = np.arange(self.num_classes)
          self.class_names = [clean_name(c["name"]) for c in self.class_info]
          self.num_images = len(self.image_info)
          self._image_ids = np.arange(self.num_images)
  
          self.class_from_source_map = {"{}.{}".format(info['source'], info['id']): id
                                        for info, id in zip(self.class_info, self.class_ids)}
  
          # Map sources to class_ids they support
          self.sources = list(set([i['source'] for i in self.class_info]))
          self.source_class_ids = {}
          # Loop over datasets
          for source in self.sources:
              self.source_class_ids[source] = []
              # Find classes that belong to this dataset
              for i, info in enumerate(self.class_info):
                  # Include BG class in all datasets
                  if i == 0 or source == info['source']:
                      self.source_class_ids[source].append(i)
  
      def map_source_class_id(self, source_class_id):
          """Takes a source class ID and returns the int class ID assigned to it.
  
          For example:
          dataset.map_source_class_id("coco.12") -> 23
          """
          return self.class_from_source_map[source_class_id]
  
      def get_source_class_id(self, class_id, source):
          """Map an internal class ID to the corresponding class ID in the source dataset."""
          info = self.class_info[class_id]
          assert info['source'] == source
          return info['id']
  
      def append_data(self, class_info, image_info):
          self.external_to_class_id = {}
          for i, c in enumerate(self.class_info):
              for ds, id in c["map"]:
                  self.external_to_class_id[ds + str(id)] = i
  
          # Map external image IDs to internal ones.
          self.external_to_image_id = {}
          for i, info in enumerate(self.image_info):
              self.external_to_image_id[info["ds"] + str(info["id"])] = i
  
      @property
      def image_ids(self):
          return self._image_ids
  
      def source_image_link(self, image_id):
          """Returns the path or URL to the image.
          Override this to return a URL to the image if it's availble online for easy
          debugging.
          """
          return self.image_info[image_id]["path"]
  
      def load_image(self, image_id):
          """Load the specified image and return a [H,W,3] Numpy array.
          """
          # Load image
          image = skimage.io.imread(self.image_info[image_id]['path'])
          # If grayscale. Convert to RGB for consistency.
          if image.ndim != 3:
              image = skimage.color.gray2rgb(image)
          return image
  
      def load_mask(self, image_id):
          """Load instance masks for the given image.
  
          Different datasets use different ways to store masks. Override this
          method to load instance masks and return them in the form of am
          array of binary masks of shape [height, width, instances].
  
          Returns:
              masks: A bool array of shape [height, width, instance count] with
                  a binary mask per instance.
              class_ids: a 1D array of class IDs of the instance masks.
          """
          # Override this function to load a mask from your dataset.
          # Otherwise, it returns an empty mask.
          mask = np.empty([0, 0, 0])
          class_ids = np.empty([0], np.int32)
          return mask, class_ids
  
  
  def resize_image(image, min_dim=None, max_dim=None, padding=False):
      """
      Resizes an image keeping the aspect ratio.
  
      min_dim: if provided, resizes the image such that it's smaller
          dimension == min_dim
      max_dim: if provided, ensures that the image longest side doesn't
          exceed this value.
      padding: If true, pads image with zeros so it's size is max_dim x max_dim
  
      Returns:
      image: the resized image
      window: (y1, x1, y2, x2). If max_dim is provided, padding might
          be inserted in the returned image. If so, this window is the
          coordinates of the image part of the full image (excluding
          the padding). The x2, y2 pixels are not included.
      scale: The scale factor used to resize the image
      padding: Padding added to the image [(top, bottom), (left, right), (0, 0)]
      """
      # Default window (y1, x1, y2, x2) and default scale == 1.
      h, w = image.shape[:2]
      window = (0, 0, h, w)
      scale = 1
  
      # Scale?
      if min_dim:
          # Scale up but not down
          scale = max(1, min_dim / min(h, w))
      # Does it exceed max dim?
      if max_dim:
          image_max = max(h, w)
          if round(image_max * scale) > max_dim:
              scale = max_dim / image_max
      # Resize image and mask
      if scale != 1:
          image = scipy.misc.imresize(
              image, (round(h * scale), round(w * scale)))
      # Need padding?
      if padding:
          # Get new height and width
          h, w = image.shape[:2]
          top_pad = (max_dim - h) // 2
          bottom_pad = max_dim - h - top_pad
          left_pad = (max_dim - w) // 2
          right_pad = max_dim - w - left_pad
          padding = [(top_pad, bottom_pad), (left_pad, right_pad), (0, 0)]
          image = np.pad(image, padding, mode='constant', constant_values=0)
          window = (top_pad, left_pad, h + top_pad, w + left_pad)
      return image, window, scale, padding
  
  
  def resize_2d(data, scale, padding, pad_val):
      """Resizes a mask using the given scale and padding.
      Typically, you get the scale and padding from resize_image() to
      ensure both, the image and the mask, are resized consistently.
  
      scale: mask scaling factor
      padding: Padding to add to the mask in the form
              [(top, bottom), (left, right), (0, 0)]
      """
      h, w = data.shape[:2]
      mask = scipy.ndimage.zoom(data, zoom=[scale, scale], order=0)
      mask = np.pad(mask, padding[:2], mode='constant', constant_values=pad_val)
      return mask
  
  def resize_3d(data, scale, padding, pad_val):
      """Resizes a mask using the given scale and padding.
      Typically, you get the scale and padding from resize_image() to
      ensure both, the image and the mask, are resized consistently.
  
      scale: mask scaling factor
      padding: Padding to add to the mask in the form
              [(top, bottom), (left, right), (0, 0)]
      """
      h, w = data.shape[:2]
      mask = scipy.ndimage.zoom(data, zoom=[scale, scale, 1], order=0)
      mask = np.pad(mask, padding, mode='constant', constant_values=pad_val)
      return mask
  