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

Add RandomBrightness preprocessing layer. #122

Closed
wants to merge 9 commits into from

Conversation

qlzh727
Copy link
Member

@qlzh727 qlzh727 commented Feb 9, 2022

Fix #87.

As a demo (produced by the demo script).

Original image:
original

Augmented image (scale by -0.5 ~ 0.5
augmented
randomly selected)

@qlzh727 qlzh727 requested review from fchollet and LukeWood February 9, 2022 23:20
During inference time, the output will be identical to input. Call the layer with
training=True to adjust brightness of the input.

Note that same brightness adjustment will be apply to all the images in the same
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what we want? Does this have any positive/negative significant impact on convergence?
https://github.com/tensorflow/models/blob/master/official/vision/beta/projects/simclr/dataloaders/preprocess_ops.py#L30

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great link - yeah, if possible we should avoid applying the same augmentations to an entire batch unless it is extremely difficult to avoid doing so.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if it is the same on the entire batch why we are not just layer wrapping:
https://www.tensorflow.org/api_docs/python/tf/image/random_brightness

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also as we have discussed in #91 (comment) we still need to evaluate if we want to expose the op in the public API, keep it only in the keras-cv private API or just embedded in the layer itself.

In the case we want to expose the op in the public API are we going to almost duplicate tf.image.random_brightness?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. The new implementation will generate different adjustment for each of the image in the batch.

On a side note, tf.image.random_brightness has some assumption about the dtype and the range of the value. Eg when dtype is int, it expect the value to range from [0, 255]. When the dtype is float, it will assume the value is ranged [0.0, 1.0]. This means if you give it a float32 image with value [0, 255], then it will almost adjust nothing, since the delta is only [0, 1] and not scaled with * 255.

The current implementation in this PR is only expecting RGB colored image, and all the dtypes should work.

Copy link
Contributor

@bhack bhack Feb 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this one is true, but I think that we are entering in another policy here where forking ops is preferred then PR Tensorflow (core).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the difference between the current PR and tf ops is quite large and have different assumption

  1. Scale the delta based on the input dtype (this is the major one, and has been apply to all the tf.image ops, which I don't think can be easily changed)
  2. Support of batch inputs. All the existing tf.image ops only take care of single image input. Updating all of them to have batch input support might require more work (but doable).
  3. Output value clip. (Minor and doable).
  4. Uneven delta range. (Minor and doable).

I am not sure who is the active API owner of tf.image ops, @fchollet for more inputs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Are you sure?

In the end probably it is better to take the ownership of tf.image here and handle the deprecation warning PRs in TF instead of growing incompatible API between TFA/TF/Keras-*.

Then I understand the everyone care only about its ownership but I think that we could improve the library perception to the users/contributors if we think as an ecosystem.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you are right, my mistake. The current PR apply different adjustment to each image, but the tf.image apply the same adjustment.

Checked with @fchollet offline. It seems that there isn't an active owner for tf.image at this moment. We will have some discussion about how we are going to move forward.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current PR apply different adjustment to each image, but the tf.image apply the same adjustment.

Yes like your first original implementation here.
In the end as I've explained it is more an ecosystem coordination/harmonization topic then just forking ops with slightly incompatible behaviors so I've mentioned you with a new comment at #74 (comment).
I hope that we could track this general topic there.

batch.

Args:
scale: Float or a list/tuple of 2 floats between -1.0 and 1.0. The scale is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we expose image pixel boundaries as well? I'm starting to think we should support all formats for pixel values (i..e [0,1], [-1, 1], [0, 255]).

This seems like a decision we should make and keep consistent across all of our layers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For brightness/contrast and any color related augmentation, I feel we should stay with RGB color (0~255). The normalization of the input (for model) should happen after the color adjustment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think so, like there is a separate rescaling layer that can be used for pixel normalization after all transformation.

@LukeWood
Copy link
Contributor

LukeWood commented Feb 9, 2022

Thanks Scott for the feature add!

@qlzh727
Copy link
Member Author

qlzh727 commented Feb 10, 2022

It seems that the test for random_cutout are failing. We should fix or disable it for now.

@LukeWood
Copy link
Contributor

It seems that the test for random_cutout are failing. We should fix or disable it for now.

Weird - I just ran locally and it worked. Let me look into what's going on here.

@bhack bhack mentioned this pull request Feb 10, 2022
false_fn=lambda: inputs,
)

def _brightness_adjust(self, image):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to standardize image or images?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@mattdangerw
Copy link
Member

A couple comments on consistency within Keras. It seems like bad UX that:

  • Random contrast will live and be documented in core Keras, as tf.keras.layers.RandomConstrast.
  • Random brightness live will and be documented here as keras_cv.layers.RandomBrightness.
  • The two layers will have different expectations around input ranges.

I am not super opinionated on a solution. But I think we should:

  • Consider landing this in core keras, or aliasing RandomContrast here, so the two can be used in the same way.
  • If we stick with the 0, 255 range here, consider doing the same for RandomContrast, if no one is relying on the current behavior.

@bhack
Copy link
Contributor

bhack commented Feb 11, 2022

@mattdangerw Also internally tf.keras.layers.RandomConstrast implementaton relies on the tf.image ops:

https://github.com/keras-team/keras/blob/v2.8.0/keras/layers/preprocessing/image_preprocessing.py#L1183

Here in the brightness the ops is forked with a slightly different behaviour.

@LukeWood
Copy link
Contributor

A couple comments on consistency within Keras. It seems like bad UX that:

  • Random contrast will live and be documented in core Keras, as tf.keras.layers.RandomConstrast.
  • Random brightness live will and be documented here as keras_cv.layers.RandomBrightness.
  • The two layers will have different expectations around input ranges.

I am not super opinionated on a solution. But I think we should:

  • Consider landing this in core keras, or aliasing RandomContrast here, so the two can be used in the same way.
  • If we stick with the 0, 255 range here, consider doing the same for RandomContrast, if no one is relying on the current behavior.

I like the idea of aliasing RandomContrast. It will quickly become an outlier as more and more image based preprocessing layers become hosted in KerasCV. Changing the old behavior to expect [0, 255] seems reasonable if KPLs have not been around long enough that this breaking change will be too difficult to get submitted.

@mattdangerw
Copy link
Member

I like the idea of aliasing RandomContrast. It will quickly become an outlier as more and more image based preprocessing layers become hosted in KerasCV. Changing the old behavior to expect [0, 255] seems reasonable if KPLs have not been around long enough that this breaking change will be too difficult to get submitted.

Unless we do some sort of bulk solution, I think that tension will continue to persist somewhat given that there are 10ish image preprocessing layers in core Keras.

I would actually vote for RandomBrightness in core keras. That would give us an informal split of the "really simple" image augmentation (flips, rotations, brightness, contrast) living in core, and the more complex (cutmix, cutout, solarization, histogram equalization, randaugment) living in keras-cv. I would find that somewhat intuitive, and we can (and probably have to) cross link from one to the other in our documentation.

@LukeWood
Copy link
Contributor

LukeWood commented Feb 11, 2022

I like the idea of aliasing RandomContrast. It will quickly become an outlier as more and more image based preprocessing layers become hosted in KerasCV. Changing the old behavior to expect [0, 255] seems reasonable if KPLs have not been around long enough that this breaking change will be too difficult to get submitted.

Unless we do some sort of bulk solution, I think that tension will continue to persist somewhat given that there are 10ish image preprocessing layers in core Keras.

I would actually vote for RandomBrightness in core keras. That would give us an informal split of the "really simple" image augmentation (flips, rotations, brightness, contrast) living in core, and the more complex (cutmix, cutout, solarization, histogram equalization, randaugment) living in keras-cv. I would find that somewhat intuitive, and we can (and probably have to) cross link from one to the other in our documentation.

yeah, that's a really good point. I underestimated how many image augmentations were in core Keras. RandomBrightness does seem to fit the mould a bit better in core Keras.

Ok, I'm on board to add it there.

@bhack
Copy link
Contributor

bhack commented Feb 11, 2022

Honestly we are going to strongly confuse users on the namespace to use or the wheel to install to find a specific CV preprocessing layer.
I suppose that we are going to also confuse contributors on what repo they are going to propose a PR/ticket for CV preprocessing ops.

How solarize is different from brightness? It is hard to semantically grasp this namespace difference as the TF user base has both in the same tf.image namespace:

https://www.tensorflow.org/api_docs/python/tf/image

For brightness adjustment, the value should be same for all the channels. The existing implementation was generating the different value across the channel.
@qlzh727
Copy link
Member Author

qlzh727 commented Feb 11, 2022

I like the idea of aliasing RandomContrast. It will quickly become an outlier as more and more image based preprocessing layers become hosted in KerasCV. Changing the old behavior to expect [0, 255] seems reasonable if KPLs have not been around long enough that this breaking change will be too difficult to get submitted.

Unless we do some sort of bulk solution, I think that tension will continue to persist somewhat given that there are 10ish image preprocessing layers in core Keras.
I would actually vote for RandomBrightness in core keras. That would give us an informal split of the "really simple" image augmentation (flips, rotations, brightness, contrast) living in core, and the more complex (cutmix, cutout, solarization, histogram equalization, randaugment) living in keras-cv. I would find that somewhat intuitive, and we can (and probably have to) cross link from one to the other in our documentation.

yeah, that's a really good point. I underestimated how many image augmentations were in core Keras. RandomBrightness does seem to fit the mould a bit better in core Keras.

Ok, I'm on board to add it there.

I also like the idea of having core image KPL in keras core, and have extended KPL in keras-cv, in the meantime, the core image KPL should probably be aliasing here.

For the behavior difference of [0, 255] vs [0, 1] in the existing keras image KPL, we should probably fix the discrepancy and unify to [0, 255]. This means we will update the image KPL in core keras.

Copy link
Contributor

@fchollet fchollet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

import tensorflow as tf

_SCALE_VALIDATION_ERROR = (
"The `scale` should be number or a list of two numbers "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The scale argument should be a number (or a list of two numbers) "
"in the range [-1.0, 1.0]. "

batch.

Args:
scale: Float or a list/tuple of 2 floats between -1.0 and 1.0. The scale is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For naming consistency with other preprocessing layers, we should use factor, see e.g. https://keras.io/api/layers/preprocessing_layers/image_augmentation/random_rotation/

When -1 is chosen, the output image will be black, and when 1.0 is
chosen, the image will be fully white. When only one float is provided,
eg, 0.2, then -0.2 will be used for lower bound and 0.2 will be used for
upper bound.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also support integers? Interpreted as a range in absolute levels, e.g. 32 would be interpreted as a +-32 shift of the channels

if input_number > 1.0 or input_number < -1.0:
raise ValueError(_SCALE_VALIDATION_ERROR + f"Got {input_number}")

def call(self, inputs, training=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Augmentation preprocessing layers should default to training=True (default should be to apply the transformation). You can assume that training is a Python bool in what follows.

copybara-service bot pushed a commit to keras-team/keras that referenced this pull request Feb 15, 2022
This change is ported from keras-team/keras-cv#122.

This layer is considered as a core KPL, since we already have RandomContrast. All the image related KPL will be exposed in the keras-cv later as well.

PiperOrigin-RevId: 428849704
copybara-service bot pushed a commit to keras-team/keras that referenced this pull request Feb 15, 2022
This change is ported from keras-team/keras-cv#122.

This layer is considered as a core KPL, since we already have RandomContrast. All the image related KPL will be exposed in the keras-cv later as well.

PiperOrigin-RevId: 428849704
copybara-service bot pushed a commit to keras-team/keras that referenced this pull request Feb 16, 2022
This change is ported from keras-team/keras-cv#122.

This layer is considered as a core KPL, since we already have RandomContrast. All the image related KPL will be exposed in the keras-cv later as well.

PiperOrigin-RevId: 428849704
copybara-service bot pushed a commit to keras-team/keras that referenced this pull request Feb 16, 2022
This change is ported from keras-team/keras-cv#122.

This layer is considered as a core KPL, since we already have RandomContrast. All the image related KPL will be exposed in the keras-cv later as well.

PiperOrigin-RevId: 428849704
copybara-service bot pushed a commit to keras-team/keras that referenced this pull request Feb 16, 2022
This change is ported from keras-team/keras-cv#122.

This layer is considered as a core KPL, since we already have RandomContrast. All the image related KPL will be exposed in the keras-cv later as well.

PiperOrigin-RevId: 429127641
copybara-service bot pushed a commit to tensorflow/tensorflow that referenced this pull request Feb 16, 2022
This change is ported from keras-team/keras-cv#122.

This layer is considered as a core KPL, since we already have RandomContrast. All the image related KPL will be exposed in the keras-cv later as well.

PiperOrigin-RevId: 429127641
Change-Id: Ide2609d3f2c8d1f33260c99b8607f9df0b289200
@qlzh727
Copy link
Member Author

qlzh727 commented Feb 21, 2022

Closing this PR now. Since we have keras.layers.RandomContrast, Keras team decided to keep the RandomBrightness in the core keras as well since both of them are very close to each other. See https://github.com/keras-team/keras/blob/9628af85a0a2cb04cf433b1ad991017b70ae2005/keras/layers/preprocessing/image_preprocessing.py#L1393 for more details.

@qlzh727 qlzh727 closed this Feb 21, 2022
@bhack bhack mentioned this pull request May 16, 2022
@qlzh727 qlzh727 deleted the bright branch September 20, 2022 17:49
freedomtan pushed a commit to freedomtan/keras-cv that referenced this pull request Jul 20, 2023
* Add keras_core.layers.Attention

* Address style comments

* Fix jax
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RandomBrightness preprocessing layer
7 participants