如何将 base64 编码的图像传递给 Tensorflow 预测?

How to pass base64 encoded image to Tensorflow prediction?

我有一个 google-cloud-ml 模型,我可以通过传递 float32 的 3 维数组来 运行 预测...

{ 'instances' [ { 'input' : '[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]' } ] }

然而,这不是传输图像的有效格式,所以我想传递 base64 编码的 png 或 jpeg。 This document 谈到这样做,但不清楚的是整个 json 对象的样子。 { 'b64' : 'x0welkja...' } 是否代替了 '[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]',使封闭的 'instances' 和 'input' 保持不变?还是其他结构?或者张量流模型是否必须在 base64 上 训练

TensorFlow 模型不必在 base64 数据上进行训练。保持你的训练图不变。但是,在导出模型时,您需要导出一个可以接受 PNG 或 jpeg(或者可能是原始数据,如果很小的话)数据的模型。然后,当您导出模型时,您需要确保使用以 _bytes 结尾的输出名称。这向 CloudML Engine 发出您将发送 base64 编码数据的信号。把它们放在一起就像这样:

from tensorflow.contrib.saved_model.python.saved_model import utils

# Shape of [None] means we can have a batch of images.
image = tf.placeholder(shape = [None], dtype = tf.string)
# Decode the image.
decoded = tf.image.decode_jpeg(image, channels=3)
# Do the rest of the processing.
scores = build_model(decoded)

# The input name needs to have "_bytes" suffix.
inputs = { 'image_bytes': image }
outputs = { 'scores': scores }
utils.simple_save(session, export_dir, inputs, outputs)

您发送的请求将如下所示:

{
    "instances": [{
        "b64": "x0welkja..."
    }]
}

如果您只是想要一种将图像发送到模型的有效方法(不一定是 base-64 编码),我建议您将图像上传到 Google 云存储,然后让您的模型读取 GCS。这样,您就不受图像大小的限制,您可以利用 GCS API 提供的多部分、多线程、可恢复上传等。

TensorFlow 的 tf.read_file 将直接关闭 GCS。这是将执行此操作的服务 input_fn 的示例。您对 CMLE 的请求将向其发送图像 URL (gs://bucket/some/path/to/image.jpg)

def read_and_preprocess(filename, augment=False):
    # decode the image file starting from the filename
    # end up with pixel values that are in the -1, 1 range
    image_contents = tf.read_file(filename)
    image = tf.image.decode_jpeg(image_contents, channels=NUM_CHANNELS)
    image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
    image = tf.expand_dims(image, 0) # resize_bilinear needs batches
    image = tf.image.resize_bilinear(image, [HEIGHT, WIDTH], align_corners=False)
    #image = tf.image.per_image_whitening(image)  # useful if mean not important
    image = tf.subtract(image, 0.5)
    image = tf.multiply(image, 2.0) # -1 to 1
    return image

def serving_input_fn():
    inputs = {'imageurl': tf.placeholder(tf.string, shape=())}
    filename = tf.squeeze(inputs['imageurl']) # make it a scalar
    image = read_and_preprocess(filename)
    # make the outer dimension unknown (and not 1)
    image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])

    features = {'image' : image}
    return tf.estimator.export.ServingInputReceiver(features, inputs)

您的训练代码将训练实际图像,就像上面 rhaertel80 的建议一样。请参阅 https://github.com/GoogleCloudPlatform/training-data-analyst/blob/master/courses/machine_learning/deepdive/08_image/flowersmodel/trainer/task.py#L27 以了解 training/evaluation 输入函数的外观。

我试图使用@Lak 的回答(感谢 Lak)在一个 json 文件中获取多个实例的在线预测,但不断出现以下错误(我的测试中有两个实例 json,因此形状 [2]):

input filename tensor must be scalar but had shape [2]

问题是 ML 引擎显然将所有实例一起批处理并将它们传递给服务 inpur 接收器函数,但@Lak 的示例代码假定输入是单个实例(如果您有单个实例,它确实可以正常工作在你的 json 中)。我更改了代码,以便它可以处理一批输入。我希望它能帮助某人:

def read_and_preprocess(filename):
    image_contents = tf.read_file(filename)
    image = tf.image.decode_image(image_contents, channels=NUM_CHANNELS)
    image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
    return image

def serving_input_fn():
    inputs = {'imageurl': tf.placeholder(tf.string, shape=(None))}
    filename = inputs['imageurl']
    image = tf.map_fn(read_and_preprocess, filename, dtype=tf.float32)
    # make the outer dimension unknown (and not 1)
    image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])

    features = {'image': image}
    return tf.estimator.export.ServingInputReceiver(features, inputs)

关键的变化是 1) 你没有 squeeze 输入张量(在你的 json 只包含一个实例的特殊情况下会造成麻烦)和 2) 使用tf.map_fnread_and_preprocess 函数应用于一批输入图像 url。