panoptic_qualities.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # Copyright The PyTorch Lightning team.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from collections.abc import Collection
  15. import torch
  16. from torch import Tensor
  17. from torchmetrics.functional.detection._panoptic_quality_common import (
  18. _get_category_id_to_continuous_id,
  19. _get_void_color,
  20. _panoptic_quality_compute,
  21. _panoptic_quality_update,
  22. _parse_categories,
  23. _prepocess_inputs,
  24. _validate_inputs,
  25. )
  26. def panoptic_quality(
  27. preds: Tensor,
  28. target: Tensor,
  29. things: Collection[int],
  30. stuffs: Collection[int],
  31. allow_unknown_preds_category: bool = False,
  32. return_sq_and_rq: bool = False,
  33. return_per_class: bool = False,
  34. ) -> Tensor:
  35. r"""Compute `Panoptic Quality`_ for panoptic segmentations.
  36. .. math::
  37. PQ = \frac{IOU}{TP + 0.5 FP + 0.5 FN}
  38. where IOU, TP, FP and FN are respectively the sum of the intersection over union for true positives, the number of
  39. true positives, false positives and false negatives. This metric is inspired by the PQ implementation of
  40. panopticapi, a standard implementation for the PQ metric for object detection.
  41. .. note:
  42. Points in the target tensor that do not map to a known category ID are automatically ignored in the metric
  43. computation.
  44. Args:
  45. preds:
  46. torch tensor with panoptic detection of shape [height, width, 2] containing the pair
  47. (category_id, instance_id) for each pixel of the image. If the category_id refer to a stuff, the
  48. instance_id is ignored.
  49. target:
  50. torch tensor with ground truth of shape [height, width, 2] containing the pair (category_id, instance_id)
  51. for each pixel of the image. If the category_id refer to a stuff, the instance_id is ignored.
  52. things:
  53. Set of ``category_id`` for countable things.
  54. stuffs:
  55. Set of ``category_id`` for uncountable stuffs.
  56. allow_unknown_preds_category:
  57. Boolean flag to specify if unknown categories in the predictions are to be ignored in the metric
  58. computation or raise an exception when found.
  59. return_sq_and_rq:
  60. Boolean flag to specify if Segmentation Quality and Recognition Quality should be also returned.
  61. return_per_class:
  62. Boolean flag to specify if the per-class values should be returned or the class average.
  63. Raises:
  64. ValueError:
  65. If ``things``, ``stuffs`` have at least one common ``category_id``.
  66. TypeError:
  67. If ``things``, ``stuffs`` contain non-integer ``category_id``.
  68. TypeError:
  69. If ``preds`` or ``target`` is not an ``torch.Tensor``.
  70. ValueError:
  71. If ``preds`` or ``target`` has different shape.
  72. ValueError:
  73. If ``preds`` has less than 3 dimensions.
  74. ValueError:
  75. If the final dimension of ``preds`` has size != 2.
  76. Example:
  77. >>> from torch import tensor
  78. >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]],
  79. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  80. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  81. ... [[0, 0], [7, 0], [6, 0], [1, 0]],
  82. ... [[0, 0], [7, 0], [7, 0], [7, 0]]]])
  83. >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]],
  84. ... [[0, 1], [0, 1], [6, 0], [0, 1]],
  85. ... [[0, 1], [0, 1], [6, 0], [1, 0]],
  86. ... [[0, 1], [7, 0], [1, 0], [1, 0]],
  87. ... [[0, 1], [7, 0], [7, 0], [7, 0]]]])
  88. >>> panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7})
  89. tensor(0.5463, dtype=torch.float64)
  90. You can also return the segmentation and recognition quality alognside the PQ
  91. >>> from torch import tensor
  92. >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]],
  93. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  94. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  95. ... [[0, 0], [7, 0], [6, 0], [1, 0]],
  96. ... [[0, 0], [7, 0], [7, 0], [7, 0]]]])
  97. >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]],
  98. ... [[0, 1], [0, 1], [6, 0], [0, 1]],
  99. ... [[0, 1], [0, 1], [6, 0], [1, 0]],
  100. ... [[0, 1], [7, 0], [1, 0], [1, 0]],
  101. ... [[0, 1], [7, 0], [7, 0], [7, 0]]]])
  102. >>> panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7}, return_sq_and_rq=True)
  103. tensor([0.5463, 0.6111, 0.6667], dtype=torch.float64)
  104. You can also specify to return the per-class metrics
  105. >>> from torch import tensor
  106. >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]],
  107. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  108. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  109. ... [[0, 0], [7, 0], [6, 0], [1, 0]],
  110. ... [[0, 0], [7, 0], [7, 0], [7, 0]]]])
  111. >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]],
  112. ... [[0, 1], [0, 1], [6, 0], [0, 1]],
  113. ... [[0, 1], [0, 1], [6, 0], [1, 0]],
  114. ... [[0, 1], [7, 0], [1, 0], [1, 0]],
  115. ... [[0, 1], [7, 0], [7, 0], [7, 0]]]])
  116. >>> panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7}, return_per_class=True)
  117. tensor([[0.5185, 0.0000, 0.6667, 1.0000]], dtype=torch.float64)
  118. You can also specify to return the per-class metrics and the segmentation and recognition quality
  119. >>> from torch import tensor
  120. >>> preds = tensor([[[[6, 0], [0, 0], [6, 0], [6, 0]],
  121. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  122. ... [[0, 0], [0, 0], [6, 0], [0, 1]],
  123. ... [[0, 0], [7, 0], [6, 0], [1, 0]],
  124. ... [[0, 0], [7, 0], [7, 0], [7, 0]]]])
  125. >>> target = tensor([[[[6, 0], [0, 1], [6, 0], [0, 1]],
  126. ... [[0, 1], [0, 1], [6, 0], [0, 1]],
  127. ... [[0, 1], [0, 1], [6, 0], [1, 0]],
  128. ... [[0, 1], [7, 0], [1, 0], [1, 0]],
  129. ... [[0, 1], [7, 0], [7, 0], [7, 0]]]])
  130. >>> panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7},
  131. ... return_per_class=True, return_sq_and_rq=True)
  132. tensor([[0.5185, 0.7778, 0.6667],
  133. [0.0000, 0.0000, 0.0000],
  134. [0.6667, 0.6667, 1.0000],
  135. [1.0000, 1.0000, 1.0000]], dtype=torch.float64)
  136. """
  137. things, stuffs = _parse_categories(things, stuffs)
  138. _validate_inputs(preds, target)
  139. void_color = _get_void_color(things, stuffs)
  140. cat_id_to_continuous_id = _get_category_id_to_continuous_id(things, stuffs)
  141. flatten_preds = _prepocess_inputs(things, stuffs, preds, void_color, allow_unknown_preds_category)
  142. flatten_target = _prepocess_inputs(things, stuffs, target, void_color, True)
  143. iou_sum, true_positives, false_positives, false_negatives = _panoptic_quality_update(
  144. flatten_preds, flatten_target, cat_id_to_continuous_id, void_color
  145. )
  146. pq, sq, rq, pq_avg, sq_avg, rq_avg = _panoptic_quality_compute(
  147. iou_sum,
  148. true_positives,
  149. false_positives,
  150. false_negatives,
  151. )
  152. if return_per_class:
  153. if return_sq_and_rq:
  154. return torch.stack((pq, sq, rq), dim=-1)
  155. return pq.view(1, -1)
  156. if return_sq_and_rq:
  157. return torch.stack((pq_avg, sq_avg, rq_avg), dim=0)
  158. return pq_avg
  159. def modified_panoptic_quality(
  160. preds: Tensor,
  161. target: Tensor,
  162. things: Collection[int],
  163. stuffs: Collection[int],
  164. allow_unknown_preds_category: bool = False,
  165. ) -> Tensor:
  166. r"""Compute `Modified Panoptic Quality`_ for panoptic segmentations.
  167. The metric was introduced in `Seamless Scene Segmentation paper`_, and is an adaptation of the original
  168. `Panoptic Quality`_ where the metric for a stuff class is computed as
  169. .. math::
  170. PQ^{\dagger}_c = \frac{IOU_c}{|S_c|}
  171. where :math:`IOU_c` is the sum of the intersection over union of all matching segments for a given class, and
  172. :math:`|S_c|` is the overall number of segments in the ground truth for that class.
  173. .. note:
  174. Points in the target tensor that do not map to a known category ID are automatically ignored in the metric
  175. computation.
  176. Args:
  177. preds:
  178. torch tensor with panoptic detection of shape [height, width, 2] containing the pair
  179. (category_id, instance_id) for each pixel of the image. If the category_id refer to a stuff, the
  180. instance_id is ignored.
  181. target:
  182. torch tensor with ground truth of shape [height, width, 2] containing the pair (category_id, instance_id)
  183. for each pixel of the image. If the category_id refer to a stuff, the instance_id is ignored.
  184. things:
  185. Set of ``category_id`` for countable things.
  186. stuffs:
  187. Set of ``category_id`` for uncountable stuffs.
  188. allow_unknown_preds_category:
  189. Boolean flag to specify if unknown categories in the predictions are to be ignored in the metric
  190. computation or raise an exception when found.
  191. Raises:
  192. ValueError:
  193. If ``things``, ``stuffs`` have at least one common ``category_id``.
  194. TypeError:
  195. If ``things``, ``stuffs`` contain non-integer ``category_id``.
  196. TypeError:
  197. If ``preds`` or ``target`` is not an ``torch.Tensor``.
  198. ValueError:
  199. If ``preds`` or ``target`` has different shape.
  200. ValueError:
  201. If ``preds`` has less than 3 dimensions.
  202. ValueError:
  203. If the final dimension of ``preds`` has size != 2.
  204. Example:
  205. >>> from torch import tensor
  206. >>> preds = tensor([[[0, 0], [0, 1], [6, 0], [7, 0], [0, 2], [1, 0]]])
  207. >>> target = tensor([[[0, 1], [0, 0], [6, 0], [7, 0], [6, 0], [255, 0]]])
  208. >>> modified_panoptic_quality(preds, target, things = {0, 1}, stuffs = {6, 7})
  209. tensor(0.7667, dtype=torch.float64)
  210. """
  211. things, stuffs = _parse_categories(things, stuffs)
  212. _validate_inputs(preds, target)
  213. void_color = _get_void_color(things, stuffs)
  214. cat_id_to_continuous_id = _get_category_id_to_continuous_id(things, stuffs)
  215. flatten_preds = _prepocess_inputs(things, stuffs, preds, void_color, allow_unknown_preds_category)
  216. flatten_target = _prepocess_inputs(things, stuffs, target, void_color, True)
  217. iou_sum, true_positives, false_positives, false_negatives = _panoptic_quality_update(
  218. flatten_preds,
  219. flatten_target,
  220. cat_id_to_continuous_id,
  221. void_color,
  222. modified_metric_stuffs=stuffs,
  223. )
  224. _, _, _, pq_avg, _, _ = _panoptic_quality_compute(iou_sum, true_positives, false_positives, false_negatives)
  225. return pq_avg