aleju / imgaug

Image augmentation for machine learning experiments.
http://imgaug.readthedocs.io
MIT License
14.43k stars 2.45k forks source link

Chance of no augmentation being applied #109

Open CMCDragonkai opened 6 years ago

CMCDragonkai commented 6 years ago

It seems there's always a chance of no augmentation applied. Is it possible to make sure that at least 1 random augmentation is applied?

aleju commented 6 years ago
iaa.SomeOf((1, 3), [iaa.Dropout(...), iaa.Affine(...), iaa.AdditiveGaussianNoise()])

would always apply 1 to 3 of the 3 augmenters (i.e. at least one).

iaa.OneOf([iaa.Dropout(...), iaa.Affine(...), iaa.AdditiveGaussianNoise()])

always applies exactly 1 of the 3 augmenters.

iaa.Sequential([iaa.Dropout(...), iaa.Affine(...), iaa.AdditiveGaussianNoise()])

always apply 3 of the 3 augmenters.

If you use Sometimes or augmenters with probabilities (e.g. Invert), there is always a small chance that none of them will be applied. This can currently only be avoided if you add some other augmenters of which at least one is always applied (using above methods).

CMCDragonkai commented 5 years ago

I've taken your advice, but I've detected that no augmentation can still happen with:

iaa.SomeOf((1, None) ...)

Everything else works, except after checking the output augmentations, I found no augmentation was applied to one specific image. This image is nothing special. Testing in isolation allowed me to augment the image normally. It's just as I used it a batch of many images, a single image kept not being augmented.

I created an "augmentation schema" which I apply:

{
  "augs": [{
      "type": "Fliplr",
      "args": [1.0]
    },
    {
      "type": "Flipud",
      "args": [1.0]
    },
    {
      "type": "CropAndPad",
      "kwargs": {
        "percent": [-0.25, 0.25],
        "pad_mode": "constant"
      }
    },
    {
      "type": "Multiply",
      "args": [
        [0.9, 1.1]
      ],
      "kwargs": {
        "per_channel": 0.5,
        "name": "Multiply"
      }
    },
    {
      "type": "GaussianBlur",
      "kwargs": {
        "sigma": [0, 0.1],
        "name": "GaussianBlur"
      }
    },
    {
      "type": "AdditiveGaussianNoise",
      "kwargs": {
        "scale": [0, 5.1],
        "per_channel": true,
        "name": "AdditiveGaussianNoise"
      }
    },
    {
      "type": "Affine",
      "kwargs": {
        "scale": {
          "x": [0.8, 1.2],
          "y": [0.8, 1.2]
        },
        "translate_percent": {
          "x": [-0.2, 0.2],
          "y": [-0.2, 0.2]
        },
        "rotate": [-50, 50],
        "shear": [-8, 8],
        "order": 3,
        "mode": "constant"
      }
    }
  ],
  "mask_filter": ["Multiply", "GaussianBlur", "AdditiveGaussianNoise"]
}
def parse_aug_config(config_file):
    aug_config = json.load(config_file, cls=AugmentationDecoder)
    augs = []
    for aug in aug_config['augs']:
        aug_constructor = getattr(iaa, aug['type'])
        aug_args = aug.get('args', [])
        aug_kwargs = aug.get('kwargs', {})
        augs.append(aug_constructor(*aug_args, **aug_kwargs))
    augs_mask_filter = aug_config['mask_filter']
    aug_pipeline_template = iaa.SomeOf((1, None), augs, random_order=True)

    def masks_activator(images, augmenter, parents, default):
        if augmenter.name in augs_mask_filter:
            return False
        else:
            return default

    masks_hook = ia.HooksImages(activator=masks_activator)
    return (aug_pipeline_template, masks_hook)
def augment_image_mask(image, mask, aug_pipeline_template, masks_hook):
    aug_pipeline = aug_pipeline_template.to_deterministic()
    if mask is not None:
        (image_aug, *_) = aug_pipeline.augment_images(np.array([image]))
        (mask_aug, *_) = aug_pipeline.augment_images(
            np.array([mask]), hooks=masks_hook)
    else:
        (image_aug, *_) = aug_pipeline.augment_images(np.array([image]))
        mask_aug = None
    return (image_aug, mask_aug)

I'm running imgaug 0.2.6.

Are you sure there's no way that iaa.SomeOf especially with the augmentations that I'm using that 0 augmentations may be applied?

aleju commented 5 years ago

Your json code contains a lot of lists for parameter ranges, e.g. "sigma": [0, 0.1]. These are parsed by json.load as lists and provided to the augmenter constructors without changes. The augmenters then interpret these parameters as lists of allowed values and pick randomly from these lists. So for the above sigma example you would get either 0 or 0.1 sampled, but not anything in between. To get a uniform distribution, you have to provide either (0, 0.1) (i.e. a tuple) or imgaug.parameters.Uniform(0, 0.1) as a parameter. There is of course also a chance that a uniform distribution randomly sampled 0 or 0.1.

btw

(image_aug, *_) = aug_pipeline.augment_images(np.array([image]))

can probably be replaced by

image_aug = aug_pipeline.augment_image(np.array([image]))
CMCDragonkai commented 5 years ago

Oh... well JSON only has lists, I wonder what's a good way to integrate the difference between lists and tuples in our augmentation schema.

CMCDragonkai commented 5 years ago

I think the best way for now is always interpret the lists as tuples, as that was what I originally was doing before I extracted the code into a JSON schema.