如何在Keras TensorFlow 2.3中使用随机缩放?

4
我正试图为分辨率为128x160、通道数为1的tiff文件添加随机缩放,但是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
)

输出为:

类型错误:float()参数必须是字符串或数字,而不是'NoneType'

如何将任意随机缩放量作为参数传递给我的图像?
公共 Kaggle 笔记本在此处:

https://www.kaggle.com/puelon/notebook75c416766a

TypeError:用户代码中发生错误:
    <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)

你能提供完整的异常堆栈吗?不只是最后一行错误信息。 - Arty
2个回答

3
可能你的img对象类型错误。对于random_zoom(...)函数,您需要提供输入张量或3D numpy数组,其形状为(height, width, channels),即对于大小为300x200的RGB图像,数组应该是形状为(200, 300, 3)。可以通过PIL库获得这种类型的numpy数组,例如下面的代码。
此外,如果您正在使用TF代码,则正在处理张量,但是random_zoom需要知道所有维度及其整数大小。如果在图形构建时不知道某些维度,则张量可能具有None大小,并且可能会导致您的情况下出现关于NoneType的错误。为了克服这个问题,您需要将random_zoom使用包装到numpy function interface中,这将强制函数输入成为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_zoom的文档。
我实现了下面的简单代码,它可以工作。
代码中的输入如下:

input

输出结果如下:

input

下一段代码也可以在这里在线运行

# 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://istack.dev59.com/Fc3Jb.webp').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://istack.dev59.com/MWk9T.webp
PIL.Image.fromarray(img).save('result.png')

是的,当我这样调用函数时:img = tf.convert_to_tensor(img),其中函数定义为def np_random_zoom(img): return tf.keras.preprocessing.image.random_zoom( img, zoom_range, row_axis=0, col_axis=1, channel_axis=2, fill_mode='nearest', )。错误已经消失了。 - user11597888
我尝试将其作为tf.data.Dataset的一部分,但不幸的是出现了“OperatorNotAllowedInGraphError:不允许迭代tf.Tensor:AutoGraph确实转换了此函数。这可能表明您正在尝试使用不受支持的功能。” - kawingkelvin
添加: 看起来它不喜欢我从选择的缩放变换中将zx、zy输出到Python函数外部,以便我可以在地标标签上执行相应的变换。这似乎有些棘手,超出了这个问题的范围,也许需要单独询问。 - kawingkelvin
@Arty 我暂时决定不使用numpy函数,而是使用现有的tf图像API,例如sample_distorted_bounding_box和pad_to_bounding_box,其中特定配置r有效地实现了random_zoom。 - kawingkelvin
通过使用基本的tf图像API进行添加,我拥有更多的灵活性,不一定需要将“缩放”始终居中(在一个操作中执行翻译+缩放)。 - kawingkelvin
显示剩余9条评论

1
我一般不认为在Keras预处理函数的范围之外使用它们是正确的方法。一个简单的方法是使用tf.image.random_crop。假设你的图像大于(200, 200, 3),你可以只使用这一行代码:
img = tf.image.random_crop(img, (200, 200, 3))

让我们尝试一个例子。原始图片:
enter image description here

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)的随机裁剪图像: 在此输入图像描述


也许这种情况可以用crop来替换,但我认为通常混合使用不同库的代码并添加一些微小的适配器是可以的。keras是一个非常棒的库,有许多功能没有被移植到TF的后端函数中,比如这个random_zoom,可能大部分都不会被移植。因此,了解如何从一个库制作必要的包装器是很好的。如果你看一下我的当前答案代码,我的适配器只有一行,所以可能已经足够好了。但还是谢谢你的回答版本。 - Arty
我不同意,你不应该为这样简单的操作使用高级模块。这最好通过你不得不想出的复杂解决方案以及tf.numpy_function的缺点来说明。它还要慢2倍,并且带有限制。它只是不适合这样使用。 - Nicolas Gervais

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接