CoolFace
Apppublic

karolmajek/maxdeeplab

sourceHugging Faceupdated 5y agoView on Hugging Face
0likes
create_step_panoptic_maps.py306 linesDownload Raw Back to utils
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 16r"""Creates STEP panoptic map from semantic and instance maps.17 18This script implements the process of merging semantic maps (from our extra19annotations[1]) and instance maps (collected from the MOTS[2]) to obtain the20STEP panoptic map.21 22[1] Mark Weber, etc. STEP: Segmenting and Tracking Every Pixel, arXiv:2102.1185923[2] Paul Voigtlaender, etc. Multi-object tracking and segmentation. CVPR, 201924 25To run this script, you need to install opencv-python (>=4.4.0).26e.g. In Linux, run27$pip install opencv-python28 29The input directory structure should be as follows:30 31+ INPUT_SEMANTIC_MAP_ROOT_DIR32  + train33    + sequence_id34      - *.png35      ...36  + val37 38+ INPUT_INSTANCE_MAP_ROOT_DIR39  + train40    + sequence_id41      - *.png42       ...43  + val44 45+ OUTPUT_PANOPTIC_MAP_ROOT_DIR (generated)46  + train47    + sequence_id48      - *.png49       ...50  + val51 52The ground-truth panoptic map is generated and encoded as the following in PNG53format:54  R: semantic_id55  G: instance_id // 25656  B: instance % 25657 58The generated panoptic maps will be used by ../build_step_data.py to create59tfrecords for training and evaluation.60 61Example to run the scipt:62 63```bash64   python deeplab2/data/utils/create_step_panoptic_maps.py \65     --input_semantic_map_root_dir=...66     ...67```68"""69 70import os71from typing import Any, Sequence, Union72 73from absl import app74from absl import flags75from absl import logging76import cv277import numpy as np78from PIL import Image79import tensorflow as tf80 81FLAGS = flags.FLAGS82flags.DEFINE_string('input_semantic_map_root_dir', None,83                    'Path to a directory containing the semantic map.')84flags.DEFINE_string('input_instance_root_dir', None,85                    'Path to a directory containing the instance map.')86flags.DEFINE_string('output_panoptic_map_root_dir', None,87                    'Path to a directory where we write the panoptic map.')88flags.DEFINE_integer(89    'kernel_size', 15, 'Kernel size to extend instance object boundary when '90    'merging it with semantic map.')91flags.DEFINE_enum('dataset_name', 'kitti-step',92                  ['kitti-step', 'motchallenge-step'], 'Name of the dataset')93 94# The label definition below follows Cityscapes label definition in95# https://www.cityscapes-dataset.com/.96MOTCHALLENGE_MERGED_CLASSES = (0, 3, 4, 5, 6, 7, 9, 13, 14, 15, 16, 17)97NUM_VALID_CLASSES = 1998SEMANTIC_CAR = 1399SEMANTIC_PERSON = 11100SEMANTIC_VOID = 255101INSTANCE_CAR = 1102INSTANCE_PERSON = 2103INSTANCE_LABEL_DIVISOR = 1000104 105 106def encode_panoptic_map(panoptic_map: np.ndarray) -> np.ndarray:107  """Encodes the panoptic map in three channel image format."""108  # Encoding format: R: semantic | G: instance // 256 | B: instance % 256109  semantic_id = panoptic_map // INSTANCE_LABEL_DIVISOR110  instance_id = panoptic_map % INSTANCE_LABEL_DIVISOR111  return np.dstack(112      (semantic_id, instance_id // 256, instance_id % 256)).astype(np.uint8)113 114 115def load_image(image_path: str) -> np.ndarray:116  """Loads an image as numpy array."""117  with tf.io.gfile.GFile(image_path, 'rb') as f:118    return np.array(Image.open(f))119 120 121def _update_motchallege_label_map(semantic_map: np.ndarray) -> np.ndarray:122  """Updates semantic map by merging some classes."""123  # For MOTChallenge dataset, we merge some classes since they are less124  # representative:125  #--------------------------------------------------------------126  # Original index | Updated index|          Note127  #----------------+--------------+------------------------------128  #       0        |      1       |   map road to sidewalk129  #       1        |      1       |   keep sidewalk130  #       2        |      2       |   keep building131  #       3        |     255      |   not present anyway132  #       4        |     255      |   remove fence133  #       5        |     255      |   remove pole134  #       6        |     255      |   remove traffic light135  #       7        |     255      |   not present anyway136  #       8        |      8       |   keep vegetation137  #       9        |      8       |   map terrain to vegetation138  #       10       |     10       |   keep sky139  #       11       |     11       |   keep pedestrain140  #       12       |     12       |   keep rider141  #       13       |     255      |   remove car142  #       14       |     255      |   not present anyway143  #       15       |     255      |   not present anyway144  #       16       |     255      |   not present anyway145  #       17       |     255      |   remove motorcycle146  #       18       |     18       |   keep bicycle147  #       255      |     255      |   keep void148  #--------------------------------------------------------------149  for label in MOTCHALLENGE_MERGED_CLASSES:150    if label == 0:151      semantic_map[semantic_map == label] = 1152    elif label == 9:153      semantic_map[semantic_map == label] = 8154    else:155      semantic_map[semantic_map == label] = 255156  return semantic_map157 158 159def _compute_panoptic_id(semantic_id: Union[int, np.ndarray],160                         instance_id: Union[int, np.ndarray]) -> Any:161  """Gets the panoptic id by combining semantic and instance id."""162  return semantic_id * INSTANCE_LABEL_DIVISOR + instance_id163 164 165def _remap_motchallege_semantic_indices(panoptic_id: np.ndarray) -> np.ndarray:166  """Updates MOTChallenge semantic map by re-mapping label indices."""167  semantic_id = panoptic_id // INSTANCE_LABEL_DIVISOR168  instance_id = panoptic_id % INSTANCE_LABEL_DIVISOR169  # Re-mapping index170  # 1 -> 0:     sidewalk171  # 2 -> 1:     building172  # 8 -> 2:     vegetation173  # 10 -> 3:    sky174  # 11 -> 4:    pedestrain175  # 12 -> 5:    rider176  # 18 -> 6:    bicycle177  # 255 -> 255: void178  all_labels = set(range(NUM_VALID_CLASSES))179  for i, label in enumerate(180      sorted(all_labels - set(MOTCHALLENGE_MERGED_CLASSES))):181    semantic_id[semantic_id == label] = i182  return _compute_panoptic_id(semantic_id, instance_id)183 184 185def _get_semantic_maps(semantic_map_root: str, dataset_split: str,186                       sequence_id: str) -> Sequence[str]:187  """Gets files for the specified data type and dataset split."""188  search_files = os.path.join(semantic_map_root, dataset_split, sequence_id,189                              '*')190  filenames = tf.io.gfile.glob(search_files)191  return sorted(filenames)192 193 194class StepPanopticMapGenerator(object):195  """Class to generate and write panoptic map from semantic and instance map."""196 197  def __init__(self, kernel_size: int, dataset_name: str):198    self.kernel_size = kernel_size199    self.is_mots_challenge = (dataset_name == 'motchallenge-step')200 201  def _update_semantic_label_map(self, instance_map: np.ndarray,202                                 semantic_map: np.ndarray) -> np.ndarray:203    """Updates semantic map by leveraging semantic map and instance map."""204    kernel = np.ones((self.kernel_size, self.kernel_size), np.uint8)205    updated_semantic_map = semantic_map.astype(np.int32)206    if self.is_mots_challenge:207      updated_semantic_map = _update_motchallege_label_map(updated_semantic_map)208    for label in (SEMANTIC_CAR, SEMANTIC_PERSON):209      semantic_mask = (semantic_map == label)210      if label == SEMANTIC_PERSON:211        # The instance ids are encoded according to212        # https://www.vision.rwth-aachen.de/page/mots213        instance_mask = (214            instance_map // INSTANCE_LABEL_DIVISOR == INSTANCE_PERSON)215      elif label == SEMANTIC_CAR:216        instance_mask = instance_map // INSTANCE_LABEL_DIVISOR == INSTANCE_CAR217      # Run dilation on the instance map to merge it with semantic map.218      instance_mask = instance_mask.astype(np.uint8)219      dilated_instance_mask = cv2.dilate(instance_mask, kernel)220      void_boundary = np.logical_and(dilated_instance_mask - instance_mask,221                                     semantic_mask)222      updated_semantic_map[void_boundary] = SEMANTIC_VOID223    return updated_semantic_map224 225  def merge_panoptic_map(self, semantic_map: np.ndarray,226                         instance_map: np.ndarray) -> np.ndarray:227    """Merges semantic labels with given instance map."""228    # Use semantic_map as the base map.229    updated_semantic_map = self._update_semantic_label_map(230        instance_map, semantic_map)231    panoptic_map = _compute_panoptic_id(updated_semantic_map, 0)232    # Merge instance.233    mask_car = instance_map // INSTANCE_LABEL_DIVISOR == INSTANCE_CAR234    # The instance map has index from 0 but the panoptic map's instance index235    # will start from 1.236    instance_id = (instance_map[mask_car] % INSTANCE_LABEL_DIVISOR) + 1237    panoptic_map[mask_car] = _compute_panoptic_id(SEMANTIC_CAR,238                                                  instance_id.astype(np.int32))239    mask_person = instance_map // INSTANCE_LABEL_DIVISOR == INSTANCE_PERSON240    instance_id = (instance_map[mask_person] % INSTANCE_LABEL_DIVISOR) + 1241    panoptic_map[mask_person] = _compute_panoptic_id(242        SEMANTIC_PERSON, instance_id.astype(np.int32))243 244    # Remap label indices.245    if self.is_mots_challenge:246      panoptic_map = _remap_motchallege_semantic_indices(panoptic_map)247    return panoptic_map248 249  def build_panoptic_maps(self, semantic_map_root: str, instance_map_root: str,250                          dataset_split: str, sequence_id: str,251                          panoptic_map_root: str):252    """Creates panoptic maps and save them as PNG format.253 254    Args:255      semantic_map_root: Semantic map root folder.256      instance_map_root: Instance map root folder.257      dataset_split: Train/Val/Test split of the data.258      sequence_id: Sequence id of the data.259      panoptic_map_root: Panoptic map root folder where the encoded panoptic260        maps will be saved.261    """262    semantic_maps = _get_semantic_maps(semantic_map_root, dataset_split,263                                       sequence_id)264    for semantic_map_path in semantic_maps:265      image_name = os.path.basename(semantic_map_path)266      instance_map_path = os.path.join(instance_map_root, dataset_split,267                                       sequence_id, image_name)268      if not tf.io.gfile.exists(instance_map_path):269        logging.warn('Could not find instance map for %s', semantic_map_path)270        continue271      semantic_map = load_image(semantic_map_path)272      instance_map = load_image(instance_map_path)273      panoptic_map = self.merge_panoptic_map(semantic_map, instance_map)274      encoded_panoptic_map = Image.fromarray(275          encode_panoptic_map(panoptic_map)).convert('RGB')276      panoptic_map_path = os.path.join(panoptic_map_root, dataset_split,277                                       sequence_id, image_name)278      with tf.io.gfile.GFile(panoptic_map_path, 'wb') as f:279        encoded_panoptic_map.save(f, format='PNG')280 281 282def main(argv: Sequence[str]) -> None:283  if len(argv) > 1:284    raise app.UsageError('Too many command-line arguments.')285 286  panoptic_map_generator = StepPanopticMapGenerator(FLAGS.kernel_size,287                                                    FLAGS.dataset_name)288  for dataset_split in ('train', 'val', 'test'):289    sem_dir = os.path.join(FLAGS.input_semantic_map_root_dir, dataset_split)290    if not tf.io.gfile.exists(sem_dir):291      logging.info('Split %s not found.', dataset_split)292      continue293    for set_dir in tf.io.gfile.listdir(sem_dir):294      tf.io.gfile.makedirs(295          os.path.join(FLAGS.output_panoptic_map_root_dir, dataset_split,296                       set_dir))297      logging.info('Start to create panoptic map for split %s, sequence %s.',298                   dataset_split, set_dir)299      panoptic_map_generator.build_panoptic_maps(300          FLAGS.input_semantic_map_root_dir, FLAGS.input_instance_root_dir,301          dataset_split, set_dir, FLAGS.output_panoptic_map_root_dir)302 303 304if __name__ == '__main__':305  app.run(main)306