Я обнаружил несоответствие между:
- функциями изменения размера изображения из
keras.preprocessing
, которые являются обертками для функций PIL - функциями изменения размера изображения в
tf.image
TensorFlow.
Я тренирую модель глубокого обучения для задачи по компьютерному зрению с Керасом (на самом деле, с tf.keras
, но здесь это не имеет значения).Затем я обслуживаю модель с помощью TF Serving, что требует от меня отправки изображений в модель в виде закодированных байтовых строк, где они декодируются с использованием tf.image.decode_png
перед прохождением графа модели.
Проблема возникает, когда яизменить размер изображения. Изменение размера с помощью билинейной интерполяции (или любого другого метода) дает разные результаты с PIL по сравнению с tf.image
, до такой степени, что классификация по модели изменяется в зависимости от того, какую функцию я использую.
Код ниже предоставляет воспроизводимый пример.
import numpy as np
from PIL import Image
from keras.preprocessing.image import load_img, img_to_array
import tensorflow as tf
# Generate an 'image' with numpy, save as png
np.random.seed(42)
image = np.random.randint(0, 255, size=(256, 256, 3)).astype(np.uint8)
Image.fromarray(image).convert("RGB").save('my_image.png')
Теперь давайте загрузим изображение двумя способами.Сначала с оболочками PIL от Keras, как во время обучения модели, затем кодируются в виде двоичной строки и декодируются с помощью функций TensorFlow, как на моем сервере моделей.
# Using Keras PIL wrappers
keras_image = img_to_array(load_img('./my_image.png'))
# Using TF functionalities
with tf.Session() as sess:
with open('./my_image.png', 'rb') as f:
tf_image_ = tf.image.decode_png(f.read())
tf_image = sess.run(tf_image_)
Пока все хорошо, так как оба изображенияточно так же (кроме dtype, когда Керас преобразовал изображение в float32):
# Assert equality
np.array_equal(keras_image, tf_image)
> True
Повторение этого кода с изменением размера, однако, дает другой результат:
# Using Keras PIL wrappers, with resizing
keras_image_rs = img_to_array(load_img('./my_image.png',
target_size=(224, 224),
interpolation='bilinear'))
# Using TF functionalities, with resizing
with tf.Session() as sess:
with open('./my_image.png', 'rb') as f:
tf_image_ = tf.image.decode_png(f.read())
# Add and remove dimension
# As tf.image.resize_* requires a batch dimension
tf_image_ = tf.expand_dims(tf_image_, 0)
tf_image_ = tf.image.resize_bilinear(tf_image_,
[224, 224],
align_corners=True)
tf_image_ = tf.squeeze(tf_image_, axis=[0])
tf_image_rs = sess.run(tf_image_)
# Assert equality
np.array_equal(keras_image_rs, tf_image_rs)
> False
Среднееабсолютная разница между двумя изображениями не является ничтожной:
np.mean(np.abs(keras_image_rs - tf_image_rs))
7.982703
Я играл с аргументом align_corners
и пробовал и другие доступные методы интерполяции.Ни один из них не дает такой же вывод, как при изменении размера изображения с помощью PIL.Это довольно раздражает, поскольку дает мне перекос между тренировками и результатами тестирования.Кто-нибудь имеет представление о том, что вызывает такое поведение, или о том, как это исправить?