Return the obj, count, and y of the majority instance in each cluster (i.e. the object with which it has the highest overlap). :param num_classes: int Number of classes in the dataset. Specifying `num_classes` allows identifying 'void' labels. By convention,
(
self,
num_classes: int = None
)
| 157 | return self.obj.unique().numel() |
| 158 | |
| 159 | def major( |
| 160 | self, |
| 161 | num_classes: int = None |
| 162 | ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| 163 | """Return the obj, count, and y of the majority instance in each |
| 164 | cluster (i.e. the object with which it has the highest overlap). |
| 165 | |
| 166 | :param num_classes: int |
| 167 | Number of classes in the dataset. Specifying `num_classes` |
| 168 | allows identifying 'void' labels. By convention, we assume |
| 169 | `y ∈ [0, self.num_classes-1]` ARE ALL VALID LABELS (i.e. not |
| 170 | 'ignored', 'void', 'unknown', etc), while `y < 0` AND |
| 171 | `y >= self.num_classes` ARE VOID LABELS. Void data is dealt |
| 172 | with following https://arxiv.org/abs/1801.00868 and |
| 173 | https://arxiv.org/abs/1905.01220 |
| 174 | """ |
| 175 | |
| 176 | # If `num_classes` was not passed, we set it to `y_max + 1` |
| 177 | # (i.e. there are no 'void' objects) |
| 178 | num_classes = num_classes if num_classes else self.y.max() + 1 |
| 179 | |
| 180 | # Compute the cluster index for each overlap (i.e. each row in |
| 181 | # self.values) |
| 182 | cluster_idx = self.indices |
| 183 | |
| 184 | # Search the overlaps with void objects |
| 185 | pair_is_void = (self.y < 0) | (self.y >= num_classes) |
| 186 | |
| 187 | # Search for the obj with the largest overlap, for each cluster |
| 188 | x = torch.stack((self.count, self.count * ~pair_is_void)).T |
| 189 | res = scatter_max(x, cluster_idx, dim=0) |
| 190 | count = res[0][:, 0] |
| 191 | argmax = res[1][:, 0] |
| 192 | obj = self.obj[argmax] |
| 193 | y = self.y[argmax] |
| 194 | |
| 195 | # If no cluster mainly overlaps with a void object, exit here |
| 196 | is_major_void = (y < 0) | (y >= num_classes) |
| 197 | if (~is_major_void).all(): |
| 198 | return obj, count, y |
| 199 | |
| 200 | # Otherwise, we need to find those clusters which overlap with |
| 201 | # void, but with less than 50%. These clusters will not be |
| 202 | # assigned to their main void cluster, but to their second-best |
| 203 | # overlap. This way, only clusters with +50% void overlap will |
| 204 | # be excluded from metrics computation, as defined in: |
| 205 | # https://arxiv.org/abs/1801.00868 |
| 206 | |
| 207 | # Search if any of the clusters assigned to a void object have |
| 208 | # less than 50% void points |
| 209 | total_count = scatter_sum(self.count, cluster_idx, dim=0) |
| 210 | major_50_plus = (count / total_count) > 0.5 |
| 211 | if major_50_plus[is_major_void].all(): |
| 212 | return obj, count, y |
| 213 | |
| 214 | # Assign the clusters with less than 50% void overlap to their |
| 215 | # second-best overlap |
| 216 | count_no_void = res[0][:, 1] |
no outgoing calls
no test coverage detected