tf.image.decode_jpeg - 内容必须是标量,而得到的形状为 [1]。

3
我已经基于 TensorFlow Serving 构建了一个服务器/客户端示例,用于图像分类。以下是该教程链接:https://github.com/tmlabonte/tendies/blob/master/minimum_working_example/tendies-basic-tutorial.ipynb 客户端: 它接受图像作为输入,将其转换为 Base64 格式,使用 JSON 将其传递给服务器。
input_image = open(image, "rb").read()
print("Raw bitstring: " + str(input_image[:10]) + " ... " + str(input_image[-10:]))

# Encode image in b64
encoded_input_string = base64.b64encode(input_image)
input_string = encoded_input_string.decode("utf-8")
print("Base64 encoded string: " + input_string[:10] + " ... " + input_string[-10:])

# Wrap bitstring in JSON
instance = [{"images": input_string}]
data = json.dumps({"instances": instance})
print(data[:30] + " ... " + data[-10:])

r = requests.post('http://localhost:9000/v1/models/cnn:predict', data=data)
  #json.loads(r.content)
print(r.text)

服务器

一旦将模型加载为.h5文件,服务器必须保存为SavedModel。 图像必须作为Base64编码的字符串从客户端传递到服务器。

model=tf.keras.models.load_model('./model.h5')
  input_bytes = tf.placeholder(tf.string, shape=[], name="input_bytes")
#  input_bytes = tf.reshape(input_bytes, [])
    # Transform bitstring to uint8 tensor
  input_tensor = tf.image.decode_jpeg(input_bytes, channels=3)

    # Convert to float32 tensor
  input_tensor = tf.image.convert_image_dtype(input_tensor, dtype=tf.float32)
  input_tensor = input_tensor / 127.5 - 1.0

    # Ensure tensor has correct shape
  input_tensor = tf.reshape(input_tensor, [64, 64, 3])

    # CycleGAN's inference function accepts a batch of images
    # So expand the single tensor into a batch of 1
  input_tensor = tf.expand_dims(input_tensor, 0)


#  x = model.input
  y = model(input_tensor)

然后input_bytes成为SavedModel中prediction_signature的输入

 tensor_info_x = tf.saved_model.utils.build_tensor_info(input_bytes)

在最后,服务器的结果如下:
§ saved_model_cli show --dir ./ --all

signature_def['predict']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['images'] tensor_info:
        dtype: DT_STRING
        shape: ()
        name: input_bytes:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (1, 4)
        name: sequential_1/dense_2/Softmax:0
  Method name is: tensorflow/serving/predict

发送图片

当我发送base64格式的图片时,服务器返回了一个运行错误,提示输入的形状似乎不是标量:

Using TensorFlow backend.
Raw bitstring: b'\xff\xd8\xff\xe0\x00\x10JFIF' ... b'0;s\xcfJ(\xa0h\xff\xd9'
Base64 encoded string: /9j/4AAQSk ... 9KKKBo/9k=
{"instances": [{"images": "/9j ... Bo/9k="}]}
{ "error": "contents must be scalar, got shape [1]\n\t [[{{node DecodeJpeg}} = DecodeJpeg[_output_shapes=[[?,?,3]], acceptable_fraction=1, channels=3, dct_method=\"\", fancy_upscaling=true, ratio=1, try_recover_truncated=false, _device=\"/job:localhost/replica:0/task:0/device:CPU:0\"](_arg_input_bytes_0_0)]]" }

从服务器上可以看到,input_bytes的形状是标量,即shape=[],我也尝试使用tf.reshape(input_bytes, [])来改变形状,但无济于事,始终出现相同的错误。 我在互联网和Stackoverflow上都没有找到关于这个错误的解决方案。请问您能否建议如何解决这个问题? 谢谢!

2个回答

4

我解决了这个问题,现在我想评论一下如何解决,以便您可以从解决方案中受益!

当您发送像这样的JSON时:

{"instances": [{"images": "/9j ... Bo/9k="}]}

实际上,你发送的是大小为1的数组,因为你在[]中放了一个空格。 如果你想发送两张图片,应该这样写:

{"instances": [{"images": "/9j ... Bo/9k="}, {"images": "/9j ... Bo/9k="}]}

这里的尺寸是2 (形状为[2])

所以解决方案是在占位符中声明接受任何类型的大小,形状为[无]

input_bytes = tf.placeholder(tf.string, shape=[None], name="input_bytes")

如果您仅发送1张图像,则可以通过以下方式将向量1转换为标量:

input_scalar = tf.reshape(input_bytes, [])

我的代码中还有一个错误,我没有考虑到在tensorflow/serving中,有一种将base64解码的功能,可以通过在json中明确声明“b64”来实现,请参考RESTful API Encoding binary values。因此,如果您发送

{"instances": [{"images": {"b64": "/9j ... Bo/9k="}}]}

服务器将自动解码Base64输入,正确的比特流将传递到tf.image.decode_jpeg。

0
input_bytes = tf.placeholder(tf.string, shape=[], name="input_bytes")
input_tensor = tf.image.decode_jpeg(input_bytes, channels=3)

"tf.image.decode_jpeg" 只能接受标量


请考虑提供链接到资源或其他文档来支持您的答案。这样会更清晰明了。 - Azhar Khan

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