Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pad to Target Shape #1234

Open
rickymwalsh opened this issue Nov 13, 2024 · 0 comments
Open

Pad to Target Shape #1234

rickymwalsh opened this issue Nov 13, 2024 · 0 comments
Labels
enhancement New feature or request

Comments

@rickymwalsh
Copy link
Contributor

🚀 Feature

A transform with most of the same functionality as CropOrPad but never crops, only pads to ensure a minimum target shape is achieved.

Motivation

Using a patch-based pipeline, I need to pad my images to ensure they are are not smaller than the patch size. For example, the S-I size varies between ~250 to >1000, and my patch size is usually 320 or 496. But I don't want to crop because I still want to be able to take multiple patches from the volumes.

Pitch

Either a new class to achieve this, built on top of the existing CropOrPad and just removing the crop section of apply_transform:

class PadToTargetShape(tio.CropOrPad):
    """Pad, if necessary, to match a target shape."""
    def __init__(self, target_shape, **kwargs):
        super().__init__(target_shape=target_shape, **kwargs)

    def apply_transform(self, subject: tio.Subject) -> tio.Subject:
        subject.check_consistent_space()
        padding_params, _ = self.compute_crop_or_pad(subject)
        padding_kwargs = {'padding_mode': self.padding_mode}
        if padding_params is not None:
            pad = Pad(padding_params, **padding_kwargs)
            subject = pad(subject)  # type: ignore[assignment]
        return subject

Or change the existing CropOrPad class to add flags.

class CropOrPad(SpatialTransform):
    def __init__(
        self,
        target_shape: Union[int, TypeTripletInt, None] = None,
        padding_mode: Union[str, float] = 0,
        mask_name: Optional[str] = None,
        labels: Optional[Sequence[int]] = None,
        apply_crop: bool = True,
        apply_pad: bool = True,
        **kwargs,
    ):
        .....
        self.apply_crop = apply_crop
        self.apply_pad = apply_pad

    def apply_transform(self, subject: Subject) -> Subject:
        subject.check_consistent_space()
        padding_params, cropping_params = self.compute_crop_or_pad(subject)
        padding_kwargs = {'padding_mode': self.padding_mode}
        if self.apply_pad and padding_params is not None:  # Added check here!
            pad = Pad(padding_params, **padding_kwargs)
            subject = pad(subject)  # type: ignore[assignment]
        if self.apply_crop and cropping_params is not None:  # Added check here!
            crop = Crop(cropping_params)
            subject = crop(subject)  # type: ignore[assignment]
        return subject

Alternatives

  • This could be done as part of pre-processing, but it is less dynamic when wanting to experiment with patch sizes.
  • It could be done by calculating the difference between the existing shape and target shape and using tio.transforms.Pad, but it would be much nicer just to have a Class already able to do this, along with the other functionality of CropOrPad, if required.

Maybe I'm missing something obvious, an easy way of achieving this already, if so let me know :)

@rickymwalsh rickymwalsh added the enhancement New feature or request label Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant