Skip to content

Conversation

@MalyalaKarthik66
Copy link
Contributor

This PR adds fill_mode="crop" to keras.layers.RandomRotation.

What’s new

  • Introduces a new fill_mode="crop" that removes border artifacts by
    pad → rotate → center-crop, while preserving the input shape.
  • Recomputes the affine rotation matrix for the padded canvas to ensure
    correct rotation geometry.
  • Supports structured inputs (images, segmentation_masks,
    bounding_boxes) with synchronized transformations.
  • Adds clear documentation and runnable examples directly in the layer
    docstring.

Tests

  • Adds tests covering:
    • crop mode shape preservation (batched & unbatched)
    • dict inputs (images + segmentation masks)
    • bounding boxes
    • correctness vs fill_mode="constant"
    • invalid fill_mode handling

Fixes #21954.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @MalyalaKarthik66, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the keras.layers.RandomRotation layer by introducing a new fill_mode option, "crop". This mode intelligently handles rotations by padding the image, performing the rotation, and then center-cropping it back to the original dimensions. This process effectively eliminates common border artifacts that can arise from rotations while maintaining the original image shape, which is crucial for tasks like segmentation. Additionally, the changes ensure that structured inputs, such as images, segmentation masks, and bounding boxes, are transformed synchronously, preserving their spatial relationships.

Highlights

  • New fill_mode="crop": Introduces a new fill_mode="crop" to keras.layers.RandomRotation that removes border artifacts by applying a 'pad → rotate → center-crop' sequence, preserving the input shape.
  • Structured Input Support: The layer now supports synchronized transformations for structured inputs, including images, segmentation_masks, and bounding_boxes, ensuring all related data are transformed consistently.
  • Affine Matrix Recomputation: The affine rotation matrix is recomputed for the padded canvas to ensure correct rotation geometry when using fill_mode="crop".
  • Comprehensive Testing: New tests cover shape preservation for batched and unbatched inputs, dict inputs (images + segmentation masks), bounding boxes, correctness compared to fill_mode="constant", and invalid fill_mode handling.
  • Documentation and Examples: Clear documentation and runnable examples have been added directly within the layer's docstring to illustrate the new features.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new "crop" fill mode to the RandomRotation layer, which pads, rotates, and then center-crops images to maintain the original shape and avoid border artifacts. The changes extend the layer's functionality to support structured inputs, including images, segmentation masks, and bounding boxes, ensuring synchronized transformations. This involved significant refactoring of the RandomRotation class, adding helper methods for padding, cropping, and affine transformations, and updating the __init__ method and documentation. New test cases were added to validate the "crop" mode's behavior, output shape preservation, handling of structured inputs, and the absence of fill_value pixels. Review comments suggest improving consistency by directly using the imported ops module for backend operations instead of self.backend or a local ops_backend alias in the _apply_rotation_with_crop and transform_bounding_boxes methods.

Comment on lines 318 to 326
translate_x=self.backend.numpy.zeros(
[transformation["batch_size"]]
),
translate_y=self.backend.numpy.zeros(
[transformation["batch_size"]]
),
scale=self.backend.numpy.ones([transformation["batch_size"]]),
shear_x=self.backend.numpy.zeros([transformation["batch_size"]]),
shear_y=self.backend.numpy.zeros([transformation["batch_size"]]),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency, please use the imported ops module for backend operations (e.g., ops.numpy.zeros) instead of accessing it via self.backend.

            translate_x=ops.numpy.zeros([transformation["batch_size"]]),
            translate_y=ops.numpy.zeros([transformation["batch_size"]]),
            scale=ops.numpy.ones([transformation["batch_size"]]),
            shear_x=ops.numpy.zeros([transformation["batch_size"]]),
            shear_y=ops.numpy.zeros([transformation["batch_size"]]),

Comment on lines 371 to 397
ops_backend = self.backend
height = transformation["image_height"]
width = transformation["image_width"]
batch_size = transformation["batch_size"]

# Keras preprocessing bbox dict: {"boxes": ..., "labels": ...}
# Convert to xyxy for rotation, then clip and convert back.
bounding_boxes = converters.convert_format(
bounding_boxes,
source=self.bounding_box_format,
target="xyxy",
height=height,
width=width,
)

boxes = bounding_boxes["boxes"]
boxes = converters.affine_transform(
boxes=boxes,
angle=transformation["angle"],
translate_x=ops_backend.numpy.zeros([batch_size]),
translate_y=ops_backend.numpy.zeros([batch_size]),
scale=ops_backend.numpy.ones([batch_size]),
shear_x=ops_backend.numpy.zeros([batch_size]),
shear_y=ops_backend.numpy.zeros([batch_size]),
height=height,
width=width,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency with the rest of the file, it's better to use the imported ops module for backend operations instead of creating a local ops_backend alias. This improves readability and adheres to the intended API usage.

        height = transformation["image_height"]
        width = transformation["image_width"]
        batch_size = transformation["batch_size"]

        # Keras preprocessing bbox dict: {"boxes": ..., "labels": ...}
        # Convert to xyxy for rotation, then clip and convert back.
        bounding_boxes = converters.convert_format(
            bounding_boxes,
            source=self.bounding_box_format,
            target="xyxy",
            height=height,
            width=width,
        )

        boxes = bounding_boxes["boxes"]
        boxes = converters.affine_transform(
            boxes=boxes,
            angle=transformation["angle"],
            translate_x=ops.numpy.zeros([batch_size]),
            translate_y=ops.numpy.zeros([batch_size]),
            scale=ops.numpy.ones([batch_size]),
            shear_x=ops.numpy.zeros([batch_size]),
            shear_y=ops.numpy.zeros([batch_size]),
            height=height,
            width=width,
        )

@codecov-commenter
Copy link

codecov-commenter commented Jan 3, 2026

Codecov Report

❌ Patch coverage is 87.50000% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.69%. Comparing base (c67eddb) to head (a80d5bd).

Files with missing lines Patch % Lines
...eprocessing/image_preprocessing/random_rotation.py 87.50% 6 Missing and 5 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master   #21977   +/-   ##
=======================================
  Coverage   82.69%   82.69%           
=======================================
  Files         588      588           
  Lines       61448    61511   +63     
  Branches     9622     9629    +7     
=======================================
+ Hits        50812    50868   +56     
- Misses       8147     8150    +3     
- Partials     2489     2493    +4     
Flag Coverage Δ
keras 82.51% <84.09%> (+<0.01%) ⬆️
keras-jax 61.56% <78.40%> (+0.01%) ⬆️
keras-numpy 56.83% <78.40%> (+0.01%) ⬆️
keras-openvino 37.33% <10.22%> (-0.03%) ⬇️
keras-tensorflow 63.73% <78.40%> (+0.01%) ⬆️
keras-torch 62.47% <78.40%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@shashaka
Copy link
Collaborator

It would be very helpful to understand the logic if you could add a gist for this PR. You may refer to my sample gist below.

https://colab.research.google.com/drive/1n_0OzVKFHz5g0bFNOjQNulAAIjBuXvOT#scrollTo=lNIsN7fVPl3P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Usages of Keras Augmentaiton Layers

4 participants