Spaces:
Runtime error
Runtime error
| # Lint as: python2, python3 | |
| # Copyright 2019 The TensorFlow Authors All Rights Reserved. | |
| # | |
| # 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. | |
| # ============================================================================== | |
| """Code to compute segmentation in a "streaming" pattern in Tensorflow. | |
| These aggregate the metric over examples of the evaluation set. Each example is | |
| assumed to be fed in in a stream, and the metric implementation accumulates | |
| across them. | |
| """ | |
| from __future__ import absolute_import | |
| from __future__ import division | |
| from __future__ import print_function | |
| import tensorflow as tf | |
| from deeplab.evaluation import panoptic_quality | |
| from deeplab.evaluation import parsing_covering | |
| _EPSILON = 1e-10 | |
| def _realdiv_maybe_zero(x, y): | |
| """Support tf.realdiv(x, y) where y may contain zeros.""" | |
| return tf.where(tf.less(y, _EPSILON), tf.zeros_like(x), tf.realdiv(x, y)) | |
| def _running_total(value, shape, name=None): | |
| """Maintains a running total of tensor `value` between calls.""" | |
| with tf.variable_scope(name, 'running_total', [value]): | |
| total_var = tf.get_variable( | |
| 'total', | |
| shape, | |
| value.dtype, | |
| initializer=tf.zeros_initializer(), | |
| trainable=False, | |
| collections=[ | |
| tf.GraphKeys.LOCAL_VARIABLES, tf.GraphKeys.METRIC_VARIABLES | |
| ]) | |
| updated_total = tf.assign_add(total_var, value, use_locking=True) | |
| return total_var, updated_total | |
| def _panoptic_quality_helper( | |
| groundtruth_category_array, groundtruth_instance_array, | |
| predicted_category_array, predicted_instance_array, num_classes, | |
| max_instances_per_category, ignored_label, offset): | |
| """Helper function to compute panoptic quality.""" | |
| pq = panoptic_quality.PanopticQuality(num_classes, ignored_label, | |
| max_instances_per_category, offset) | |
| pq.compare_and_accumulate(groundtruth_category_array, | |
| groundtruth_instance_array, | |
| predicted_category_array, predicted_instance_array) | |
| return pq.iou_per_class, pq.tp_per_class, pq.fn_per_class, pq.fp_per_class | |
| def streaming_panoptic_quality(groundtruth_categories, | |
| groundtruth_instances, | |
| predicted_categories, | |
| predicted_instances, | |
| num_classes, | |
| max_instances_per_category, | |
| ignored_label, | |
| offset, | |
| name=None): | |
| """Aggregates the panoptic metric across calls with different input tensors. | |
| See tf.metrics.* functions for comparable functionality and usage. | |
| Args: | |
| groundtruth_categories: A 2D uint16 tensor of groundtruth category labels. | |
| groundtruth_instances: A 2D uint16 tensor of groundtruth instance labels. | |
| predicted_categories: A 2D uint16 tensor of predicted category labels. | |
| predicted_instances: A 2D uint16 tensor of predicted instance labels. | |
| num_classes: Number of classes in the dataset as an integer. | |
| max_instances_per_category: The maximum number of instances for each class | |
| as an integer or integer tensor. | |
| ignored_label: The class id to be ignored in evaluation as an integer or | |
| integer tensor. | |
| offset: The maximum number of unique labels as an integer or integer tensor. | |
| name: An optional variable_scope name. | |
| Returns: | |
| qualities: A tensor of shape `[6, num_classes]`, where (1) panoptic quality, | |
| (2) segmentation quality, (3) recognition quality, (4) total_tp, | |
| (5) total_fn and (6) total_fp are saved in the respective rows. | |
| update_ops: List of operations that update the running overall panoptic | |
| quality. | |
| Raises: | |
| RuntimeError: If eager execution is enabled. | |
| """ | |
| if tf.executing_eagerly(): | |
| raise RuntimeError('Cannot aggregate when eager execution is enabled.') | |
| input_args = [ | |
| tf.convert_to_tensor(groundtruth_categories, tf.uint16), | |
| tf.convert_to_tensor(groundtruth_instances, tf.uint16), | |
| tf.convert_to_tensor(predicted_categories, tf.uint16), | |
| tf.convert_to_tensor(predicted_instances, tf.uint16), | |
| tf.convert_to_tensor(num_classes, tf.int32), | |
| tf.convert_to_tensor(max_instances_per_category, tf.int32), | |
| tf.convert_to_tensor(ignored_label, tf.int32), | |
| tf.convert_to_tensor(offset, tf.int32), | |
| ] | |
| return_types = [ | |
| tf.float64, | |
| tf.float64, | |
| tf.float64, | |
| tf.float64, | |
| ] | |
| with tf.variable_scope(name, 'streaming_panoptic_quality', input_args): | |
| panoptic_results = tf.py_func( | |
| _panoptic_quality_helper, input_args, return_types, stateful=False) | |
| iou, tp, fn, fp = tuple(panoptic_results) | |
| total_iou, updated_iou = _running_total( | |
| iou, [num_classes], name='iou_total') | |
| total_tp, updated_tp = _running_total(tp, [num_classes], name='tp_total') | |
| total_fn, updated_fn = _running_total(fn, [num_classes], name='fn_total') | |
| total_fp, updated_fp = _running_total(fp, [num_classes], name='fp_total') | |
| update_ops = [updated_iou, updated_tp, updated_fn, updated_fp] | |
| sq = _realdiv_maybe_zero(total_iou, total_tp) | |
| rq = _realdiv_maybe_zero(total_tp, | |
| total_tp + 0.5 * total_fn + 0.5 * total_fp) | |
| pq = tf.multiply(sq, rq) | |
| qualities = tf.stack([pq, sq, rq, total_tp, total_fn, total_fp], axis=0) | |
| return qualities, update_ops | |
| def _parsing_covering_helper( | |
| groundtruth_category_array, groundtruth_instance_array, | |
| predicted_category_array, predicted_instance_array, num_classes, | |
| max_instances_per_category, ignored_label, offset, normalize_by_image_size): | |
| """Helper function to compute parsing covering.""" | |
| pc = parsing_covering.ParsingCovering(num_classes, ignored_label, | |
| max_instances_per_category, offset, | |
| normalize_by_image_size) | |
| pc.compare_and_accumulate(groundtruth_category_array, | |
| groundtruth_instance_array, | |
| predicted_category_array, predicted_instance_array) | |
| return pc.weighted_iou_per_class, pc.gt_area_per_class | |
| def streaming_parsing_covering(groundtruth_categories, | |
| groundtruth_instances, | |
| predicted_categories, | |
| predicted_instances, | |
| num_classes, | |
| max_instances_per_category, | |
| ignored_label, | |
| offset, | |
| normalize_by_image_size=True, | |
| name=None): | |
| """Aggregates the covering across calls with different input tensors. | |
| See tf.metrics.* functions for comparable functionality and usage. | |
| Args: | |
| groundtruth_categories: A 2D uint16 tensor of groundtruth category labels. | |
| groundtruth_instances: A 2D uint16 tensor of groundtruth instance labels. | |
| predicted_categories: A 2D uint16 tensor of predicted category labels. | |
| predicted_instances: A 2D uint16 tensor of predicted instance labels. | |
| num_classes: Number of classes in the dataset as an integer. | |
| max_instances_per_category: The maximum number of instances for each class | |
| as an integer or integer tensor. | |
| ignored_label: The class id to be ignored in evaluation as an integer or | |
| integer tensor. | |
| offset: The maximum number of unique labels as an integer or integer tensor. | |
| normalize_by_image_size: Whether to normalize groundtruth region areas by | |
| image size. If True, groundtruth instance areas and weighted IoUs will be | |
| divided by the size of the corresponding image before accumulated across | |
| the dataset. | |
| name: An optional variable_scope name. | |
| Returns: | |
| coverings: A tensor of shape `[3, num_classes]`, where (1) per class | |
| coverings, (2) per class sum of weighted IoUs, and (3) per class sum of | |
| groundtruth region areas are saved in the perspective rows. | |
| update_ops: List of operations that update the running overall parsing | |
| covering. | |
| Raises: | |
| RuntimeError: If eager execution is enabled. | |
| """ | |
| if tf.executing_eagerly(): | |
| raise RuntimeError('Cannot aggregate when eager execution is enabled.') | |
| input_args = [ | |
| tf.convert_to_tensor(groundtruth_categories, tf.uint16), | |
| tf.convert_to_tensor(groundtruth_instances, tf.uint16), | |
| tf.convert_to_tensor(predicted_categories, tf.uint16), | |
| tf.convert_to_tensor(predicted_instances, tf.uint16), | |
| tf.convert_to_tensor(num_classes, tf.int32), | |
| tf.convert_to_tensor(max_instances_per_category, tf.int32), | |
| tf.convert_to_tensor(ignored_label, tf.int32), | |
| tf.convert_to_tensor(offset, tf.int32), | |
| tf.convert_to_tensor(normalize_by_image_size, tf.bool), | |
| ] | |
| return_types = [ | |
| tf.float64, | |
| tf.float64, | |
| ] | |
| with tf.variable_scope(name, 'streaming_parsing_covering', input_args): | |
| covering_results = tf.py_func( | |
| _parsing_covering_helper, input_args, return_types, stateful=False) | |
| weighted_iou_per_class, gt_area_per_class = tuple(covering_results) | |
| total_weighted_iou_per_class, updated_weighted_iou_per_class = ( | |
| _running_total( | |
| weighted_iou_per_class, [num_classes], | |
| name='weighted_iou_per_class_total')) | |
| total_gt_area_per_class, updated_gt_area_per_class = _running_total( | |
| gt_area_per_class, [num_classes], name='gt_area_per_class_total') | |
| covering_per_class = _realdiv_maybe_zero(total_weighted_iou_per_class, | |
| total_gt_area_per_class) | |
| coverings = tf.stack([ | |
| covering_per_class, | |
| total_weighted_iou_per_class, | |
| total_gt_area_per_class, | |
| ], | |
| axis=0) | |
| update_ops = [updated_weighted_iou_per_class, updated_gt_area_per_class] | |
| return coverings, update_ops | |