Inconsistency between image resizing with Keras PIL and TensorFlow?
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.
Introduction
Yes, image resizing can differ between Keras using PIL and TensorFlow using tf.image.resize, even when the target size looks identical. The differences usually come from interpolation algorithms, antialiasing behavior, pixel coordinate conventions, aspect-ratio handling, and dtype conversion.
This matters more than many teams expect. If training used one resizing pipeline and inference uses another, the model may see systematically different pixel values and lose accuracy in subtle, hard-to-debug ways.
Why the Outputs Differ
The most common causes are:
- different interpolation defaults
- different antialiasing behavior when shrinking
- different channel and dtype handling
- different preprocessing order, such as resize before crop versus crop before resize
Even if both sides say “bilinear,” the implementation details can still differ enough to produce non-identical arrays.
A Minimal Comparison
PIL example:
TensorFlow example:
Both produce a 224 x 224 x 3 float tensor, but the values may not be exactly the same.
Common Sources of Mismatch
One common mismatch is dtype order. PIL often gives you uint8 data first, while TensorFlow pipelines often convert to float32 during or before resize. That can slightly alter results depending on the processing order.
Another mismatch is antialiasing. TensorFlow exposes an antialias option:
PIL may use different filtering behavior when downscaling, so enabling or disabling antialiasing can change how closely TensorFlow matches the PIL-based output.
A third mismatch is hidden preprocessing. For example:
- one path may center-crop before resize
- another may resize directly
- one may normalize to
[-1, 1] - another may normalize to
[0, 1]
Those differences often get blamed on resizing even when the root cause is the overall preprocessing pipeline.
Keras Utilities May Still Use PIL Semantics
Older Keras image utilities and some preprocessing helpers historically relied on PIL under the hood. Newer TensorFlow pipelines often use tf.image directly. So “Keras versus TensorFlow” may actually mean “PIL semantics versus TensorFlow image-op semantics.”
That is why the real fix is usually not to guess which library is “correct,” but to pick one pipeline and use it consistently in training, validation, and inference.
How to Make Training and Inference Consistent
The safest strategy is:
- choose one resizing implementation
- use it everywhere
- test exact pixel outputs on a sample image
If you are training in TensorFlow, the simplest answer is often to move all image preprocessing into a TensorFlow input pipeline:
Then use the same function, or an exported equivalent, in validation and serving.
If your production stack must use PIL, then it may be better to train with PIL-style preprocessing too, or at least benchmark the difference explicitly.
How to Debug the Difference
If outputs do not match, compare them step by step:
- raw decoded image shape
- channel order
- dtype before resize
- interpolation mode
- antialias setting
- normalization range after resize
You can also inspect the actual numeric difference:
That tells you whether the mismatch is tiny numerical noise or a genuinely different preprocessing behavior.
Common Pitfalls
One common mistake is assuming “bilinear” means identical output across libraries. It does not guarantee bit-for-bit equivalence.
Another mistake is training with one pipeline and serving with another because the resize calls look superficially similar. Models can be very sensitive to that mismatch.
It is also easy to focus only on resize and ignore normalization, crop order, or channel handling, which often contribute just as much to the inconsistency.
Finally, do not validate the pipeline only by image shape. Two arrays can have the same shape and still be meaningfully different as model inputs.
Summary
- PIL-based Keras resizing and TensorFlow resizing can produce different pixel values.
- Differences usually come from interpolation details, antialiasing, dtype conversion, and pipeline order.
- The safest solution is to standardize on one preprocessing pipeline across training and inference.
- Compare actual arrays, not just output shapes, when debugging.
- In production ML systems, consistency matters more than picking one library out of habit.
Related reading
- Increase or decrease learning rate for adding neurons or weights?
- Index of a maximum element in TensorFlow tensor
- Initial bias values for a neural network
- Initial bias values for a neural network
- Inference using saved model in Tensorflow 2 how to control in/output?
- Inference using saved model in Tensorflow 2 how to control in/output?
- Initialize keras placeholder as Input to a Custom Layer
- Initializing LSTM hidden state Tensorflow/Keras
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.