Spaces:
Runtime error
Runtime error
| # coding=utf-8 | |
| # Copyright 2021 The Deeplab2 Authors. | |
| # | |
| # 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. | |
| """Converts Cityscapes data to sharded TFRecord file format with Example protos. | |
| Please check ../g3doc/setup/cityscapes.md for instructions. | |
| """ | |
| import collections | |
| import json | |
| import math | |
| import os | |
| from absl import app | |
| from absl import flags | |
| from absl import logging | |
| import numpy as np | |
| import tensorflow as tf | |
| from deeplab2.data import data_utils | |
| from deeplab2.data import dataset | |
| FLAGS = flags.FLAGS | |
| flags.DEFINE_string('cityscapes_root', None, 'Cityscapes dataset root folder.') | |
| flags.DEFINE_string('output_dir', None, | |
| 'Path to save converted TFRecord of TensorFlow examples.') | |
| flags.DEFINE_boolean('create_panoptic_data', True, | |
| 'Whether to create semantic or panoptic dataset.') | |
| flags.DEFINE_boolean('treat_crowd_as_ignore', True, | |
| 'Whether to apply ignore labels to crowd pixels in ' | |
| 'panoptic label.') | |
| _NUM_SHARDS = 10 | |
| _SPLITS_TO_SIZES = dataset.CITYSCAPES_INFORMATION.splits_to_sizes | |
| _IGNORE_LABEL = dataset.CITYSCAPES_PANOPTIC_INFORMATION.ignore_label | |
| _CLASS_HAS_INSTANCE_LIST = dataset.CITYSCAPES_PANOPTIC_INFORMATION.class_has_instances_list | |
| _PANOPTIC_LABEL_DIVISOR = dataset.CITYSCAPES_PANOPTIC_INFORMATION.panoptic_label_divisor | |
| # A map from data type to folder name that saves the data. | |
| _FOLDERS_MAP = { | |
| 'image': 'leftImg8bit', | |
| 'label': 'gtFine', | |
| } | |
| # A map from data type to filename postfix. | |
| _POSTFIX_MAP = { | |
| 'image': '_leftImg8bit', | |
| 'label': '_gtFine_labelTrainIds', | |
| } | |
| # A map from data type to data format. | |
| _DATA_FORMAT_MAP = { | |
| 'image': 'png', | |
| 'label': 'png', | |
| } | |
| _PANOPTIC_LABEL_FORMAT = 'raw' | |
| def _get_images(cityscapes_root, dataset_split): | |
| """Gets files for the specified data type and dataset split. | |
| Args: | |
| cityscapes_root: String, path to Cityscapes dataset root folder. | |
| dataset_split: String, dataset split ('train', 'val', 'test') | |
| Returns: | |
| A list of sorted file names or None when getting label for | |
| test set. | |
| """ | |
| pattern = '*%s.%s' % (_POSTFIX_MAP['image'], _DATA_FORMAT_MAP['image']) | |
| search_files = os.path.join( | |
| cityscapes_root, _FOLDERS_MAP['image'], dataset_split, '*', pattern) | |
| filenames = tf.io.gfile.glob(search_files) | |
| return sorted(filenames) | |
| def _split_image_path(image_path): | |
| """Helper method to extract split paths from input image path. | |
| Args: | |
| image_path: String, path to the image file. | |
| Returns: | |
| A tuple of (cityscape root, dataset split, cityname and shared filename | |
| prefix). | |
| """ | |
| image_path = os.path.normpath(image_path) | |
| path_list = image_path.split(os.sep) | |
| image_folder, dataset_split, city_name, file_name = path_list[-4:] | |
| if image_folder != _FOLDERS_MAP['image']: | |
| raise ValueError('Expects image path %s containing image folder.' | |
| % image_path) | |
| pattern = '%s.%s' % (_POSTFIX_MAP['image'], _DATA_FORMAT_MAP['image']) | |
| if not file_name.endswith(pattern): | |
| raise ValueError('Image file name %s should end with %s' % | |
| (file_name, pattern)) | |
| file_prefix = file_name[:-len(pattern)] | |
| return os.sep.join(path_list[:-4]), dataset_split, city_name, file_prefix | |
| def _get_semantic_annotation(image_path): | |
| cityscapes_root, dataset_split, city_name, file_prefix = _split_image_path( | |
| image_path) | |
| semantic_annotation = '%s%s.%s' % (file_prefix, _POSTFIX_MAP['label'], | |
| _DATA_FORMAT_MAP['label']) | |
| return os.path.join(cityscapes_root, _FOLDERS_MAP['label'], dataset_split, | |
| city_name, semantic_annotation) | |
| def _get_panoptic_annotation(cityscapes_root, dataset_split, | |
| annotation_file_name): | |
| panoptic_folder = 'cityscapes_panoptic_%s_trainId' % dataset_split | |
| return os.path.join(cityscapes_root, _FOLDERS_MAP['label'], panoptic_folder, | |
| annotation_file_name) | |
| def _read_segments(cityscapes_root, dataset_split): | |
| """Reads segments information from json file. | |
| Args: | |
| cityscapes_root: String, path to Cityscapes dataset root folder. | |
| dataset_split: String, dataset split. | |
| Returns: | |
| segments_dict: A dictionary that maps `image_id` (common file prefix) to | |
| a tuple of (panoptic annotation file name, segments). Please refer to | |
| _generate_panoptic_label() method on the detail structure of `segments`. | |
| """ | |
| json_filename = os.path.join( | |
| cityscapes_root, _FOLDERS_MAP['label'], | |
| 'cityscapes_panoptic_%s_trainId.json' % dataset_split) | |
| with tf.io.gfile.GFile(json_filename) as f: | |
| panoptic_dataset = json.load(f) | |
| segments_dict = {} | |
| for annotation in panoptic_dataset['annotations']: | |
| image_id = annotation['image_id'] | |
| if image_id in segments_dict: | |
| raise ValueError('Image ID %s already exists' % image_id) | |
| annotation_file_name = annotation['file_name'] | |
| segments = annotation['segments_info'] | |
| segments_dict[image_id] = (annotation_file_name, segments) | |
| return segments_dict | |
| def _generate_panoptic_label(panoptic_annotation_file, segments): | |
| """Creates panoptic label map from annotations. | |
| Args: | |
| panoptic_annotation_file: String, path to panoptic annotation (populated | |
| with `trainId`). | |
| segments: A list of dictionaries containing information of every segment. | |
| Read from panoptic_${DATASET_SPLIT}_trainId.json. This method consumes | |
| the following fields in each dictionary: | |
| - id: panoptic id | |
| - category_id: semantic class id | |
| - area: pixel area of this segment | |
| - iscrowd: if this segment is crowd region | |
| Returns: | |
| A 2D numpy int32 array with the same height / width with panoptic | |
| annotation. Each pixel value represents its panoptic ID. Please refer to | |
| ../g3doc/setup/cityscapes.md for more details about how panoptic ID is | |
| assigned. | |
| """ | |
| with tf.io.gfile.GFile(panoptic_annotation_file, 'rb') as f: | |
| panoptic_label = data_utils.read_image(f.read()) | |
| if panoptic_label.mode != 'RGB': | |
| raise ValueError('Expect RGB image for panoptic label, gets %s' % | |
| panoptic_label.mode) | |
| panoptic_label = np.array(panoptic_label, dtype=np.int32) | |
| # Cityscapes panoptic map is created by: | |
| # color = [segmentId % 256, segmentId // 256, segmentId // 256 // 256] | |
| panoptic_label = np.dot(panoptic_label, [1, 256, 256 * 256]) | |
| semantic_label = np.ones_like(panoptic_label) * _IGNORE_LABEL | |
| instance_label = np.zeros_like(panoptic_label) | |
| # Running count of instances per semantic category. | |
| instance_count = collections.defaultdict(int) | |
| for segment in segments: | |
| selected_pixels = panoptic_label == segment['id'] | |
| pixel_area = np.sum(selected_pixels) | |
| if pixel_area != segment['area']: | |
| raise ValueError('Expect %d pixels for segment %s, gets %d.' % | |
| (segment['area'], segment, pixel_area)) | |
| category_id = segment['category_id'] | |
| semantic_label[selected_pixels] = category_id | |
| if category_id in _CLASS_HAS_INSTANCE_LIST: | |
| if segment['iscrowd']: | |
| # Cityscapes crowd pixels will have instance ID of 0. | |
| if FLAGS.treat_crowd_as_ignore: | |
| semantic_label[selected_pixels] = _IGNORE_LABEL | |
| continue | |
| # Non-crowd pixels will have instance ID starting from 1. | |
| instance_count[category_id] += 1 | |
| if instance_count[category_id] >= _PANOPTIC_LABEL_DIVISOR: | |
| raise ValueError('Too many instances for category %d in this image.' % | |
| category_id) | |
| instance_label[selected_pixels] = instance_count[category_id] | |
| elif segment['iscrowd']: | |
| raise ValueError('Stuff class should not have `iscrowd` label.') | |
| panoptic_label = semantic_label * _PANOPTIC_LABEL_DIVISOR + instance_label | |
| return panoptic_label.astype(np.int32) | |
| def _convert_split_name(dataset_split): | |
| return dataset_split + '_fine' | |
| def _create_semantic_label(image_path): | |
| """Creates labels for semantic segmentation.""" | |
| with tf.io.gfile.GFile(_get_semantic_annotation(image_path), 'rb') as f: | |
| label_data = f.read() | |
| return label_data, _DATA_FORMAT_MAP['label'] | |
| def _create_panoptic_label(image_path, segments_dict): | |
| """Creates labels for panoptic segmentation.""" | |
| cityscapes_root, dataset_split, _, file_prefix = _split_image_path(image_path) | |
| annotation_file_name, segments = segments_dict[file_prefix] | |
| panoptic_annotation_file = _get_panoptic_annotation(cityscapes_root, | |
| dataset_split, | |
| annotation_file_name) | |
| panoptic_label = _generate_panoptic_label(panoptic_annotation_file, segments) | |
| return panoptic_label.tostring(), _PANOPTIC_LABEL_FORMAT | |
| def _convert_dataset(cityscapes_root, dataset_split, output_dir): | |
| """Converts the specified dataset split to TFRecord format. | |
| Args: | |
| cityscapes_root: String, path to Cityscapes dataset root folder. | |
| dataset_split: String, the dataset split (one of `train`, `val` and `test`). | |
| output_dir: String, directory to write output TFRecords to. | |
| Raises: | |
| RuntimeError: If loaded image and label have different shape, or if the | |
| image file with specified postfix could not be found. | |
| """ | |
| image_files = _get_images(cityscapes_root, dataset_split) | |
| num_images = len(image_files) | |
| expected_dataset_size = _SPLITS_TO_SIZES[_convert_split_name(dataset_split)] | |
| if num_images != expected_dataset_size: | |
| raise ValueError('Expects %d images, gets %d' % | |
| (expected_dataset_size, num_images)) | |
| segments_dict = None | |
| if FLAGS.create_panoptic_data: | |
| segments_dict = _read_segments(FLAGS.cityscapes_root, dataset_split) | |
| num_per_shard = int(math.ceil(len(image_files) / _NUM_SHARDS)) | |
| for shard_id in range(_NUM_SHARDS): | |
| shard_filename = '%s-%05d-of-%05d.tfrecord' % ( | |
| dataset_split, shard_id, _NUM_SHARDS) | |
| output_filename = os.path.join(output_dir, shard_filename) | |
| with tf.io.TFRecordWriter(output_filename) as tfrecord_writer: | |
| start_idx = shard_id * num_per_shard | |
| end_idx = min((shard_id + 1) * num_per_shard, num_images) | |
| for i in range(start_idx, end_idx): | |
| # Read the image. | |
| with tf.io.gfile.GFile(image_files[i], 'rb') as f: | |
| image_data = f.read() | |
| if dataset_split == 'test': | |
| label_data, label_format = None, None | |
| elif FLAGS.create_panoptic_data: | |
| label_data, label_format = _create_panoptic_label( | |
| image_files[i], segments_dict) | |
| else: | |
| label_data, label_format = _create_semantic_label(image_files[i]) | |
| # Convert to tf example. | |
| _, _, _, file_prefix = _split_image_path(image_files[i]) | |
| example = data_utils.create_tfexample(image_data, | |
| _DATA_FORMAT_MAP['image'], | |
| file_prefix, label_data, | |
| label_format) | |
| tfrecord_writer.write(example.SerializeToString()) | |
| def main(unused_argv): | |
| tf.io.gfile.makedirs(FLAGS.output_dir) | |
| for dataset_split in ('train', 'val', 'test'): | |
| logging.info('Starts to processing dataset split %s.', dataset_split) | |
| _convert_dataset(FLAGS.cityscapes_root, dataset_split, FLAGS.output_dir) | |
| if __name__ == '__main__': | |
| flags.mark_flags_as_required(['cityscapes_root', 'output_dir']) | |
| app.run(main) | |