From 60e41776dd4a88236f9e087e786b734d251871a3 Mon Sep 17 00:00:00 2001 From: Jirka Borovec <6035284+Borda@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:57:09 +0200 Subject: [PATCH] imports: deprecate from pkg root [2/n] Detection (#1694) * imports: detection * functionals * imports * fix * all --- src/torchmetrics/__init__.py | 3 +- src/torchmetrics/detection/__init__.py | 6 +- src/torchmetrics/detection/_deprecated.py | 60 +++++ .../detection/modified_panoptic_quality.py | 212 ------------------ ...optic_quality.py => panoptic_qualities.py} | 181 ++++++++++++++- src/torchmetrics/functional/__init__.py | 5 +- .../functional/detection/__init__.py | 5 +- .../functional/detection/_deprecated.py | 64 ++++++ .../detection/modified_panoptic_quality.py | 101 --------- ...optic_quality.py => panoptic_qualities.py} | 78 ++++++- .../deprecations/root_class_imports.py | 6 + .../test_modified_panoptic_quality.py | 4 +- .../detection/test_panoptic_quality.py | 4 +- 13 files changed, 402 insertions(+), 327 deletions(-) create mode 100644 src/torchmetrics/detection/_deprecated.py delete mode 100644 src/torchmetrics/detection/modified_panoptic_quality.py rename src/torchmetrics/detection/{panoptic_quality.py => panoptic_qualities.py} (53%) create mode 100644 src/torchmetrics/functional/detection/_deprecated.py delete mode 100644 src/torchmetrics/functional/detection/modified_panoptic_quality.py rename src/torchmetrics/functional/detection/{panoptic_quality.py => panoptic_qualities.py} (57%) diff --git a/src/torchmetrics/__init__.py b/src/torchmetrics/__init__.py index 1aedeb14c07..c954718693e 100644 --- a/src/torchmetrics/__init__.py +++ b/src/torchmetrics/__init__.py @@ -45,7 +45,8 @@ StatScores, ) from torchmetrics.collections import MetricCollection # noqa: E402 -from torchmetrics.detection import ModifiedPanopticQuality, PanopticQuality # noqa: E402 +from torchmetrics.detection._deprecated import _ModifiedPanopticQuality as ModifiedPanopticQuality # noqa: E402 +from torchmetrics.detection._deprecated import _PanopticQuality as PanopticQuality # noqa: E402 from torchmetrics.image import ( # noqa: E402 ErrorRelativeGlobalDimensionlessSynthesis, MultiScaleStructuralSimilarityIndexMeasure, diff --git a/src/torchmetrics/detection/__init__.py b/src/torchmetrics/detection/__init__.py index 68dd3723400..790aa9ed3ed 100644 --- a/src/torchmetrics/detection/__init__.py +++ b/src/torchmetrics/detection/__init__.py @@ -11,10 +11,12 @@ # 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. +from torchmetrics.detection.panoptic_qualities import ModifiedPanopticQuality, PanopticQuality from torchmetrics.utilities.imports import _TORCHVISION_GREATER_EQUAL_0_8 +__all__ = ["ModifiedPanopticQuality", "PanopticQuality"] + if _TORCHVISION_GREATER_EQUAL_0_8: from torchmetrics.detection.mean_ap import MeanAveragePrecision # noqa: F401 -from torchmetrics.detection.modified_panoptic_quality import ModifiedPanopticQuality # noqa: F401 -from torchmetrics.detection.panoptic_quality import PanopticQuality # noqa: F401 + __all__.append("MeanAveragePrecision") diff --git a/src/torchmetrics/detection/_deprecated.py b/src/torchmetrics/detection/_deprecated.py new file mode 100644 index 00000000000..37ebd8f6228 --- /dev/null +++ b/src/torchmetrics/detection/_deprecated.py @@ -0,0 +1,60 @@ +from typing import Any, Collection + +from torchmetrics.detection import ModifiedPanopticQuality, PanopticQuality +from torchmetrics.utilities.prints import _deprecated_root_import_class + + +class _ModifiedPanopticQuality(ModifiedPanopticQuality): + """Wrapper for deprecated import. + + >>> from torch import tensor + >>> preds = tensor([[[0, 0], [0, 1], [6, 0], [7, 0], [0, 2], [1, 0]]]) + >>> target = tensor([[[0, 1], [0, 0], [6, 0], [7, 0], [6, 0], [255, 0]]]) + >>> pq_modified = _ModifiedPanopticQuality(things = {0, 1}, stuffs = {6, 7}) + >>> pq_modified(preds, target) + tensor(0.7667, dtype=torch.float64) + """ + + def __init__( + self, + things: Collection[int], + stuffs: Collection[int], + allow_unknown_preds_category: bool = False, + **kwargs: Any, + ) -> None: + _deprecated_root_import_class("ModifiedPanopticQuality", "detection") + return super().__init__( + things=things, stuffs=stuffs, allow_unknown_preds_category=allow_unknown_preds_category, **kwargs + ) + + +class _PanopticQuality(PanopticQuality): + """Wrapper for deprecated import. + + >>> from torch import tensor + >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [7, 0], [6, 0], [1, 0]], + ... [[0, 0], [7, 0], [7, 0], [7, 0]]]]) + >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [1, 0]], + ... [[0, 1], [7, 0], [1, 0], [1, 0]], + ... [[0, 1], [7, 0], [7, 0], [7, 0]]]]) + >>> panoptic_quality = _PanopticQuality(things = {0, 1}, stuffs = {6, 7}) + >>> panoptic_quality(preds, target) + tensor(0.5463, dtype=torch.float64) + """ + + def __init__( + self, + things: Collection[int], + stuffs: Collection[int], + allow_unknown_preds_category: bool = False, + **kwargs: Any, + ) -> None: + _deprecated_root_import_class("PanopticQuality", "detection") + return super().__init__( + things=things, stuffs=stuffs, allow_unknown_preds_category=allow_unknown_preds_category, **kwargs + ) diff --git a/src/torchmetrics/detection/modified_panoptic_quality.py b/src/torchmetrics/detection/modified_panoptic_quality.py deleted file mode 100644 index 18c761d2aa8..00000000000 --- a/src/torchmetrics/detection/modified_panoptic_quality.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright The PyTorch Lightning team. -# -# 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. -from typing import Any, Collection, Optional, Sequence, Union - -import torch -from torch import Tensor - -from torchmetrics.functional.detection._panoptic_quality_common import ( - _get_category_id_to_continuous_id, - _get_void_color, - _panoptic_quality_compute, - _panoptic_quality_update, - _parse_categories, - _prepocess_inputs, - _validate_inputs, -) -from torchmetrics.metric import Metric -from torchmetrics.utilities.imports import _MATPLOTLIB_AVAILABLE -from torchmetrics.utilities.plot import _AX_TYPE, _PLOT_OUT_TYPE - -if not _MATPLOTLIB_AVAILABLE: - __doctest_skip__ = ["ModifiedPanopticQuality.plot"] - - -class ModifiedPanopticQuality(Metric): - r"""Compute `Modified Panoptic Quality`_ for panoptic segmentations. - - The metric was introduced in `Seamless Scene Segmentation paper`_, and is an adaptation of the original - `Panoptic Quality`_ where the metric for a stuff class is computed as - - .. math:: - PQ^{\dagger}_c = \frac{IOU_c}{|S_c|} - - where IOU_c is the sum of the intersection over union of all matching segments for a given class, and \|S_c| is - the overall number of segments in the ground truth for that class. - - .. note: - Points in the target tensor that do not map to a known category ID are automatically ignored in the metric - computation. - - Args: - things: - Set of ``category_id`` for countable things. - stuffs: - Set of ``category_id`` for uncountable stuffs. - allow_unknown_preds_category: - Boolean flag to specify if unknown categories in the predictions are to be ignored in the metric - computation or raise an exception when found. - - - Raises: - ValueError: - If ``things``, ``stuffs`` have at least one common ``category_id``. - TypeError: - If ``things``, ``stuffs`` contain non-integer ``category_id``. - - Example: - >>> from torch import tensor - >>> from torchmetrics.detection import ModifiedPanopticQuality - >>> preds = tensor([[[0, 0], [0, 1], [6, 0], [7, 0], [0, 2], [1, 0]]]) - >>> target = tensor([[[0, 1], [0, 0], [6, 0], [7, 0], [6, 0], [255, 0]]]) - >>> pq_modified = ModifiedPanopticQuality(things = {0, 1}, stuffs = {6, 7}) - >>> pq_modified(preds, target) - tensor(0.7667, dtype=torch.float64) - """ - is_differentiable: bool = False - higher_is_better: bool = True - full_state_update: bool = False - plot_lower_bound: float = 0.0 - plot_upper_bound: float = 1.0 - - iou_sum: Tensor - true_positives: Tensor - false_positives: Tensor - false_negatives: Tensor - - def __init__( - self, - things: Collection[int], - stuffs: Collection[int], - allow_unknown_preds_category: bool = False, - **kwargs: Any, - ) -> None: - super().__init__(**kwargs) - - things, stuffs = _parse_categories(things, stuffs) - self.things = things - self.stuffs = stuffs - self.void_color = _get_void_color(things, stuffs) - self.cat_id_to_continuous_id = _get_category_id_to_continuous_id(things, stuffs) - self.allow_unknown_preds_category = allow_unknown_preds_category - - # per category intermediate metrics - n_categories = len(things) + len(stuffs) - self.add_state("iou_sum", default=torch.zeros(n_categories, dtype=torch.double), dist_reduce_fx="sum") - self.add_state("true_positives", default=torch.zeros(n_categories, dtype=torch.int), dist_reduce_fx="sum") - self.add_state("false_positives", default=torch.zeros(n_categories, dtype=torch.int), dist_reduce_fx="sum") - self.add_state("false_negatives", default=torch.zeros(n_categories, dtype=torch.int), dist_reduce_fx="sum") - - def update(self, preds: Tensor, target: Tensor) -> None: - r"""Update state with predictions and targets. - - Args: - preds: panoptic detection of shape ``[batch, *spatial_dims, 2]`` containing - the pair ``(category_id, instance_id)`` for each point. - If the ``category_id`` refer to a stuff, the instance_id is ignored. - - target: ground truth of shape ``[batch, *spatial_dims, 2]`` containing - the pair ``(category_id, instance_id)`` for each pixel of the image. - If the ``category_id`` refer to a stuff, the instance_id is ignored. - - Raises: - TypeError: - If ``preds`` or ``target`` is not an ``torch.Tensor``. - ValueError: - If ``preds`` and ``target`` have different shape. - ValueError: - If ``preds`` has less than 3 dimensions. - ValueError: - If the final dimension of ``preds`` has size != 2. - """ - _validate_inputs(preds, target) - flatten_preds = _prepocess_inputs( - self.things, self.stuffs, preds, self.void_color, self.allow_unknown_preds_category - ) - flatten_target = _prepocess_inputs(self.things, self.stuffs, target, self.void_color, True) - iou_sum, true_positives, false_positives, false_negatives = _panoptic_quality_update( - flatten_preds, - flatten_target, - self.cat_id_to_continuous_id, - self.void_color, - modified_metric_stuffs=self.stuffs, - ) - self.iou_sum += iou_sum - self.true_positives += true_positives - self.false_positives += false_positives - self.false_negatives += false_negatives - - def compute(self) -> Tensor: - """Compute panoptic quality based on inputs passed in to ``update`` previously.""" - return _panoptic_quality_compute(self.iou_sum, self.true_positives, self.false_positives, self.false_negatives) - - def plot( - self, val: Optional[Union[Tensor, Sequence[Tensor]]] = None, ax: Optional[_AX_TYPE] = None - ) -> _PLOT_OUT_TYPE: - """Plot a single or multiple values from the metric. - - Args: - val: Either a single result from calling `metric.forward` or `metric.compute` or a list of these results. - If no value is provided, will automatically call `metric.compute` and plot that result. - ax: An matplotlib axis object. If provided will add plot to that axis - - Returns: - Figure object and Axes object - - Raises: - ModuleNotFoundError: - If `matplotlib` is not installed - - .. plot:: - :scale: 75 - - >>> from torch import tensor - >>> from torchmetrics.detection import ModifiedPanopticQuality - >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]], - ... [[0, 0], [0, 0], [6, 0], [0, 1]], - ... [[0, 0], [0, 0], [6, 0], [0, 1]], - ... [[0, 0], [7, 0], [6, 0], [1, 0]], - ... [[0, 0], [7, 0], [7, 0], [7, 0]]]]) - >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]], - ... [[0, 1], [0, 1], [6, 0], [0, 1]], - ... [[0, 1], [0, 1], [6, 0], [1, 0]], - ... [[0, 1], [7, 0], [1, 0], [1, 0]], - ... [[0, 1], [7, 0], [7, 0], [7, 0]]]]) - >>> metric = ModifiedPanopticQuality(things = {0, 1}, stuffs = {6, 7}) - >>> metric.update(preds, target) - >>> fig_, ax_ = metric.plot() - - .. plot:: - :scale: 75 - - >>> # Example plotting multiple values - >>> from torch import tensor - >>> from torchmetrics.detection import ModifiedPanopticQuality - >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]], - ... [[0, 0], [0, 0], [6, 0], [0, 1]], - ... [[0, 0], [0, 0], [6, 0], [0, 1]], - ... [[0, 0], [7, 0], [6, 0], [1, 0]], - ... [[0, 0], [7, 0], [7, 0], [7, 0]]]]) - >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]], - ... [[0, 1], [0, 1], [6, 0], [0, 1]], - ... [[0, 1], [0, 1], [6, 0], [1, 0]], - ... [[0, 1], [7, 0], [1, 0], [1, 0]], - ... [[0, 1], [7, 0], [7, 0], [7, 0]]]]) - >>> metric = ModifiedPanopticQuality(things = {0, 1}, stuffs = {6, 7}) - >>> vals = [] - >>> for _ in range(20): - ... vals.append(metric(preds, target)) - >>> fig_, ax_ = metric.plot(vals) - """ - return self._plot(val, ax) diff --git a/src/torchmetrics/detection/panoptic_quality.py b/src/torchmetrics/detection/panoptic_qualities.py similarity index 53% rename from src/torchmetrics/detection/panoptic_quality.py rename to src/torchmetrics/detection/panoptic_qualities.py index bf8a0e5f722..a913826af6b 100644 --- a/src/torchmetrics/detection/panoptic_quality.py +++ b/src/torchmetrics/detection/panoptic_qualities.py @@ -30,7 +30,7 @@ from torchmetrics.utilities.plot import _AX_TYPE, _PLOT_OUT_TYPE if not _MATPLOTLIB_AVAILABLE: - __doctest_skip__ = ["PanopticQuality.plot"] + __doctest_skip__ = ["PanopticQuality.plot", "ModifiedPanopticQuality.plot"] class PanopticQuality(Metric): @@ -213,3 +213,182 @@ def plot( >>> fig_, ax_ = metric.plot(vals) """ return self._plot(val, ax) + + +class ModifiedPanopticQuality(Metric): + r"""Compute `Modified Panoptic Quality`_ for panoptic segmentations. + + The metric was introduced in `Seamless Scene Segmentation paper`_, and is an adaptation of the original + `Panoptic Quality`_ where the metric for a stuff class is computed as + + .. math:: + PQ^{\dagger}_c = \frac{IOU_c}{|S_c|} + + where IOU_c is the sum of the intersection over union of all matching segments for a given class, and \|S_c| is + the overall number of segments in the ground truth for that class. + + .. note: + Points in the target tensor that do not map to a known category ID are automatically ignored in the metric + computation. + + Args: + things: + Set of ``category_id`` for countable things. + stuffs: + Set of ``category_id`` for uncountable stuffs. + allow_unknown_preds_category: + Boolean flag to specify if unknown categories in the predictions are to be ignored in the metric + computation or raise an exception when found. + + + Raises: + ValueError: + If ``things``, ``stuffs`` have at least one common ``category_id``. + TypeError: + If ``things``, ``stuffs`` contain non-integer ``category_id``. + + Example: + >>> from torch import tensor + >>> from torchmetrics.detection import ModifiedPanopticQuality + >>> preds = tensor([[[0, 0], [0, 1], [6, 0], [7, 0], [0, 2], [1, 0]]]) + >>> target = tensor([[[0, 1], [0, 0], [6, 0], [7, 0], [6, 0], [255, 0]]]) + >>> pq_modified = ModifiedPanopticQuality(things = {0, 1}, stuffs = {6, 7}) + >>> pq_modified(preds, target) + tensor(0.7667, dtype=torch.float64) + """ + is_differentiable: bool = False + higher_is_better: bool = True + full_state_update: bool = False + plot_lower_bound: float = 0.0 + plot_upper_bound: float = 1.0 + + iou_sum: Tensor + true_positives: Tensor + false_positives: Tensor + false_negatives: Tensor + + def __init__( + self, + things: Collection[int], + stuffs: Collection[int], + allow_unknown_preds_category: bool = False, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) + + things, stuffs = _parse_categories(things, stuffs) + self.things = things + self.stuffs = stuffs + self.void_color = _get_void_color(things, stuffs) + self.cat_id_to_continuous_id = _get_category_id_to_continuous_id(things, stuffs) + self.allow_unknown_preds_category = allow_unknown_preds_category + + # per category intermediate metrics + n_categories = len(things) + len(stuffs) + self.add_state("iou_sum", default=torch.zeros(n_categories, dtype=torch.double), dist_reduce_fx="sum") + self.add_state("true_positives", default=torch.zeros(n_categories, dtype=torch.int), dist_reduce_fx="sum") + self.add_state("false_positives", default=torch.zeros(n_categories, dtype=torch.int), dist_reduce_fx="sum") + self.add_state("false_negatives", default=torch.zeros(n_categories, dtype=torch.int), dist_reduce_fx="sum") + + def update(self, preds: Tensor, target: Tensor) -> None: + r"""Update state with predictions and targets. + + Args: + preds: panoptic detection of shape ``[batch, *spatial_dims, 2]`` containing + the pair ``(category_id, instance_id)`` for each point. + If the ``category_id`` refer to a stuff, the instance_id is ignored. + + target: ground truth of shape ``[batch, *spatial_dims, 2]`` containing + the pair ``(category_id, instance_id)`` for each pixel of the image. + If the ``category_id`` refer to a stuff, the instance_id is ignored. + + Raises: + TypeError: + If ``preds`` or ``target`` is not an ``torch.Tensor``. + ValueError: + If ``preds`` and ``target`` have different shape. + ValueError: + If ``preds`` has less than 3 dimensions. + ValueError: + If the final dimension of ``preds`` has size != 2. + """ + _validate_inputs(preds, target) + flatten_preds = _prepocess_inputs( + self.things, self.stuffs, preds, self.void_color, self.allow_unknown_preds_category + ) + flatten_target = _prepocess_inputs(self.things, self.stuffs, target, self.void_color, True) + iou_sum, true_positives, false_positives, false_negatives = _panoptic_quality_update( + flatten_preds, + flatten_target, + self.cat_id_to_continuous_id, + self.void_color, + modified_metric_stuffs=self.stuffs, + ) + self.iou_sum += iou_sum + self.true_positives += true_positives + self.false_positives += false_positives + self.false_negatives += false_negatives + + def compute(self) -> Tensor: + """Compute panoptic quality based on inputs passed in to ``update`` previously.""" + return _panoptic_quality_compute(self.iou_sum, self.true_positives, self.false_positives, self.false_negatives) + + def plot( + self, val: Optional[Union[Tensor, Sequence[Tensor]]] = None, ax: Optional[_AX_TYPE] = None + ) -> _PLOT_OUT_TYPE: + """Plot a single or multiple values from the metric. + + Args: + val: Either a single result from calling `metric.forward` or `metric.compute` or a list of these results. + If no value is provided, will automatically call `metric.compute` and plot that result. + ax: An matplotlib axis object. If provided will add plot to that axis + + Returns: + Figure object and Axes object + + Raises: + ModuleNotFoundError: + If `matplotlib` is not installed + + .. plot:: + :scale: 75 + + >>> from torch import tensor + >>> from torchmetrics.detection import ModifiedPanopticQuality + >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [7, 0], [6, 0], [1, 0]], + ... [[0, 0], [7, 0], [7, 0], [7, 0]]]]) + >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [1, 0]], + ... [[0, 1], [7, 0], [1, 0], [1, 0]], + ... [[0, 1], [7, 0], [7, 0], [7, 0]]]]) + >>> metric = ModifiedPanopticQuality(things = {0, 1}, stuffs = {6, 7}) + >>> metric.update(preds, target) + >>> fig_, ax_ = metric.plot() + + .. plot:: + :scale: 75 + + >>> # Example plotting multiple values + >>> from torch import tensor + >>> from torchmetrics.detection import ModifiedPanopticQuality + >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [7, 0], [6, 0], [1, 0]], + ... [[0, 0], [7, 0], [7, 0], [7, 0]]]]) + >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [1, 0]], + ... [[0, 1], [7, 0], [1, 0], [1, 0]], + ... [[0, 1], [7, 0], [7, 0], [7, 0]]]]) + >>> metric = ModifiedPanopticQuality(things = {0, 1}, stuffs = {6, 7}) + >>> vals = [] + >>> for _ in range(20): + ... vals.append(metric(preds, target)) + >>> fig_, ax_ = metric.plot(vals) + """ + return self._plot(val, ax) diff --git a/src/torchmetrics/functional/__init__.py b/src/torchmetrics/functional/__init__.py index b48d0b7f586..45756170d13 100644 --- a/src/torchmetrics/functional/__init__.py +++ b/src/torchmetrics/functional/__init__.py @@ -39,8 +39,8 @@ from torchmetrics.functional.classification.roc import roc from torchmetrics.functional.classification.specificity import specificity from torchmetrics.functional.classification.stat_scores import stat_scores -from torchmetrics.functional.detection.modified_panoptic_quality import modified_panoptic_quality -from torchmetrics.functional.detection.panoptic_quality import panoptic_quality +from torchmetrics.functional.detection._deprecated import _modified_panoptic_quality as modified_panoptic_quality +from torchmetrics.functional.detection._deprecated import _panoptic_quality as panoptic_quality from torchmetrics.functional.image.d_lambda import spectral_distortion_index from torchmetrics.functional.image.ergas import error_relative_global_dimensionless_synthesis from torchmetrics.functional.image.gradients import image_gradients @@ -147,7 +147,6 @@ "mean_squared_error", "mean_squared_log_error", "minkowski_distance", - "modified_panoptic_quality", "multiscale_structural_similarity_index_measure", "pairwise_cosine_similarity", "pairwise_euclidean_distance", diff --git a/src/torchmetrics/functional/detection/__init__.py b/src/torchmetrics/functional/detection/__init__.py index 15c5417b473..7ec83290faa 100644 --- a/src/torchmetrics/functional/detection/__init__.py +++ b/src/torchmetrics/functional/detection/__init__.py @@ -11,5 +11,6 @@ # 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. -from torchmetrics.functional.detection.modified_panoptic_quality import modified_panoptic_quality # noqa: F401 -from torchmetrics.functional.detection.panoptic_quality import panoptic_quality # noqa: F401 +from torchmetrics.functional.detection.panoptic_qualities import modified_panoptic_quality, panoptic_quality + +__all__ = ["modified_panoptic_quality", "panoptic_quality"] diff --git a/src/torchmetrics/functional/detection/_deprecated.py b/src/torchmetrics/functional/detection/_deprecated.py new file mode 100644 index 00000000000..26ea32707a9 --- /dev/null +++ b/src/torchmetrics/functional/detection/_deprecated.py @@ -0,0 +1,64 @@ +from typing import Collection + +from torch import Tensor + +from torchmetrics.functional.detection.panoptic_qualities import modified_panoptic_quality, panoptic_quality +from torchmetrics.utilities.prints import _deprecated_root_import_func + + +def _modified_panoptic_quality( + preds: Tensor, + target: Tensor, + things: Collection[int], + stuffs: Collection[int], + allow_unknown_preds_category: bool = False, +) -> Tensor: + """Wrapper for deprecated import. + + >>> from torch import tensor + >>> preds = tensor([[[0, 0], [0, 1], [6, 0], [7, 0], [0, 2], [1, 0]]]) + >>> target = tensor([[[0, 1], [0, 0], [6, 0], [7, 0], [6, 0], [255, 0]]]) + >>> _modified_panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7}) + tensor(0.7667, dtype=torch.float64) + """ + _deprecated_root_import_func("modified_panoptic_quality", "detection") + return modified_panoptic_quality( + preds=preds, + target=target, + things=things, + stuffs=stuffs, + allow_unknown_preds_category=allow_unknown_preds_category, + ) + + +def _panoptic_quality( + preds: Tensor, + target: Tensor, + things: Collection[int], + stuffs: Collection[int], + allow_unknown_preds_category: bool = False, +) -> Tensor: + """Wrapper for deprecated import. + + >>> from torch import tensor + >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [0, 0], [6, 0], [0, 1]], + ... [[0, 0], [7, 0], [6, 0], [1, 0]], + ... [[0, 0], [7, 0], [7, 0], [7, 0]]]]) + >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [0, 1]], + ... [[0, 1], [0, 1], [6, 0], [1, 0]], + ... [[0, 1], [7, 0], [1, 0], [1, 0]], + ... [[0, 1], [7, 0], [7, 0], [7, 0]]]]) + >>> _panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7}) + tensor(0.5463, dtype=torch.float64) + """ + _deprecated_root_import_func("panoptic_quality", "detection") + return panoptic_quality( + preds=preds, + target=target, + things=things, + stuffs=stuffs, + allow_unknown_preds_category=allow_unknown_preds_category, + ) diff --git a/src/torchmetrics/functional/detection/modified_panoptic_quality.py b/src/torchmetrics/functional/detection/modified_panoptic_quality.py deleted file mode 100644 index cda9c894b1d..00000000000 --- a/src/torchmetrics/functional/detection/modified_panoptic_quality.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright The PyTorch Lightning team. -# -# 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. -from typing import Collection - -from torch import Tensor - -from torchmetrics.functional.detection._panoptic_quality_common import ( - _get_category_id_to_continuous_id, - _get_void_color, - _panoptic_quality_compute, - _panoptic_quality_update, - _parse_categories, - _prepocess_inputs, - _validate_inputs, -) - - -def modified_panoptic_quality( - preds: Tensor, - target: Tensor, - things: Collection[int], - stuffs: Collection[int], - allow_unknown_preds_category: bool = False, -) -> Tensor: - r"""Compute `Modified Panoptic Quality`_ for panoptic segmentations. - - The metric was introduced in `Seamless Scene Segmentation paper`_, and is an adaptation of the original - `Panoptic Quality`_ where the metric for a stuff class is computed as - - .. math:: - PQ^{\dagger}_c = \frac{IOU_c}{|S_c|} - - where IOU_c is the sum of the intersection over union of all matching segments for a given class, and \|S_c| is - the overall number of segments in the ground truth for that class. - - .. note: - Points in the target tensor that do not map to a known category ID are automatically ignored in the metric - computation. - - Args: - preds: - torch tensor with panoptic detection of shape [height, width, 2] containing the pair - (category_id, instance_id) for each pixel of the image. If the category_id refer to a stuff, the - instance_id is ignored. - target: - torch tensor with ground truth of shape [height, width, 2] containing the pair (category_id, instance_id) - for each pixel of the image. If the category_id refer to a stuff, the instance_id is ignored. - things: - Set of ``category_id`` for countable things. - stuffs: - Set of ``category_id`` for uncountable stuffs. - allow_unknown_preds_category: - Boolean flag to specify if unknown categories in the predictions are to be ignored in the metric - computation or raise an exception when found. - - Raises: - ValueError: - If ``things``, ``stuffs`` have at least one common ``category_id``. - TypeError: - If ``things``, ``stuffs`` contain non-integer ``category_id``. - TypeError: - If ``preds`` or ``target`` is not an ``torch.Tensor``. - ValueError: - If ``preds`` or ``target`` has different shape. - ValueError: - If ``preds`` has less than 3 dimensions. - ValueError: - If the final dimension of ``preds`` has size != 2. - - Example: - >>> from torch import tensor - >>> preds = tensor([[[0, 0], [0, 1], [6, 0], [7, 0], [0, 2], [1, 0]]]) - >>> target = tensor([[[0, 1], [0, 0], [6, 0], [7, 0], [6, 0], [255, 0]]]) - >>> modified_panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7}) - tensor(0.7667, dtype=torch.float64) - """ - things, stuffs = _parse_categories(things, stuffs) - _validate_inputs(preds, target) - void_color = _get_void_color(things, stuffs) - cat_id_to_continuous_id = _get_category_id_to_continuous_id(things, stuffs) - flatten_preds = _prepocess_inputs(things, stuffs, preds, void_color, allow_unknown_preds_category) - flatten_target = _prepocess_inputs(things, stuffs, target, void_color, True) - iou_sum, true_positives, false_positives, false_negatives = _panoptic_quality_update( - flatten_preds, - flatten_target, - cat_id_to_continuous_id, - void_color, - modified_metric_stuffs=stuffs, - ) - return _panoptic_quality_compute(iou_sum, true_positives, false_positives, false_negatives) diff --git a/src/torchmetrics/functional/detection/panoptic_quality.py b/src/torchmetrics/functional/detection/panoptic_qualities.py similarity index 57% rename from src/torchmetrics/functional/detection/panoptic_quality.py rename to src/torchmetrics/functional/detection/panoptic_qualities.py index fbac497f5aa..6fcb14960e1 100644 --- a/src/torchmetrics/functional/detection/panoptic_quality.py +++ b/src/torchmetrics/functional/detection/panoptic_qualities.py @@ -13,7 +13,8 @@ # limitations under the License. from typing import Collection -from torch import Tensor +import torch +from torch import Tensor, tensor from torchmetrics.functional.detection._panoptic_quality_common import ( _get_category_id_to_continuous_id, @@ -102,3 +103,78 @@ def panoptic_quality( flatten_preds, flatten_target, cat_id_to_continuous_id, void_color ) return _panoptic_quality_compute(iou_sum, true_positives, false_positives, false_negatives) + + +def modified_panoptic_quality( + preds: Tensor, + target: Tensor, + things: Collection[int], + stuffs: Collection[int], + allow_unknown_preds_category: bool = False, +) -> Tensor: + r"""Compute `Modified Panoptic Quality`_ for panoptic segmentations. + + The metric was introduced in `Seamless Scene Segmentation paper`_, and is an adaptation of the original + `Panoptic Quality`_ where the metric for a stuff class is computed as + + .. math:: + PQ^{\dagger}_c = \frac{IOU_c}{|S_c|} + + where IOU_c is the sum of the intersection over union of all matching segments for a given class, and \|S_c| is + the overall number of segments in the ground truth for that class. + + .. note: + Points in the target tensor that do not map to a known category ID are automatically ignored in the metric + computation. + + Args: + preds: + torch tensor with panoptic detection of shape [height, width, 2] containing the pair + (category_id, instance_id) for each pixel of the image. If the category_id refer to a stuff, the + instance_id is ignored. + target: + torch tensor with ground truth of shape [height, width, 2] containing the pair (category_id, instance_id) + for each pixel of the image. If the category_id refer to a stuff, the instance_id is ignored. + things: + Set of ``category_id`` for countable things. + stuffs: + Set of ``category_id`` for uncountable stuffs. + allow_unknown_preds_category: + Boolean flag to specify if unknown categories in the predictions are to be ignored in the metric + computation or raise an exception when found. + + Raises: + ValueError: + If ``things``, ``stuffs`` have at least one common ``category_id``. + TypeError: + If ``things``, ``stuffs`` contain non-integer ``category_id``. + TypeError: + If ``preds`` or ``target`` is not an ``torch.Tensor``. + ValueError: + If ``preds`` or ``target`` has different shape. + ValueError: + If ``preds`` has less than 3 dimensions. + ValueError: + If the final dimension of ``preds`` has size != 2. + + Example: + >>> from torch import tensor + >>> preds = tensor([[[0, 0], [0, 1], [6, 0], [7, 0], [0, 2], [1, 0]]]) + >>> target = tensor([[[0, 1], [0, 0], [6, 0], [7, 0], [6, 0], [255, 0]]]) + >>> modified_panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7}) + tensor(0.7667, dtype=torch.float64) + """ + things, stuffs = _parse_categories(things, stuffs) + _validate_inputs(preds, target) + void_color = _get_void_color(things, stuffs) + cat_id_to_continuous_id = _get_category_id_to_continuous_id(things, stuffs) + flatten_preds = _prepocess_inputs(things, stuffs, preds, void_color, allow_unknown_preds_category) + flatten_target = _prepocess_inputs(things, stuffs, target, void_color, True) + iou_sum, true_positives, false_positives, false_negatives = _panoptic_quality_update( + flatten_preds, + flatten_target, + cat_id_to_continuous_id, + void_color, + modified_metric_stuffs=stuffs, + ) + return _panoptic_quality_compute(iou_sum, true_positives, false_positives, false_negatives) diff --git a/tests/unittests/deprecations/root_class_imports.py b/tests/unittests/deprecations/root_class_imports.py index 6d8ab1f64e7..7595b5244be 100644 --- a/tests/unittests/deprecations/root_class_imports.py +++ b/tests/unittests/deprecations/root_class_imports.py @@ -4,6 +4,8 @@ import pytest from torchmetrics import ( + ModifiedPanopticQuality, + PanopticQuality, PermutationInvariantTraining, ScaleInvariantSignalDistortionRatio, ScaleInvariantSignalNoiseRatio, @@ -16,6 +18,7 @@ @pytest.mark.parametrize( "metric_cls", [ + # Audio pytest.param( partial(PermutationInvariantTraining, scale_invariant_signal_noise_ratio), id="PermutationInvariantTraining" ), @@ -23,6 +26,9 @@ ScaleInvariantSignalNoiseRatio, SignalDistortionRatio, SignalNoiseRatio, + # Detection + ModifiedPanopticQuality, + PanopticQuality, ], ) def test_import_from_root_package(metric_cls): diff --git a/tests/unittests/detection/test_modified_panoptic_quality.py b/tests/unittests/detection/test_modified_panoptic_quality.py index 33eaec17a99..c25e2f69b64 100644 --- a/tests/unittests/detection/test_modified_panoptic_quality.py +++ b/tests/unittests/detection/test_modified_panoptic_quality.py @@ -18,8 +18,8 @@ import pytest import torch -from torchmetrics.detection.modified_panoptic_quality import ModifiedPanopticQuality -from torchmetrics.functional.detection.modified_panoptic_quality import modified_panoptic_quality +from torchmetrics.detection import ModifiedPanopticQuality +from torchmetrics.functional.detection import modified_panoptic_quality from unittests.helpers import seed_all from unittests.helpers.testers import MetricTester diff --git a/tests/unittests/detection/test_panoptic_quality.py b/tests/unittests/detection/test_panoptic_quality.py index 3e6e93adef4..56a5e84b300 100644 --- a/tests/unittests/detection/test_panoptic_quality.py +++ b/tests/unittests/detection/test_panoptic_quality.py @@ -18,8 +18,8 @@ import pytest import torch -from torchmetrics.detection.panoptic_quality import PanopticQuality -from torchmetrics.functional.detection.panoptic_quality import panoptic_quality +from torchmetrics.detection.panoptic_qualities import PanopticQuality +from torchmetrics.functional.detection.panoptic_qualities import panoptic_quality from unittests.helpers import seed_all from unittests.helpers.testers import MetricTester