albumentations-team / albumentations

Fast and flexible image augmentation library. Paper about the library: https://www.mdpi.com/2078-2489/11/2/125
https://albumentations.ai
MIT License
14.27k stars 1.65k forks source link

MaskDropout cannot be applied to multiple masks as described in documentation #926

Open orbiskcw opened 3 years ago

orbiskcw commented 3 years ago

🐛 Bug

I believe the MaskDropout transformation cannot be applied to multiple masks as described in the documentation here and I am thus not sure how to then provide the data such that for an instance segmentation problem, it can randomly mask out between 0 and N masks (rather than always all masks or no masks). From the above referenced page, I am under the impression that simple calling a Compose object with the named masks argument (a list of numpy arrays) should be enough, but this results in the following assertion error for me:

Traceback (most recent call last):
  File "/home/cas/.config/JetBrains/PyCharm2020.2/scratches/scratch.py", line 27, in <module>
    transformed = transform(image=image, masks=[mask1, mask2])
  File "/home/cas/Documents/repositories/tensorflow-image-classification/.venv/lib/python3.8/site-packages/albumentations/core/composition.py", line 182, in __call__
    data = t(force_apply=force_apply, **data)
  File "/home/cas/Documents/repositories/tensorflow-image-classification/.venv/lib/python3.8/site-packages/albumentations/core/transforms_interface.py", line 77, in __call__
    assert all(key in kwargs for key in self.targets_as_params), "{} requires {}".format(
AssertionError: MaskDropout requires ['mask']

To Reproduce

I included a minimal example to reproduce the issue:

import albumentations as A
import cv2
import numpy as np

# Define transformations
transform = A.Compose([
    A.MaskDropout(p=1.0)
])

# Load image
image = cv2.imread("/path/to/random/image")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Generate random masks (for simplicity sake, but was also tested with 'real' masks)
# Assuming the associated image is 1920x1440
mask1 = np.full(1920 * 1440, False) 
mask1[:1000] = True
mask2 = np.full(1920 * 1440, False)
mask2[10000:11000] = True

# Apply transformations and visualize output
transformed = transform(image=image, masks=[mask1, mask2])

Expected behavior

As explained in the documentation on the masks datatype and as hinted towards in the MaskDropout documentation, I feel like we should be able to pass the masks argument with a mask per instance. Based on your parameters, a randomly chosen subset of these masks is then dropped out.

Environment

Additional context

I am not 100% sure whether this is a bug or intentional, but if I misunderstood and it is intentional, could you elaborate on how to use the MaskDropout augmentation with multiple instances as described in the documentation:

Image & mask augmentation that zero out mask and image regions corresponding to **randomly chosen object instance** from mask.
BloodAxe commented 3 years ago

Hi. We will take a look on this issue meanwhile.

As a quick fix, would a semantic mask (e.g each pixel can have values of {C}, where C - total number of classes) work for your case?

orbiskcw commented 3 years ago

Hi. Thank you for the quick response and the suggested quick fix. That will indeed work for me. I have been working on instance segmentation for so long now, that it did not even occur to me! :wink:

Thanks! :raised_hands:

Dipet commented 3 years ago

You also could to stack all mask into single multichannel mask: np.dstack(masks)