CoolFace
Apppublic

karolmajek/Axial-DeepLab-SWideRNet

sourceHugging Faceupdated 5y agoView on Hugging Face
0likes
sample_generator.py652 linesDownload Raw Back to data
1# coding=utf-82# Copyright 2021 The Deeplab2 Authors.3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8#     http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.15 16"""This file contains code to get a sample from a dataset."""17 18import functools19 20import numpy as np21import tensorflow as tf22 23from deeplab2 import common24from deeplab2.data import dataset_utils25from deeplab2.data.preprocessing import input_preprocessing as preprocessing26 27 28def _compute_gaussian_from_std(sigma):29  """Computes the Gaussian and its size from a given standard deviation."""30  size = int(6 * sigma + 3)31  x = np.arange(size, dtype=np.float)32  y = x[:, np.newaxis]33  x0, y0 = 3 * sigma + 1, 3 * sigma + 134  return np.exp(-((x - x0)**2 + (y - y0)**2) / (2 * sigma**2)), size35 36 37class PanopticSampleGenerator:38  """This class generates samples from images and labels."""39 40  def __init__(self,41               dataset_info,42               is_training,43               crop_size,44               min_resize_value=None,45               max_resize_value=None,46               resize_factor=None,47               min_scale_factor=1.,48               max_scale_factor=1.,49               scale_factor_step_size=0,50               autoaugment_policy_name=None,51               only_semantic_annotations=False,52               thing_id_mask_annotations=False,53               max_thing_id=128,54               sigma=8,55               focus_small_instances=None):56    """Initializes the panoptic segmentation generator.57 58    Args:59      dataset_info: A dictionary with the following keys.60      - `name`: String, dataset name.61      - `ignore_label`: Integer, ignore label.62      - `class_has_instances_list`: A list of integers indicating which63        class has instance annotations.64      - `panoptic_label_divisor`: Integer, panoptic label divisor.65      - `num_classes`: Integer, number of classes.66      - `is_video_dataset`: Boolean, is video dataset or not.67      is_training: Boolean, is training mode or not.68      crop_size: Image crop size [height, width].69      min_resize_value: A 2-tuple of (height, width), desired minimum value70        after resize. If a single element is given, then height and width share71        the same value. None, empty or having 0 indicates no minimum value will72        be used.73      max_resize_value: A 2-tuple of (height, width), maximum allowed value74        after resize. If a single element is given, then height and width75        share the same value. None, empty or having 0 indicates no maximum76        value will be used.77      resize_factor: Resized dimensions are multiple of factor plus one.78      min_scale_factor: Minimum scale factor for random scale augmentation.79      max_scale_factor: Maximum scale factor for random scale augmentation.80      scale_factor_step_size: The step size from min scale factor to max scale81        factor. The input is randomly scaled based on the value of82        (min_scale_factor, max_scale_factor, scale_factor_step_size).83      autoaugment_policy_name: String, autoaugment policy name. See84        autoaugment_policy.py for available policies.85      only_semantic_annotations: An optional flag indicating whether the model86        needs only semantic annotations (default: False).87      thing_id_mask_annotations: An optional flag indicating whether the model88        needs thing_id_mask annotations. When `thing_id_mask_annotations` is89        True, we will additionally return mask annotation for each `thing`90        instance, encoded with a unique thing_id. This ground-truth annotation91        could be used to learn a better segmentation mask for each instance.92        `thing_id` indicates the number of unique thing-ID to each instance in93        an image, starting the counting from 0 (default: False).94      max_thing_id: The maximum number of possible thing instances per image. It95        is used together when thing_id_mask_annotations = True, representing the96        maximum thing ID encoded in the thing_id_mask. (default: 128).97      sigma: The standard deviation of the Gaussian used to encode the center98        keypoint (default: 8).99      focus_small_instances: An optional dict that defines how to deal with100        small instances (default: None):101        -`threshold`: An integer defining the threshold pixel number for an102          instance to be considered small.103        -`weight`: A number that defines the loss weight for small instances.104    """105    self._dataset_info = dataset_info106    self._ignore_label = self._dataset_info['ignore_label']107    self._only_semantic_annotations = only_semantic_annotations108    self._sigma = sigma109    self._instance_area_threshold = 0110    self._small_instance_weight = 1.0111    self._thing_id_mask_annotations = thing_id_mask_annotations112    self._max_thing_id = max_thing_id113    self._is_training = is_training114    self._preprocessing_fn = functools.partial(115        preprocessing.preprocess_image_and_label,116        crop_height=crop_size[0],117        crop_width=crop_size[1],118        min_resize_value=min_resize_value,119        max_resize_value=max_resize_value,120        resize_factor=resize_factor,121        min_scale_factor=min_scale_factor,122        max_scale_factor=max_scale_factor,123        scale_factor_step_size=scale_factor_step_size,124        autoaugment_policy_name=autoaugment_policy_name,125        ignore_label=self._ignore_label *126        self._dataset_info['panoptic_label_divisor'],127        is_training=self._is_training)128 129    if focus_small_instances is not None:130      self._instance_area_threshold = focus_small_instances['threshold']131      self._small_instance_weight = focus_small_instances['weight']132 133    self._gaussian, self._gaussian_size = _compute_gaussian_from_std(134        self._sigma)135    self._gaussian = tf.cast(tf.reshape(self._gaussian, [-1]), tf.float32)136 137  def __call__(self, sample_dict):138    """Gets a sample.139 140    Args:141      sample_dict: A dictionary with the following keys and values:142      - `image`: A tensor of shape [image_height, image_width, 3].143      - `image_name`: String, image name.144      - `label`: A tensor of shape [label_height, label_width, 1] or None.145      - `height`: An integer specifying the height of the image.146      - `width`: An integer specifying the width of the image.147      - `sequence`: An optional string specifying the sequence name.148      - `prev_image`: An optional tensor of the same shape as `image`.149      - `prev_label`: An optional tensor of the same shape as `label`.150      - `next_image`: An optional next-frame tensor of the shape of `image`.151      - `next_label`: An optional next-frame tensor of the shape of `label`.152 153    Returns:154      sample: A dictionary storing required data for panoptic segmentation.155    """156    return self.call(**sample_dict)157 158  def call(self,159           image,160           image_name,161           label,162           height,163           width,164           sequence='',165           prev_image=None,166           prev_label=None,167           next_image=None,168           next_label=None):169    """Gets a sample.170 171    Args:172      image: A tensor of shape [image_height, image_width, 3].173      image_name: String, image name.174      label: A tensor of shape [label_height, label_width, 1] or None.175      height: An integer specifying the height of the image.176      width: An integer specifying the width of the image.177      sequence: An optional string specifying the sequence name.178      prev_image: An optional tensor of shape [image_height, image_width, 3].179      prev_label: An optional tensor of shape [label_height, label_width, 1].180      next_image: An optional tensor of shape [image_height, image_width, 3].181      next_label: An optional tensor of shape [label_height, label_width, 1].182 183    Returns:184      sample: A dictionary storing required data for panoptic segmentation.185 186    Raises:187      ValueError: An error occurs when the label shape is invalid.188      NotImplementedError: An error occurs when thing_id_mask_annotations comes189        together with prev_image or prev_label, not currently implemented.190    """191    if label is not None:192      label.get_shape().assert_is_compatible_with(193          tf.TensorShape([None, None, 1]))194      original_label = tf.cast(label, dtype=tf.int32, name='original_label')195      if next_label is not None:196        original_next_label = tf.cast(197            next_label, dtype=tf.int32, name='original_next_label')198    # Reusing the preprocessing function for both next and prev samples.199    if next_image is not None:200      resized_image, image, label, next_image, next_label = (201          self._preprocessing_fn(202              image, label, prev_image=next_image, prev_label=next_label))203    else:204      resized_image, image, label, prev_image, prev_label = (205          self._preprocessing_fn(206              image, label, prev_image=prev_image, prev_label=prev_label))207    sample = {208        common.IMAGE: image209    }210    if prev_image is not None:211      sample[common.IMAGE] = tf.concat([image, prev_image], axis=2)212    if next_image is not None:213      sample[common.NEXT_IMAGE] = next_image214      sample[common.IMAGE] = tf.concat([image, next_image], axis=2)215    if label is not None:216      # Panoptic label for crowd regions will be ignore_label.217      semantic_label, panoptic_label, thing_mask, crowd_region = (218          dataset_utils.get_semantic_and_panoptic_label(219              self._dataset_info, label, self._ignore_label))220      sample[common.GT_SEMANTIC_KEY] = tf.squeeze(semantic_label, axis=2)221      semantic_weights = tf.ones_like(semantic_label, dtype=tf.float32)222      sample[common.SEMANTIC_LOSS_WEIGHT_KEY] = tf.squeeze(223          semantic_weights, axis=2)224      sample[common.GT_IS_CROWD] = tf.squeeze(crowd_region, axis=2)225 226      if not self._only_semantic_annotations:227        # The sample will have the original label including crowd regions.228        sample[common.GT_PANOPTIC_KEY] = tf.squeeze(label, axis=2)229        # Compute center loss for all non-crowd and non-ignore pixels.230        non_crowd_and_non_ignore_regions = tf.logical_and(231            tf.logical_not(crowd_region),232            tf.not_equal(semantic_label, self._ignore_label))233        sample[common.CENTER_LOSS_WEIGHT_KEY] = tf.squeeze(tf.cast(234            non_crowd_and_non_ignore_regions, tf.float32), axis=2)235        # Compute regression loss only for thing pixels that are not crowd.236        non_crowd_things = tf.logical_and(237            tf.logical_not(crowd_region), thing_mask)238        sample[common.REGRESSION_LOSS_WEIGHT_KEY] = tf.squeeze(tf.cast(239            non_crowd_things, tf.float32), axis=2)240 241        prev_panoptic_label = None242        next_panoptic_label = None243        if prev_label is not None:244          _, prev_panoptic_label, _, _ = (245              dataset_utils.get_semantic_and_panoptic_label(246                  self._dataset_info, prev_label, self._ignore_label))247        if next_label is not None:248          _, next_panoptic_label, _, _ = (249              dataset_utils.get_semantic_and_panoptic_label(250                  self._dataset_info, next_label, self._ignore_label))251        (sample[common.GT_INSTANCE_CENTER_KEY],252         sample[common.GT_INSTANCE_REGRESSION_KEY],253         sample[common.SEMANTIC_LOSS_WEIGHT_KEY],254         prev_center_map,255         frame_center_offsets,256         next_offset) = self._generate_gt_center_and_offset(257             panoptic_label, semantic_weights, prev_panoptic_label,258             next_panoptic_label)259 260        sample[common.GT_INSTANCE_REGRESSION_KEY] = tf.cast(261            sample[common.GT_INSTANCE_REGRESSION_KEY], tf.float32)262 263        if next_label is not None:264          sample[common.GT_NEXT_INSTANCE_REGRESSION_KEY] = tf.cast(265              next_offset, tf.float32)266          sample[common.NEXT_REGRESSION_LOSS_WEIGHT_KEY] = tf.cast(267              tf.greater(tf.reduce_sum(tf.abs(next_offset), axis=2), 0),268              tf.float32)269 270        # Only squeeze center map and semantic loss weights, as regression map271        # has two channels (x and y offsets).272        sample[common.GT_INSTANCE_CENTER_KEY] = tf.squeeze(273            sample[common.GT_INSTANCE_CENTER_KEY], axis=2)274        sample[common.SEMANTIC_LOSS_WEIGHT_KEY] = tf.squeeze(275            sample[common.SEMANTIC_LOSS_WEIGHT_KEY], axis=2)276 277        if prev_label is not None:278          sample[common.GT_FRAME_OFFSET_KEY] = frame_center_offsets279          sample[common.GT_FRAME_OFFSET_KEY] = tf.cast(280              sample[common.GT_FRAME_OFFSET_KEY], tf.float32)281          frame_offsets_present = tf.logical_or(282              tf.not_equal(frame_center_offsets[..., 0], 0),283              tf.not_equal(frame_center_offsets[..., 1], 0))284          sample[common.FRAME_REGRESSION_LOSS_WEIGHT_KEY] = tf.cast(285              frame_offsets_present, tf.float32)286          if self._is_training:287            sample[common.IMAGE] = tf.concat(288                [sample[common.IMAGE], prev_center_map], axis=2)289 290        if self._thing_id_mask_annotations:291          if any([prev_image is not None,292                  prev_label is not None,293                  next_image is not None,294                  next_label is not None]):295            raise NotImplementedError(296                'Current implementation of Max-DeepLab does not support '297                + 'prev_image, prev_label, next_image, or next_label.')298          thing_id_mask, thing_id_class = (299              self._generate_thing_id_mask_and_class(300                  panoptic_label, non_crowd_things))301          sample[common.GT_THING_ID_MASK_KEY] = tf.squeeze(302              thing_id_mask, axis=2)303          sample[common.GT_THING_ID_CLASS_KEY] = thing_id_class304 305    if not self._is_training:306      # Resized image is only used during visualization.307      sample[common.RESIZED_IMAGE] = resized_image308      sample[common.IMAGE_NAME] = image_name309      sample[common.GT_SIZE_RAW] = tf.stack([height, width], axis=0)310      if self._dataset_info['is_video_dataset']:311        sample[common.SEQUENCE_ID] = sequence312      # Keep original labels for evaluation.313      if label is not None:314        orig_semantic_label, _, _, orig_crowd_region = (315            dataset_utils.get_semantic_and_panoptic_label(316                self._dataset_info, original_label, self._ignore_label))317        sample[common.GT_SEMANTIC_RAW] = tf.squeeze(orig_semantic_label, axis=2)318        if not self._only_semantic_annotations:319          sample[common.GT_PANOPTIC_RAW] = tf.squeeze(original_label, axis=2)320          sample[common.GT_IS_CROWD_RAW] = tf.squeeze(orig_crowd_region)321          if next_label is not None:322            sample[common.GT_NEXT_PANOPTIC_RAW] = tf.squeeze(323                original_next_label, axis=2)324    return sample325 326  def _generate_thing_id_mask_and_class(self,327                                        panoptic_label,328                                        non_crowd_things):329    """Generates the ground-truth thing-ID masks and their class labels.330 331    It computes thing-ID mask and class with unique ID for each thing instance.332    `thing_id` indicates the number of unique thing-ID to each instance in an333    image, starting the counting from 0. Each pixel in thing_id_mask is labeled334    with the corresponding thing-ID.335 336    Args:337      panoptic_label: A tf.Tensor of shape [height, width, 1].338      non_crowd_things: A tf.Tensor of shape [height, width, 1], indicating339        non-crowd and thing-class regions.340 341    Returns:342      thing_id_mask: A tf.Tensor of shape [height, width, 1]. It assigns each343        non-crowd thing instance a unique mask-ID label, starting from 0.344        Unassigned pixels are set to -1.345      thing_id_class: A tf.Tensor of shape [max_thing_id]. It contains semantic346        ID of each instance assigned to thing_id_mask. The remaining347        (max_thing_id - num_things) elements are set to -1.348 349    Raises:350      ValueError: An error occurs when the thing-ID mask contains stuff or crowd351        region.352      ValueError: An error occurs when thing_count is greater or equal to353        self._max_thing_id.354 355    """356    unique_ids, _ = tf.unique(tf.reshape(panoptic_label, [-1]))357    thing_id_mask = -tf.ones_like(panoptic_label)358    thing_id_class = -tf.ones(self._max_thing_id)359    thing_count = 0360    for panoptic_id in unique_ids:361      semantic_id = panoptic_id // self._dataset_info['panoptic_label_divisor']362      # Filter out IDs that are not thing instances (i.e., IDs for ignore_label,363      # stuff classes or crowd). Stuff classes and crowd regions both have IDs364      # of the form panoptic_id = semantic_id * label_divisor (i.e., instance id365      # = 0)366      if (semantic_id == self._dataset_info['ignore_label'] or367          panoptic_id % self._dataset_info['panoptic_label_divisor'] == 0):368        continue369 370      assert_stuff_crowd = tf.debugging.Assert(371          tf.reduce_all(non_crowd_things[panoptic_label == panoptic_id]),372          ['thing-ID mask here must not contain stuff or crowd region.'])373      with tf.control_dependencies([assert_stuff_crowd]):374        panoptic_id = tf.identity(panoptic_id)375 376      thing_id_mask = tf.where(panoptic_label == panoptic_id,377                               thing_count, thing_id_mask)378 379      assert_thing_count = tf.debugging.Assert(380          thing_count < self._max_thing_id,381          ['thing_count must be smaller than self._max_thing_id.'])382      with tf.control_dependencies([assert_thing_count]):383        thing_count = tf.identity(thing_count)384 385      thing_id_class = tf.tensor_scatter_nd_update(386          thing_id_class, [[thing_count]], [semantic_id])387      thing_count += 1388    return thing_id_mask, thing_id_class389 390  def _generate_prev_centers_with_noise(self,391                                        panoptic_label,392                                        offset_noise_factor=0.05,393                                        false_positive_rate=0.2,394                                        false_positive_noise_factor=0.05):395    """Generates noisy center predictions for the previous frame.396 397    Args:398      panoptic_label: A tf.Tensor of shape [height, width, 1].399      offset_noise_factor: An optional float defining the maximum fraction of400        the object size that is used to displace the previous center.401      false_positive_rate: An optional float indicating at which probability402        false positives should be added.403      false_positive_noise_factor: An optional float defining the maximum404        fraction of the object size that is used to displace the false positive405        center.406 407    Returns:408      A tuple of (center, ids_to_center) with both being tf.Tensor of shape409      [height, width, 1] and shape [N, 2] where N is the number of unique IDs.410    """411    height = tf.shape(panoptic_label)[0]412    width = tf.shape(panoptic_label)[1]413 414    # Pad center to make boundary handling easier.415    center_pad_begin = int(round(3 * self._sigma + 1))416    center_pad_end = int(round(3 * self._sigma + 2))417    center_pad = center_pad_begin + center_pad_end418 419    center = tf.zeros((height + center_pad, width + center_pad))420    unique_ids, _ = tf.unique(tf.reshape(panoptic_label, [-1]))421    ids_to_center_x = tf.zeros_like(unique_ids, dtype=tf.int32)422    ids_to_center_y = tf.zeros_like(unique_ids, dtype=tf.int32)423 424    for panoptic_id in unique_ids:425      semantic_id = panoptic_id // self._dataset_info['panoptic_label_divisor']426      # Filter out IDs that should be ignored, are stuff classes or crowd.427      # Stuff classes and crowd regions both have IDs of the form panoptic_id =428      # semantic_id * label_divisor429      if (semantic_id == self._dataset_info['ignore_label'] or430          panoptic_id % self._dataset_info['panoptic_label_divisor'] == 0):431        continue432 433      # Convert [[y0, x0, 0], ...] to [[y0, ...], [x0, ...], [0, ...]].434      mask_index = tf.cast(435          tf.transpose(tf.where(panoptic_label == panoptic_id)), tf.float32)436      centers = tf.reduce_mean(mask_index, axis=1)437      bbox_size = (438          tf.reduce_max(mask_index, axis=1) - tf.reduce_min(mask_index, axis=1))439 440      # Add noise.441      center_y = (442          centers[0] + tf.random.normal([], dtype=tf.float32) *443          offset_noise_factor * bbox_size[0])444      center_x = (445          centers[1] + tf.random.normal([], dtype=tf.float32) *446          offset_noise_factor * bbox_size[1])447 448      center_x = tf.minimum(449          tf.maximum(tf.cast(tf.round(center_x), tf.int32), 0), width - 1)450      center_y = tf.minimum(451          tf.maximum(tf.cast(tf.round(center_y), tf.int32), 0), height - 1)452 453      id_index = tf.where(tf.equal(panoptic_id, unique_ids))454      ids_to_center_x = tf.tensor_scatter_nd_update(455          ids_to_center_x, id_index, tf.expand_dims(center_x, axis=0))456      ids_to_center_y = tf.tensor_scatter_nd_update(457          ids_to_center_y, id_index, tf.expand_dims(center_y, axis=0))458 459      def add_center_gaussian(center_x_coord, center_y_coord, center):460        # Due to the padding with center_pad_begin in center, the computed461        # center becomes the upper left corner in the center tensor.462        upper_left = center_x_coord, center_y_coord463        bottom_right = (upper_left[0] + self._gaussian_size,464                        upper_left[1] + self._gaussian_size)465 466        indices_x, indices_y = tf.meshgrid(467            tf.range(upper_left[0], bottom_right[0]),468            tf.range(upper_left[1], bottom_right[1]))469        indices = tf.transpose(470            tf.stack([tf.reshape(indices_y, [-1]),471                      tf.reshape(indices_x, [-1])]))472 473        return tf.tensor_scatter_nd_max(474            center, indices, self._gaussian, name='center_scatter')475 476      center = add_center_gaussian(center_x, center_y, center)477      # Generate false positives.478      center_y = (479          tf.cast(center_y, dtype=tf.float32) +480          tf.random.normal([], dtype=tf.float32) * false_positive_noise_factor *481          bbox_size[0])482      center_x = (483          tf.cast(center_x, dtype=tf.float32) +484          tf.random.normal([], dtype=tf.float32) * false_positive_noise_factor *485          bbox_size[1])486 487      center_x = tf.minimum(488          tf.maximum(tf.cast(tf.round(center_x), tf.int32), 0), width - 1)489      center_y = tf.minimum(490          tf.maximum(tf.cast(tf.round(center_y), tf.int32), 0), height - 1)491      # Draw a sample to decide whether to add a false positive or not.492      center = center + tf.cast(493          tf.random.uniform([], dtype=tf.float32) < false_positive_rate,494          tf.float32) * (495              add_center_gaussian(center_x, center_y, center) - center)496 497    center = center[center_pad_begin:(center_pad_begin + height),498                    center_pad_begin:(center_pad_begin + width)]499    center = tf.expand_dims(center, -1)500    return center, unique_ids, ids_to_center_x, ids_to_center_y501 502  def _generate_gt_center_and_offset(self,503                                     panoptic_label,504                                     semantic_weights,505                                     prev_panoptic_label=None,506                                     next_panoptic_label=None):507    """Generates the ground-truth center and offset from the panoptic labels.508 509    Additionally, the per-pixel weights for the semantic branch are increased510    for small instances. In case, prev_panoptic_label is passed, it also511    computes the previous center heatmap with random noise and the offsets512    between center maps.513 514    Args:515      panoptic_label: A tf.Tensor of shape [height, width, 1].516      semantic_weights: A tf.Tensor of shape [height, width, 1].517      prev_panoptic_label: An optional tf.Tensor of shape [height, width, 1].518      next_panoptic_label: An optional tf.Tensor of shape [height, width, 1].519 520    Returns:521      A tuple (center, offsets, weights, prev_center, frame_offset*,522      next_offset) with each being a tf.Tensor of shape [height, width, 1 (2*)].523      If prev_panoptic_label is None, prev_center and frame_offset are None.524      If next_panoptic_label is None, next_offset is None.525    """526    height = tf.shape(panoptic_label)[0]527    width = tf.shape(panoptic_label)[1]528 529    # Pad center to make boundary handling easier.530    center_pad_begin = int(round(3 * self._sigma + 1))531    center_pad_end = int(round(3 * self._sigma + 2))532    center_pad = center_pad_begin + center_pad_end533 534    center = tf.zeros((height + center_pad, width + center_pad))535    offset_x = tf.zeros((height, width, 1), dtype=tf.int32)536    offset_y = tf.zeros((height, width, 1), dtype=tf.int32)537    unique_ids, _ = tf.unique(tf.reshape(panoptic_label, [-1]))538 539    prev_center = None540    frame_offsets = None541    # Due to loop handling in tensorflow, these variables had to be defined for542    # all cases.543    frame_offset_x = tf.zeros((height, width, 1), dtype=tf.int32)544    frame_offset_y = tf.zeros((height, width, 1), dtype=tf.int32)545 546    # Next-frame instance offsets.547    next_offset = None548    next_offset_y = tf.zeros((height, width, 1), dtype=tf.int32)549    next_offset_x = tf.zeros((height, width, 1), dtype=tf.int32)550 551    if prev_panoptic_label is not None:552      (prev_center, prev_unique_ids, prev_centers_x, prev_centers_y553      ) = self._generate_prev_centers_with_noise(prev_panoptic_label)554 555    for panoptic_id in unique_ids:556      semantic_id = panoptic_id // self._dataset_info['panoptic_label_divisor']557      # Filter out IDs that should be ignored, are stuff classes or crowd.558      # Stuff classes and crowd regions both have IDs of the form panopti_id =559      # semantic_id * label_divisor560      if (semantic_id == self._dataset_info['ignore_label'] or561          panoptic_id % self._dataset_info['panoptic_label_divisor'] == 0):562        continue563 564      # Convert [[y0, x0, 0], ...] to [[y0, ...], [x0, ...], [0, ...]].565      mask_index = tf.transpose(tf.where(panoptic_label == panoptic_id))566      mask_y_index = mask_index[0]567      mask_x_index = mask_index[1]568 569      next_mask_index = None570      next_mask_y_index = None571      next_mask_x_index = None572      if next_panoptic_label is not None:573        next_mask_index = tf.transpose(574            tf.where(next_panoptic_label == panoptic_id))575        next_mask_y_index = next_mask_index[0]576        next_mask_x_index = next_mask_index[1]577 578      instance_area = tf.shape(mask_x_index)579      if instance_area < self._instance_area_threshold:580        semantic_weights = tf.where(panoptic_label == panoptic_id,581                                    self._small_instance_weight,582                                    semantic_weights)583 584      centers = tf.reduce_mean(tf.cast(mask_index, tf.float32), axis=1)585 586      center_x = tf.cast(tf.round(centers[1]), tf.int32)587      center_y = tf.cast(tf.round(centers[0]), tf.int32)588 589      # Due to the padding with center_pad_begin in center, the computed center590      # becomes the upper left corner in the center tensor.591      upper_left = center_x, center_y592      bottom_right = (upper_left[0] + self._gaussian_size,593                      upper_left[1] + self._gaussian_size)594 595      indices_x, indices_y = tf.meshgrid(596          tf.range(upper_left[0], bottom_right[0]),597          tf.range(upper_left[1], bottom_right[1]))598      indices = tf.transpose(599          tf.stack([tf.reshape(indices_y, [-1]),600                    tf.reshape(indices_x, [-1])]))601 602      center = tf.tensor_scatter_nd_max(603          center, indices, self._gaussian, name='center_scatter')604      offset_y = tf.tensor_scatter_nd_update(605          offset_y,606          tf.transpose(mask_index),607          center_y - tf.cast(mask_y_index, tf.int32),608          name='offset_y_scatter')609      offset_x = tf.tensor_scatter_nd_update(610          offset_x,611          tf.transpose(mask_index),612          center_x - tf.cast(mask_x_index, tf.int32),613          name='offset_x_scatter')614      if prev_panoptic_label is not None:615        mask = tf.equal(prev_unique_ids, panoptic_id)616        if tf.math.count_nonzero(mask) > 0:617          prev_center_x = prev_centers_x[mask]618          prev_center_y = prev_centers_y[mask]619 620          frame_offset_y = tf.tensor_scatter_nd_update(621              frame_offset_y,622              tf.transpose(mask_index),623              prev_center_y - tf.cast(mask_y_index, tf.int32),624              name='frame_offset_y_scatter')625          frame_offset_x = tf.tensor_scatter_nd_update(626              frame_offset_x,627              tf.transpose(mask_index),628              prev_center_x - tf.cast(mask_x_index, tf.int32),629              name='frame_offset_x_scatter')630      if next_panoptic_label is not None:631        next_offset_y = tf.tensor_scatter_nd_update(632            next_offset_y,633            tf.transpose(next_mask_index),634            center_y - tf.cast(next_mask_y_index, tf.int32),635            name='next_offset_y_scatter')636        next_offset_x = tf.tensor_scatter_nd_update(637            next_offset_x,638            tf.transpose(next_mask_index),639            center_x - tf.cast(next_mask_x_index, tf.int32),640            name='next_offset_x_scatter')641 642    offset = tf.concat([offset_y, offset_x], axis=2)643    center = center[center_pad_begin:(center_pad_begin + height),644                    center_pad_begin:(center_pad_begin + width)]645    center = tf.expand_dims(center, -1)646    if prev_panoptic_label is not None:647      frame_offsets = tf.concat([frame_offset_y, frame_offset_x], axis=2)648    if next_panoptic_label is not None:649      next_offset = tf.concat([next_offset_y, next_offset_x], axis=2)650    return (center, offset, semantic_weights, prev_center, frame_offsets,651            next_offset)652