Skip to content

Conversation

@utsab345
Copy link
Contributor

Summary

Fixed a shape mismatch issue when preprocess_input() receives 3D input (single image)
with data_format='channels_first'. The bug occurred because the mean and std tensors
were reshaped for 4D input only.

Changes

  • Added conditional reshape for both 3D and 4D cases.
  • Included shape validation for unsupported dimensions.

Test

import numpy as np, tensorflow as tf, keras
image = tf.transpose(tf.random.uniform((224, 224, 3)), perm=[2, 0, 1])
keras.applications.vgg16.preprocess_input(image, data_format='channels_first')

Closes #21749

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @utsab345, 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 primarily addresses a critical bug in the preprocess_input function within Keras applications, ensuring it correctly handles 3D image inputs when using a channels_first data format. Beyond the core fix, it also enhances the robustness of image utility functions by improving file format handling and adding new tests for image saving.

Highlights

  • Bug Fix: preprocess_input for 3D channels_first: Fixed a shape mismatch issue in keras.applications.vgg16.preprocess_input() when receiving 3D input (single image) with data_format='channels_first', which previously occurred because mean and std tensors were only reshaped for 4D input.
  • Conditional Reshaping and Validation: Implemented conditional reshaping logic for mean and standard deviation tensors within _preprocess_tensor_input to correctly handle both 3D and 4D inputs when data_format is channels_first. Additionally, added explicit shape validation to raise a ValueError for unsupported input dimensions.
  • Improved save_img Utility: Enhanced the save_img utility by normalizing file_format='jpg' to file_format='jpeg' and updating the warning message for RGBA to RGB conversion to reflect the 'JPEG' format explicitly.
  • New Integration Tests for save_img: Added new integration tests for the save_img utility to ensure proper saving of JPG images, including verification of RGBA to RGB conversion when saving to the JPEG format.
  • .gitignore Update: The .gitignore file was updated to include the venv directory, preventing virtual environment files from being committed.
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.

@codecov-commenter
Copy link

codecov-commenter commented Oct 17, 2025

Codecov Report

❌ Patch coverage is 21.42857% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.83%. Comparing base (e2be4de) to head (a935e1c).

Files with missing lines Patch % Lines
keras/src/applications/imagenet_utils.py 27.27% 4 Missing and 4 partials ⚠️
keras/src/utils/image_utils.py 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #21754      +/-   ##
==========================================
+ Coverage   82.63%   84.83%   +2.20%     
==========================================
  Files         572      572              
  Lines       58555    58566      +11     
  Branches     9153     9158       +5     
==========================================
+ Hits        48385    49687    +1302     
+ Misses       7843     6433    -1410     
- Partials     2327     2446     +119     
Flag Coverage Δ
keras 84.65% <21.42%> (+2.21%) ⬆️
keras-jax 65.43% <21.42%> (+2.23%) ⬆️
keras-numpy 57.66% <0.00%> (+0.09%) ⬆️
keras-openvino 34.49% <0.00%> (+0.11%) ⬆️
keras-tensorflow 66.19% <21.42%> (+2.23%) ⬆️
keras-torch 65.72% <21.42%> (+2.21%) ⬆️
keras.applications 83.31% <27.27%> (?)
keras.applications-jax 83.31% <27.27%> (?)
keras.applications-numpy 22.72% <0.00%> (?)
keras.applications-openvino 22.72% <0.00%> (?)
keras.applications-tensorflow 83.31% <27.27%> (?)
keras.applications-torch 83.04% <27.27%> (?)

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.

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 correctly fixes a shape mismatch issue in preprocess_input for 3D inputs with data_format='channels_first'. The changes are well-tested and also include a nice improvement in save_img to normalize the 'jpg' format. I've suggested a couple of improvements in imagenet_utils.py to improve the error messages and reduce code duplication. Overall, this is a good contribution.

Comment on lines +281 to +286
if ndim == 3:
mean_tensor = ops.reshape(mean_tensor, (3, 1, 1))
elif ndim == 4:
mean_tensor = ops.reshape(mean_tensor, (1, 3, 1, 1))
else:
raise ValueError(f"Unsupported shape for channels_first: {x.shape}")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

While this logic correctly handles 3D and 4D inputs, the error message could be more informative. According to the Keras API design guidelines, a good error message should explain what was expected and how the user can fix it.1 This message only states what was received. Consider clarifying that only 3D and 4D tensors are supported for channels_first.

Additionally, you can make the code slightly cleaner by checking for the invalid ndim case first.

Suggested change
if ndim == 3:
mean_tensor = ops.reshape(mean_tensor, (3, 1, 1))
elif ndim == 4:
mean_tensor = ops.reshape(mean_tensor, (1, 3, 1, 1))
else:
raise ValueError(f"Unsupported shape for channels_first: {x.shape}")
if ndim not in (3, 4):
raise ValueError(
f"Unsupported tensor rank: {ndim}. With `data_format='channels_first'`, "
"`preprocess_input` only supports 3D (single image) and 4D (batch of "
f"images) tensors. Received tensor with shape: {x.shape}"
)
if ndim == 3:
mean_tensor = ops.reshape(mean_tensor, (3, 1, 1))
elif ndim == 4:
mean_tensor = ops.reshape(mean_tensor, (1, 3, 1, 1))

Style Guide References

Footnotes

  1. The style guide states that error messages should be contextual, informative, and actionable, explaining what happened, what was expected, and how to fix it.

Comment on lines 292 to +302
if data_format == "channels_first":
std_tensor = ops.reshape(std_tensor, (-1, 1, 1))
if ndim == 3:
std_tensor = ops.reshape(std_tensor, (3, 1, 1))
elif ndim == 4:
std_tensor = ops.reshape(std_tensor, (1, 3, 1, 1))
else:
raise ValueError(
f"Unsupported shape for channels_first: {x.shape}"
)
else:
std_tensor = ops.reshape(std_tensor, (1,) * (ndim - 1) + (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

This logic for reshaping std_tensor is a duplicate of the logic used for mean_tensor above. To improve maintainability and reduce code duplication, consider refactoring this. You could determine the reshape_shape once at the start of the data_format == 'channels_first' block and reuse it for both tensors. This would also apply to the error handling logic.

"""
data_format = backend.standardize_data_format(data_format)
# Normalize jpg → jpeg
if file_format is not None and file_format.lower() == "jpg":
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please revert changes in this file

((50, 50, 4), "rgba.jpg"),
],
)
def test_save_jpg(tmp_path, shape, name):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use a TestCase subclass and use self.assert... methods instead of naked assert staments

Copy link
Collaborator

@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!

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.

Bug in func keras.applications.vgg16.preprocess_input() when x in 3D and data_format=='channels_first'

4 participants