#python #python-3.x #tensorflow #keras
#python #python-3.x #tensorflow #keras
Вопрос:
Я пытаюсь добавить случайное масштабирование к своим изображениям, которые представляют собой файлы tiff с разрешением 128×160 на 1 канал, но новая версия случайного масштабирования для keras tensorflow привела меня в замешательство, я не понимаю, каким должен быть формат кортежа, который он ожидает в качестве аргумента диапазона масштабирования.
Из документации.
tf.keras.preprocessing.image.random_zoom(
x, zoom_range, row_axis=1, col_axis=2, channel_axis=0, fill_mode='nearest',
cval=0.0, interpolation_order=1
)
Мне нужно добавить случайное масштабирование к моему изображению, и я пытаюсь вот так:
zoom_range = ((0.4, 0.4))
img = tf.keras.preprocessing.image.random_zoom(
img, zoom_range, row_axis=1, col_axis=2, channel_axis=0, fill_mode='nearest',
cval=0.0, interpolation_order=1
)
Результат:
TypeError: аргумент float() должен быть строкой или числом, а не ‘NoneType’
Как именно я должен передавать в качестве параметра любую случайную величину масштабирования для моих изображений?
Общедоступный блокнот kaggle здесь:
https://www.kaggle.com/puelon/notebook75c416766a
Ошибка типа: в пользовательском коде:
<ipython-input-4-9ba0455797a4>:17 load *
img = tf.keras.preprocessing.image.random_zoom(img, zoom_range, row_axis=0, col_axis=1, channel_axis=2, fill_mode='nearest')
/opt/conda/lib/python3.7/site-packages/keras_preprocessing/image/affine_transformations.py:153 random_zoom *
x = apply_affine_transform(x, zx=zx, zy=zy, channel_axis=channel_axis,
/opt/conda/lib/python3.7/site-packages/keras_preprocessing/image/affine_transformations.py:321 apply_affine_transform *
transform_matrix = transform_matrix_offset_center(
/opt/conda/lib/python3.7/site-packages/keras_preprocessing/image/affine_transformations.py:246 transform_matrix_offset_center *
o_x = float(x) / 2 0.5
/opt/conda/lib/python3.7/site-packages/tensorflow/python/autograph/operators/py_builtins.py:195 float_ **
return _py_float(x)
/opt/conda/lib/python3.7/site-packages/tensorflow/python/autograph/operators/py_builtins.py:206 _py_float
return float(x)
TypeError: float() argument must be a string or a number, not 'NoneTyp
e'
TypeError Traceback (most recent call last)
<ipython-input-4-9ba0455797a4> in <module>
27 train1, train2, test1 = d
28 train_ds = tf.data.Dataset.from_tensor_slices(train1 train2).
---> 29 shuffle(len(train1) len(train2)).map(load).batch(4)
30 test_ds = tf.data.Dataset.from_tensor_slices(test1).
31 shuffle(len(test1)).map(load).batch(4)
for i in range(len(groups)):
d = deque(groups)
d.rotate(i)
train1, train2, test1 = d
train_ds = tf.data.Dataset.from_tensor_slices(train1 train2).
shuffle(len(train1) len(train2)).map(load).batch(4)
test_ds = tf.data.Dataset.from_tensor_slices(test1).
shuffle(len(test1)).map(load).batch(4)
Комментарии:
1. Можете ли вы предоставить полный стек исключений? Не только последняя строка сообщения.
Ответ №1:
Вероятно, у вас img
неправильный тип объекта. Для random_zoom(...)
функции вам необходимо предоставить входные данные в виде тензорного или трехмерного numpy
массива формы (height, width, channels)
, т.Е. Для RGB-изображения размером 300×200 массив должен иметь форму (200, 300, 3). Такой массив numpy можно получить, например, с помощью библиотеки PIL, как в приведенном ниже коде.
Кроме того, если у вас есть TF-код, то вы имеете дело с тензорами, но random_zoom
должны знать все измерения, их целочисленные размеры. Тензоры могут иметь None
размер для некоторых измерений, если он неизвестен во время построения графика, и, вероятно, это вызывает ошибку NoneType
в вашем случае. Чтобы преодолеть это, вам нужно перенести random_zoom
использование в интерфейс функций numpy, это заставит вводить функции в виде массива numpy вместо тензора, а массивы numpy всегда имеют все размеры с известными размерами. Я также реализовал это перенос в свой код ниже.
Также вам, вероятно, нужно изменить row_axis=1, col_axis=2, channel_axis=0
row_axis=0, col_axis=1, channel_axis=2
, потому что каналы (цвета) обычно идут в наименее значимом измерении (последнем).
Документация для tf.keras.preprocessing.image.random_soom.
Далее я реализовал простой код, который работает.
Ввод в коде выглядит следующим образом:
вывод выглядит следующим образом:
Следующий код также можно запустить здесь онлайн.
# Needs: python -m pip install tensorflow numpy pillow requests
import tensorflow as tf, numpy as np, PIL.Image, requests, io
tf.compat.v1.enable_eager_execution()
zoom_range = (0.4, 0.5)
img = PIL.Image.open(io.BytesIO(requests.get('https://i.stack.imgur.com/Fc3Jb.png').content))
#img = PIL.Image.open('Ruler-Big-Icon-PNG.png')
img = np.array(img)
img = tf.convert_to_tensor(img) # This line is not needed if you already have a tensor.
# You need only this single line of code to fix your issue!
img = tf.numpy_function(lambda img: tf.keras.preprocessing.image.random_zoom(
img, zoom_range, row_axis=0, col_axis=1, channel_axis=2, fill_mode='nearest',
), [img], tf.float32)
img = np.array(img) # This line is not needed if you plan img to be a tensor futher
# Example output is https://i.stack.imgur.com/MWk9T.png
PIL.Image.fromarray(img).save('result.png')
Комментарии:
1. @JohnJones Я думаю, что знаю причину. Большинство функций TF / Keras имеют так называемую пакетную ось в позиции 0, а пакетная ось обычно имеет размер None, который показан в exception . Итак, вам нужно это исправить
row_axis=1, col_axis=2, channel_axis=3
.2. @JohnJones Я только что прочитал документ для random_zoom, и, к сожалению, вы должны учитывать, что он поддерживает только тензор ввода 3D. Это означает, что вы не можете обрабатывать пакеты. Итак, если у вас есть 4D-вывод предыдущих операций, тогда вам нужно выполнить цикл. Но подождите, у меня здесь очень медленный интернет (десятки кбит / с), и мне нужно много времени. Возможно, у вас есть 3D-тензор, но там неизвестно измерение ширины или высоты, т. Е. Его нет.
3. @JohnJones Если некоторые измерения неизвестны и отсутствуют, как в вашем случае, вам нужно обернуть строку random_zoom в kaggle в оболочку функции numpy , это сделает все измерения известными. Ужасно медленный интернет, ожидающий загрузки.
4. @JohnJones Я вставил
numpy_function
оболочку в код своего ответа, также добавил второй абзац, касающийся этого, вы можете попробовать сделать это (перенос).5. Сначала это не работало, затем я обновил страницу и увидел ваше редактирование, работает как шарм, большое вам спасибо!
Ответ №2:
Я вообще не уверен, что использование функций предварительной обработки Keras за пределами того места, где они принадлежат, является правильным подходом. Простым способом было бы использовать tf.image.random_crop
. Предполагая, что ваше изображение больше (200, 200, 3)
, вы можете просто использовать эту строку:
img = tf.image.random_crop(img, (200, 200, 3))
Давайте попробуем пример. Исходное изображение:
import tensorflow as tf
import skimage
import matplotlib.pyplot as plt
import numpy as np
X = np.stack([skimage.data.chelsea() for _ in range(10)])
ds = tf.data.Dataset.from_tensor_slices(X).
map(lambda x: tf.image.random_crop(x, (200, 200, 3)))
plt.imshow(next(iter(ds)))
plt.show()
Комментарии:
1. Возможно, только этот случай можно заменить обрезкой, но я думаю, что в целом можно смешивать код разных библиотек, добавлять несколько крошечных адаптеров. keras — это огромная классная библиотека и имеет множество функций, которые не были перенесены обратно в серверные функции TFs, такие как этот random_zoom, и, вероятно, большинство из них вообще не будут перенесены. Следовательно, полезно знать, как сделать необходимую оболочку из одной библиотеки в другую. Если вы посмотрите на код моего текущего ответа, мой адаптер просто однострочный, поэтому, вероятно, достаточно хорош. Но спасибо за вашу версию ответа.
2. Я не согласен, вы не должны использовать модули высокого уровня для таких простых операций. Это лучше всего иллюстрируется сложным обходным решением, которое вам пришлось придумать, наряду с недостатками
tf.numpy_function
. Он также в 2 раза медленнее и имеет ограничения . Он просто не предназначен для такого использования.