Same as MONAI's ``list_data_collate``, except any tensors are centrally padded to match the shape of the biggest tensor in each dimension. This transform is useful if some of the applied transforms generate batch data of different sizes. This can be used on both list and dictionary
| 42 | |
| 43 | |
| 44 | class PadListDataCollate(InvertibleTransform): |
| 45 | """ |
| 46 | Same as MONAI's ``list_data_collate``, except any tensors are centrally padded to match the shape of the biggest |
| 47 | tensor in each dimension. This transform is useful if some of the applied transforms generate batch data of |
| 48 | different sizes. |
| 49 | |
| 50 | This can be used on both list and dictionary data. |
| 51 | Note that in the case of the dictionary data, it may add the transform information to the list of invertible transforms |
| 52 | if input batch have different spatial shape, so need to call static method: `inverse` before inverting other transforms. |
| 53 | |
| 54 | Note that normally, a user won't explicitly use the `__call__` method. Rather this would be passed to the `DataLoader`. |
| 55 | This means that `__call__` handles data as it comes out of a `DataLoader`, containing batch dimension. However, the |
| 56 | `inverse` operates on dictionaries containing images of shape `C,H,W,[D]`. This asymmetry is necessary so that we can |
| 57 | pass the inverse through multiprocessing. |
| 58 | |
| 59 | Args: |
| 60 | method: padding method (see :py:class:`monai.transforms.SpatialPad`) |
| 61 | mode: padding mode (see :py:class:`monai.transforms.SpatialPad`) |
| 62 | kwargs: other arguments for the `np.pad` or `torch.pad` function. |
| 63 | note that `np.pad` treats channel dimension as the first dimension. |
| 64 | |
| 65 | """ |
| 66 | |
| 67 | def __init__(self, method: str = Method.SYMMETRIC, mode: str = PytorchPadMode.CONSTANT, **kwargs) -> None: |
| 68 | self.method = method |
| 69 | self.mode = mode |
| 70 | self.kwargs = kwargs |
| 71 | |
| 72 | def __call__(self, batch: Any): |
| 73 | """ |
| 74 | Args: |
| 75 | batch: batch of data to pad-collate |
| 76 | """ |
| 77 | # data is either list of dicts or list of lists |
| 78 | is_list_of_dicts = isinstance(batch[0], dict) |
| 79 | # loop over items inside of each element in a batch |
| 80 | batch_item = tuple(batch[0].keys()) if is_list_of_dicts else range(len(batch[0])) |
| 81 | for key_or_idx in batch_item: |
| 82 | # calculate max size of each dimension |
| 83 | max_shapes = [] |
| 84 | for elem in batch: |
| 85 | if not isinstance(elem[key_or_idx], (torch.Tensor, np.ndarray)): |
| 86 | break |
| 87 | max_shapes.append(elem[key_or_idx].shape[1:]) |
| 88 | # len > 0 if objects were arrays, else skip as no padding to be done |
| 89 | if not max_shapes: |
| 90 | continue |
| 91 | max_shape = np.array(max_shapes).max(axis=0) |
| 92 | # If all same size, skip |
| 93 | if np.all(np.array(max_shapes).min(axis=0) == max_shape): |
| 94 | continue |
| 95 | |
| 96 | # Use `SpatialPad` to match sizes, Default params are central padding, padding with 0's |
| 97 | padder = SpatialPad(spatial_size=max_shape, method=self.method, mode=self.mode, **self.kwargs) |
| 98 | for idx, batch_i in enumerate(batch): |
| 99 | orig_size = batch_i[key_or_idx].shape[1:] |
| 100 | padded = padder(batch_i[key_or_idx]) |
| 101 | batch = replace_element(padded, batch, idx, key_or_idx) |
no outgoing calls
no test coverage detected
searching dependent graphs…