| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| """Tests for the evaluator.""" |
|
|
| import os |
| import tempfile |
| from unittest import mock |
|
|
| from absl import flags |
| import numpy as np |
| import tensorflow as tf |
|
|
| from google.protobuf import text_format |
| from deeplab2 import common |
| from deeplab2 import config_pb2 |
| from deeplab2 import trainer_pb2 |
| from deeplab2.data import data_utils |
| from deeplab2.data import dataset |
| from deeplab2.data import sample_generator |
| from deeplab2.model import deeplab |
| from deeplab2.model.loss import loss_builder |
| from deeplab2.trainer import evaluator |
| from deeplab2.trainer import runner_utils |
|
|
| |
|
|
| _CONFIG_PATH = 'deeplab2/configs/example' |
|
|
| flags.DEFINE_string( |
| 'panoptic_annotation_data', |
| 'deeplab2/data/testdata/', |
| 'Path to annotated test image.') |
|
|
| FLAGS = flags.FLAGS |
|
|
| _FILENAME_PREFIX = 'dummy_000000_000000' |
| _IMAGE_FOLDER = 'leftImg8bit/' |
|
|
|
|
| def _read_proto_file(filename, proto): |
| filename = filename |
| with tf.io.gfile.GFile(filename, 'r') as proto_file: |
| return text_format.ParseLines(proto_file, proto) |
|
|
|
|
| def _create_panoptic_deeplab_loss(dataset_info): |
| semantic_loss_options = trainer_pb2.LossOptions.SingleLossOptions( |
| name='softmax_cross_entropy') |
| center_loss_options = trainer_pb2.LossOptions.SingleLossOptions(name='mse') |
| regression_loss_options = trainer_pb2.LossOptions.SingleLossOptions( |
| name='l1') |
| loss_options = trainer_pb2.LossOptions( |
| semantic_loss=semantic_loss_options, |
| center_loss=center_loss_options, |
| regression_loss=regression_loss_options) |
|
|
| loss_layer = loss_builder.DeepLabFamilyLoss( |
| loss_options, |
| num_classes=dataset_info.num_classes, |
| ignore_label=dataset_info.ignore_label, |
| thing_class_ids=dataset_info.class_has_instances_list) |
| return loss_layer |
|
|
|
|
| def _create_max_deeplab_loss(dataset_info): |
| semantic_loss_options = trainer_pb2.LossOptions.SingleLossOptions( |
| name='softmax_cross_entropy') |
| pq_style_loss_options = trainer_pb2.LossOptions.SingleLossOptions() |
| mask_id_cross_entropy_loss_options = ( |
| trainer_pb2.LossOptions.SingleLossOptions()) |
| instance_discrimination_loss_options = ( |
| trainer_pb2.LossOptions.SingleLossOptions()) |
| loss_options = trainer_pb2.LossOptions( |
| semantic_loss=semantic_loss_options, |
| pq_style_loss=pq_style_loss_options, |
| mask_id_cross_entropy_loss=mask_id_cross_entropy_loss_options, |
| instance_discrimination_loss=instance_discrimination_loss_options) |
| loss_layer = loss_builder.DeepLabFamilyLoss( |
| loss_options, |
| num_classes=dataset_info.num_classes, |
| ignore_label=dataset_info.ignore_label, |
| thing_class_ids=dataset_info.class_has_instances_list) |
| return loss_layer |
|
|
|
|
| class RealDataEvaluatorTest(tf.test.TestCase): |
|
|
| def setUp(self): |
| super().setUp() |
| self._test_img_data_dir = os.path.join( |
| FLAGS.test_srcdir, |
| FLAGS.panoptic_annotation_data, |
| _IMAGE_FOLDER) |
| self._test_gt_data_dir = os.path.join( |
| FLAGS.test_srcdir, |
| FLAGS.panoptic_annotation_data) |
| image_path = self._test_img_data_dir + _FILENAME_PREFIX + '_leftImg8bit.png' |
| with tf.io.gfile.GFile(image_path, 'rb') as image_file: |
| rgb_image = data_utils.read_image(image_file.read()) |
| self._rgb_image = tf.convert_to_tensor(np.array(rgb_image)) |
| label_path = self._test_gt_data_dir + 'dummy_gt_for_vps.png' |
| with tf.io.gfile.GFile(label_path, 'rb') as label_file: |
| label = data_utils.read_image(label_file.read()) |
| self._label = tf.expand_dims(tf.convert_to_tensor( |
| np.dot(np.array(label), [1, 256, 256 * 256])), -1) |
|
|
| def test_evaluates_max_deeplab_model(self): |
| tf.random.set_seed(0) |
| np.random.seed(0) |
| small_instances = {'threshold': 4096, 'weight': 1.0} |
| generator = sample_generator.PanopticSampleGenerator( |
| dataset.CITYSCAPES_PANOPTIC_INFORMATION._asdict(), |
| focus_small_instances=small_instances, |
| is_training=False, |
| crop_size=[769, 769], |
| thing_id_mask_annotations=True) |
| input_sample = { |
| 'image': self._rgb_image, |
| 'image_name': 'test_image', |
| 'label': self._label, |
| 'height': 800, |
| 'width': 800 |
| } |
| sample = generator(input_sample) |
|
|
| experiment_options_textproto = """ |
| experiment_name: "evaluation_test" |
| eval_dataset_options { |
| dataset: "cityscapes_panoptic" |
| file_pattern: "EMPTY" |
| batch_size: 1 |
| crop_size: 769 |
| crop_size: 769 |
| thing_id_mask_annotations: true |
| } |
| evaluator_options { |
| continuous_eval_timeout: 43200 |
| stuff_area_limit: 2048 |
| center_score_threshold: 0.1 |
| nms_kernel: 13 |
| save_predictions: true |
| save_raw_predictions: false |
| } |
| """ |
| config = text_format.Parse(experiment_options_textproto, |
| config_pb2.ExperimentOptions()) |
|
|
| model_proto_filename = os.path.join( |
| _CONFIG_PATH, 'example_coco_max_deeplab.textproto') |
| model_config = _read_proto_file(model_proto_filename, |
| config_pb2.ExperimentOptions()) |
| config.model_options.CopyFrom(model_config.model_options) |
| config.model_options.max_deeplab.auxiliary_semantic_head.output_channels = ( |
| 19) |
| model = deeplab.DeepLab(config, dataset.CITYSCAPES_PANOPTIC_INFORMATION) |
| pool_size = (49, 49) |
| model.set_pool_size(pool_size) |
|
|
| loss_layer = _create_max_deeplab_loss( |
| dataset.CITYSCAPES_PANOPTIC_INFORMATION) |
| global_step = tf.Variable(initial_value=0, dtype=tf.int64) |
|
|
| batched_sample = {} |
| for key, value in sample.items(): |
| batched_sample[key] = tf.expand_dims(value, axis=0) |
| real_data = [batched_sample] |
|
|
| with tempfile.TemporaryDirectory() as model_dir: |
| with mock.patch.object(runner_utils, 'create_dataset'): |
| ev = evaluator.Evaluator( |
| config, model, loss_layer, global_step, model_dir) |
|
|
| state = ev.eval_begin() |
| |
| self.assertTrue(os.path.isdir(os.path.join(model_dir, 'vis'))) |
|
|
| step_outputs = ev.eval_step(iter(real_data)) |
|
|
| state = ev.eval_reduce(state, step_outputs) |
| result = ev.eval_end(state) |
|
|
| expected_metric_keys = { |
| 'losses/eval_' + common.TOTAL_LOSS, |
| 'losses/eval_' + common.SEMANTIC_LOSS, |
| 'losses/eval_' + common.PQ_STYLE_LOSS_CLASS_TERM, |
| 'losses/eval_' + common.PQ_STYLE_LOSS_MASK_DICE_TERM, |
| 'losses/eval_' + common.MASK_ID_CROSS_ENTROPY_LOSS, |
| 'losses/eval_' + common.INSTANCE_DISCRIMINATION_LOSS, |
| 'evaluation/iou/IoU', |
| 'evaluation/pq/PQ', |
| 'evaluation/pq/SQ', |
| 'evaluation/pq/RQ', |
| 'evaluation/pq/TP', |
| 'evaluation/pq/FN', |
| 'evaluation/pq/FP', |
| } |
| self.assertCountEqual(result.keys(), expected_metric_keys) |
| self.assertSequenceEqual(result['losses/eval_total_loss'].shape, ()) |
|
|
|
|
| class EvaluatorTest(tf.test.TestCase): |
|
|
| def test_evaluates_panoptic_deeplab_model(self): |
| experiment_options_textproto = """ |
| experiment_name: "evaluation_test" |
| eval_dataset_options { |
| dataset: "cityscapes_panoptic" |
| file_pattern: "EMPTY" |
| batch_size: 1 |
| crop_size: 1025 |
| crop_size: 2049 |
| # Skip resizing. |
| min_resize_value: 0 |
| max_resize_value: 0 |
| } |
| evaluator_options { |
| continuous_eval_timeout: 43200 |
| stuff_area_limit: 2048 |
| center_score_threshold: 0.1 |
| nms_kernel: 13 |
| save_predictions: true |
| save_raw_predictions: false |
| } |
| """ |
| config = text_format.Parse(experiment_options_textproto, |
| config_pb2.ExperimentOptions()) |
|
|
| model_proto_filename = os.path.join( |
| _CONFIG_PATH, 'example_cityscapes_panoptic_deeplab.textproto') |
| model_config = _read_proto_file(model_proto_filename, |
| config_pb2.ExperimentOptions()) |
| config.model_options.CopyFrom(model_config.model_options) |
| model = deeplab.DeepLab(config, dataset.CITYSCAPES_PANOPTIC_INFORMATION) |
| pool_size = (33, 65) |
| model.set_pool_size(pool_size) |
|
|
| loss_layer = _create_panoptic_deeplab_loss( |
| dataset.CITYSCAPES_PANOPTIC_INFORMATION) |
| global_step = tf.Variable(initial_value=0, dtype=tf.int64) |
|
|
| fake_datum = { |
| common.IMAGE: |
| tf.zeros([1, 1025, 2049, 3]), |
| common.RESIZED_IMAGE: |
| tf.zeros([1, 1025, 2049, 3]), |
| common.GT_SIZE_RAW: |
| tf.constant([[1025, 2049]], dtype=tf.int32), |
| common.GT_SEMANTIC_KEY: |
| tf.zeros([1, 1025, 2049], dtype=tf.int32), |
| common.GT_SEMANTIC_RAW: |
| tf.zeros([1, 1025, 2049], dtype=tf.int32), |
| common.GT_PANOPTIC_RAW: |
| tf.zeros([1, 1025, 2049], dtype=tf.int32), |
| common.GT_IS_CROWD_RAW: |
| tf.zeros([1, 1025, 2049], dtype=tf.uint8), |
| common.GT_INSTANCE_CENTER_KEY: |
| tf.zeros([1, 1025, 2049], dtype=tf.float32), |
| common.GT_INSTANCE_REGRESSION_KEY: |
| tf.zeros([1, 1025, 2049, 2], dtype=tf.float32), |
| common.IMAGE_NAME: |
| 'fake', |
| common.SEMANTIC_LOSS_WEIGHT_KEY: |
| tf.zeros([1, 1025, 2049], dtype=tf.float32), |
| common.CENTER_LOSS_WEIGHT_KEY: |
| tf.zeros([1, 1025, 2049], dtype=tf.float32), |
| common.REGRESSION_LOSS_WEIGHT_KEY: |
| tf.zeros([1, 1025, 2049], dtype=tf.float32), |
| } |
| fake_data = [fake_datum] |
|
|
| with tempfile.TemporaryDirectory() as model_dir: |
| with mock.patch.object(runner_utils, 'create_dataset'): |
| ev = evaluator.Evaluator( |
| config, model, loss_layer, global_step, model_dir) |
|
|
| state = ev.eval_begin() |
| |
| self.assertTrue(os.path.isdir(os.path.join(model_dir, 'vis'))) |
|
|
| step_outputs = ev.eval_step(iter(fake_data)) |
|
|
| state = ev.eval_reduce(state, step_outputs) |
| result = ev.eval_end(state) |
|
|
| expected_metric_keys = { |
| 'losses/eval_total_loss', |
| 'losses/eval_semantic_loss', |
| 'losses/eval_center_loss', |
| 'losses/eval_regression_loss', |
| 'evaluation/iou/IoU', |
| 'evaluation/pq/PQ', |
| 'evaluation/pq/SQ', |
| 'evaluation/pq/RQ', |
| 'evaluation/pq/TP', |
| 'evaluation/pq/FN', |
| 'evaluation/pq/FP', |
| 'evaluation/ap/AP_Mask', |
| } |
| self.assertCountEqual(result.keys(), expected_metric_keys) |
|
|
| self.assertSequenceEqual(result['losses/eval_total_loss'].shape, ()) |
| self.assertEqual(result['losses/eval_total_loss'].numpy(), 0.0) |
|
|
|
|
| if __name__ == '__main__': |
| tf.test.main() |
|
|