Skip to content

Conversation

@MarcosAsh
Copy link
Contributor

@MarcosAsh MarcosAsh commented Jan 4, 2026

Summary

Unified extract_patches to handle both 2D and 3D patch extraction based on the size argument, similar to how keras.ops.conv handles different dimensions.

Changes

size=(h, w) -> 2D patch extraction. This already existed.
size=(d, h, w) -> 3D patch extraction

Why

As suggested in #21624, having a unified API is more consistent with other Keras ops like conv.

Tests

Updated test_extract_patches_invalid_size to reflect new valid 3D sizes.
Added test_extract_patches_unified_3d for the new functionality.
All existing extract_patches tests pass.

Relates to #21624

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @MarcosAsh, 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.ops.image.extract_patches function by unifying its API to seamlessly handle both 2D image and 3D volume patch extraction. By intelligently interpreting the size argument, the function now provides a consistent interface, mirroring the flexibility found in other Keras operations like keras.ops.conv. This change improves the function's versatility and aligns it with Keras's goal of providing intuitive and unified APIs for various data dimensions.

Highlights

  • Unified API for Patch Extraction: The extract_patches function now supports both 2D image and 3D volume patch extraction through a single, unified API. The dimensionality is determined by the length of the size argument.
  • 3D Patch Extraction Support: The function now accepts a size tuple of length 3 (e.g., (depth, height, width)) for extracting patches from 3D volumes, similar to how keras.ops.conv handles different dimensions.
  • Updated Documentation and Examples: The docstring for extract_patches has been updated to reflect the new 3D capabilities, including detailed explanations of input/output shapes and new examples demonstrating 3D patch extraction.
  • Enhanced Test Coverage: New tests (test_extract_patches_unified_3d) have been added to validate the correct behavior of 3D patch extraction for both batched and unbatched inputs. Existing tests for invalid size arguments (test_extract_patches_invalid_size) were also refined.

🧠 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 unifies the extract_patches function to support both 2D and 3D patches, which is a great step towards a more consistent API. The implementation looks good, and the added tests cover the new functionality well.

I have a few suggestions to further improve the user experience:

  • The docstrings for images, size, and data_format could be more explicit to improve clarity, especially with the new 3D support.
  • I've also suggested adding more robust input validation at the beginning of the extract_patches function to provide clearer error messages for invalid size arguments, along with the corresponding test updates. This aligns with Keras' API design guidelines for helpful error messages.

Comment on lines +626 to +640
images: Input image/volume or batch of images/volumes.
For 2D patches: 3D `(H, W, C)` or 4D `(N, H, W, C)`.
For 3D patches: 4D `(D, H, W, C)` or 5D `(N, D, H, W, C)`.
size: Patch size as int or tuple.
Length 2 tuple `(patch_height, patch_width)` for 2D patches.
Length 3 tuple `(patch_depth, patch_height, patch_width)` for
3D patches.
strides: Strides for patch extraction. If not specified, defaults
to `size` (non-overlapping patches).
dilation_rate: Dilation rate for patch extraction. Note that
`dilation_rate > 1` is not supported with `strides > 1`.
padding: The type of padding algorithm to use: `"same"` or `"valid"`.
data_format: A string specifying the data format of the input tensor.
It can be either `"channels_last"` or `"channels_first"`.
`"channels_last"` corresponds to inputs with shape
`(batch, height, width, channels)`, while `"channels_first"`
corresponds to inputs with shape `(batch, channels, height, width)`.
If not specified, the value will default to
`keras.config.image_data_format`.
If not specified, defaults to `keras.config.image_data_format`.
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The docstrings for images, size, and data_format could be more explicit to improve clarity, especially with the new 3D support. The current images docstring assumes channels_last without stating it, the size docstring is ambiguous about integer values, and the data_format docstring is a bit sparse. I suggest clarifying these points for a better user experience.

        images: Input image/volume or batch of images/volumes. Assumes
            `channels_last` data format.
            For 2D patches: 3D `(H, W, C)` or 4D `(N, H, W, C)`.
            For 3D patches: 4D `(D, H, W, C)` or 5D `(N, D, H, W, C)`.
        size: The size of the patches to extract.
            - If `size` is an integer, 2D patches of size `(size, size)` are
              extracted.
            - If `size` is a tuple of 2 integers, 2D patches of size
              `(patch_height, patch_width)` are extracted.
            - If `size` is a tuple of 3 integers, 3D patches of size
              `(patch_depth, patch_height, patch_width)` are extracted.
        strides: Strides for patch extraction. If not specified, defaults
            to `size` (non-overlapping patches).
        dilation_rate: Dilation rate for patch extraction. Note that
            `dilation_rate > 1` is not supported with `strides > 1`.
        padding: The type of padding algorithm to use: `"same"` or `"valid"`.
        data_format: A string specifying the data format of the input tensor.
            It can be either `"channels_last"` or `"channels_first"`.
            `"channels_last"` corresponds to inputs with shape
            `(batch, ..., channels)`, while `"channels_first"` corresponds
            to inputs with shape `(batch, channels, ...)`. If not specified,
            defaults to `keras.config.image_data_format`.

Comment on lines +677 to +678
# Determine 2D vs 3D based on size argument
if not isinstance(size, int) and len(size) == 3:
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation relies on _extract_patches to validate the size argument for non-3D cases. This can lead to a confusing error message (Expected an int or a tuple of length 2) if an invalid size like a tuple of length 4 is provided, as the function now supports 3D patches. To provide a clearer error message and adhere to the style guide of validating input early, I suggest adding validation for size at the beginning of the extract_patches function. This will require updating test_extract_patches_invalid_size to match the new error types and messages.

    if not isinstance(size, int):
        if not isinstance(size, (tuple, list)):
            raise TypeError(
                "Invalid `size` argument. Expected an int or a tuple. "
                f"Received: size={size} of type {type(size).__name__}"
            )
        if len(size) not in (2, 3):
            raise ValueError(
                "Invalid `size` argument. Expected a tuple of length 2 or 3. "
                f"Received: size={size} with length {len(size)}"
            )

    # Determine 2D vs 3D based on size argument
    if not isinstance(size, int) and len(size) == 3:
References
  1. The Keras API design guidelines state that user errors should be caught early with detailed, actionable feedback. The suggested change improves the error message for invalid size arguments to be more specific and helpful. (link)

Comment on lines 2359 to 2371
def test_extract_patches_invalid_size(self):
size = (3, 3, 3) # Invalid size, too many dimensions
size = "5" # Invalid size type
image = np.random.uniform(size=(2, 20, 20, 3))
with self.assertRaisesRegex(
TypeError, "Expected an int or a tuple of length 2"
):
kimage.extract_patches(image, size)

size = "5" # Invalid size type
size = (3, 3, 3, 3) # Invalid size, too many dimensions
with self.assertRaisesRegex(
TypeError, "Expected an int or a tuple of length 2"
):
kimage.extract_patches(image, 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

This test should be updated to reflect the improved validation in extract_patches. The error types and messages for invalid size will change with the suggested validation logic.

Suggested change
def test_extract_patches_invalid_size(self):
size = (3, 3, 3) # Invalid size, too many dimensions
size = "5" # Invalid size type
image = np.random.uniform(size=(2, 20, 20, 3))
with self.assertRaisesRegex(
TypeError, "Expected an int or a tuple of length 2"
):
kimage.extract_patches(image, size)
size = "5" # Invalid size type
size = (3, 3, 3, 3) # Invalid size, too many dimensions
with self.assertRaisesRegex(
TypeError, "Expected an int or a tuple of length 2"
):
kimage.extract_patches(image, size)
def test_extract_patches_invalid_size(self):
size = "5" # Invalid size type
image = np.random.uniform(size=(2, 20, 20, 3))
with self.assertRaisesRegex(
TypeError, "Expected an int or a tuple"
):
kimage.extract_patches(image, size)
size = (3, 3, 3, 3) # Invalid size, too many dimensions
with self.assertRaisesRegex(
ValueError, "Expected a tuple of length 2 or 3"
):
kimage.extract_patches(image, size)

@codecov-commenter
Copy link

codecov-commenter commented Jan 4, 2026

Codecov Report

❌ Patch coverage is 77.77778% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.68%. Comparing base (43fa477) to head (d41c0df).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
keras/src/ops/image.py 77.77% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #21980      +/-   ##
==========================================
- Coverage   82.69%   82.68%   -0.01%     
==========================================
  Files         588      588              
  Lines       61369    61457      +88     
  Branches     9612     9627      +15     
==========================================
+ Hits        50749    50817      +68     
- Misses       8135     8149      +14     
- Partials     2485     2491       +6     
Flag Coverage Δ
keras 82.51% <77.77%> (-0.01%) ⬇️
keras-jax 61.54% <77.77%> (-0.03%) ⬇️
keras-numpy 56.80% <77.77%> (-0.03%) ⬇️
keras-openvino 37.35% <0.00%> (-0.01%) ⬇️
keras-tensorflow 63.71% <77.77%> (-0.03%) ⬇️
keras-torch 62.46% <77.77%> (-0.03%) ⬇️

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.

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.

3 participants