tf.image.decode_jpeg - содержимое должно быть скалярным, получило форму [1]

1

Я создал демо сервер/клиент для классификации изображений по тензорным потокам, следуя этому руководству 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 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 становятся входными данными для predition_signature в SavedModel

 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, об этой ошибке. Подскажите, пожалуйста, как это исправить? Спасибо!

Теги:
tensorflow
tensorflow-serving

1 ответ

0
Лучший ответ

Я решил проблему, и я хотел бы прокомментировать, как вы можете извлечь выгоду из решения!

Когда вы отправляете JSON, как это:

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

на самом деле вы отправляете массив размера 1, когда вы помещаете [] на случай, если вы хотите отправить 2 изображения, которые вы должны написать так

{"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, [])

Также в моем коде была другая ошибка, я не учел, что в тензорном потоке/обслуживании есть функция для декодирования base64, явно указав "b64" в json, так что если вы отправляете

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

сервер автоматически декодирует ввод base64, и правильный поток битов достигнет tf.image.decode_jpeg

Ещё вопросы

Сообщество Overcoder
Наверх
Меню