Как использовать случайное масштабирование в keras tensorflow 2.3

#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()
  

Случайно обрезанное изображение размера (200, 200, 3) :
введите описание изображения здесь

Комментарии:

1. Возможно, только этот случай можно заменить обрезкой, но я думаю, что в целом можно смешивать код разных библиотек, добавлять несколько крошечных адаптеров. keras — это огромная классная библиотека и имеет множество функций, которые не были перенесены обратно в серверные функции TFs, такие как этот random_zoom, и, вероятно, большинство из них вообще не будут перенесены. Следовательно, полезно знать, как сделать необходимую оболочку из одной библиотеки в другую. Если вы посмотрите на код моего текущего ответа, мой адаптер просто однострочный, поэтому, вероятно, достаточно хорош. Но спасибо за вашу версию ответа.

2. Я не согласен, вы не должны использовать модули высокого уровня для таких простых операций. Это лучше всего иллюстрируется сложным обходным решением, которое вам пришлось придумать, наряду с недостатками tf.numpy_function . Он также в 2 раза медленнее и имеет ограничения . Он просто не предназначен для такого использования.