Работа с изображением opencv (байты, закодированные как jpg) в API Flask

Мой вызов

Мне нужно отправлять и получать изображения с помощью Flask для обнаружения объектов в реальном времени с более чем 1000 потоков камер.

Часть 1: Отправка моих видеокадров в API

Я не хочу сохранять кадры изображений на своем диске, в настоящее время у меня нет файлов .png/.jpg для отправки в API Flask. У меня уже есть данные изображения в памяти как numpy.array (Я использую cv2.VideoCapture() для извлечения кадров из видеопотоков).

Как я могу отправить эти байты numpy.array в API Flask?

В настоящее время я пытаюсь закодировать изображение с помощью cv2.imencode(), преобразовать его в байты, а затем закодировать с помощью base64.

### frame is a numpy.array with an image
img_encoded = cv2.imencode('.jpg', frame)[1]
img_bytes = img_encoded.tobytes()

Итак, img_bytes это что-то вроде этого:

b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x02\x01\x01\x01\x01\x01\x02\x01\x01\x01\x02\x02\x02\x02\x02\x04\x03\x02\x02\x02\x02\x05\x04\x04\x03'

Я пытаюсь отправить img_bytes на свой requests.post():

files = {'imageData': img_bytes}
res = requests.post('http://127.0.0.1/image', files=files)

Я получаю <Response [200]>, но API не работает должным образом. Я считаю, что мой сервер Flask плохо справляется с этими закодированными байтами... Так что моя проблема, вероятно, связана со второй частью.

Часть 2: Получение байтов изображения на моем сервере Flask и преобразование его в PIL.Image

Я определил функцию predict_image(), которая получает вывод PIL.Image.open() и выполняет все задачи по обнаружению объектов.

Моя проблема в том, что моя переменная imageData, по-видимому, не может быть правильно открыта с помощью PIL.Image.open(). type() из imageData равно <class 'werkzeug.datastructures.FileStorage'>.

В этом фрагменте ниже мой веб-сервис извлекает изображение, полученное по запросу, и выполняет по нему обнаружение объекта predict_image():

def predict_image_handler(project=None, publishedName=None):
    try:
        imageData = None
        if ('imageData' in request.files):
            imageData = request.files['imageData']
        elif ('imageData' in request.form):
            imageData = request.form['imageData']
        else:
            imageData = io.BytesIO(request.get_data())

        img = Image.open(imageData)
        results = predict_image(img)
        return jsonify(results)

    except Exception as e:
        print('EXCEPTION:', str(e))
        return 'Error processing image', 500

Я не получаю сообщение об ошибке при отправке изображений в API, но API не работает должным образом. Я считаю, что это не правильное преобразование байтов обратно в изображение.

Что мне не хватает в этом коде? Что мне нужно сделать с объектом imageData, прежде чем открывать его с помощью PIL.Image.open()?


person Renan V. Novas    schedule 01.08.2019    source источник
comment
Некоторые другие детали. У меня есть набор виртуальных машин в Azure, которые подключены к нескольким потокам камер (более 1000 камер наблюдения). У меня есть цель эффективно выполнять обнаружение объектов (стоимостью менее 12 долларов США за камеру в месяц) с частотой 2 кадра в секунду (поэтому мне нужно обработать 2000 в моем API обнаружения объектов). Я создаю контейнер Docker для работы в кластере Kubernetes. Я не хочу сохранять кадры на диске из-за ограничений по стоимости, у меня были бы дополнительные затраты времени на обработку, если бы я сохранял изображения на диск перед их обработкой.   -  person Renan V. Novas    schedule 01.08.2019


Ответы (2)


Вы отправляете img_encoded или img_bytes в «imageData»? Я действительно думаю, что отправка img_bytes как base64 может вызвать подобное поведение. Структуры данных werkzeug. FileStorage должен проксировать методы потока, но пробовали ли вы использовать imageData.stream?

person Zenithpolarus    schedule 01.08.2019
comment
Да, я пытался открыть imageData.stream методом Image.open(). Ошибка осталась. - person Renan V. Novas; 01.08.2019
comment
Я пытаюсь отправить img_bytes в «imageData». Спасибо, что выделили это, я исправил фрагмент кода по моему вопросу выше. - person Renan V. Novas; 01.08.2019
comment
Я согласен, что base64 может это исправить, но я пытался использовать base64 со структурами werkzeug.data, и мне не удалось заставить его работать правильно. - person Renan V. Novas; 01.08.2019

У меня была небольшая ошибка в коде, я пытался отправить в сообщение img_encoded вместо img_bytes. Теперь все работает.

Часть 1: создание запроса

### frame is a numpy.array with an image
img_encoded = cv2.imencode('.jpg', frame)[1]
img_bytes = img_encoded.tobytes()

files = {'imageData': img_bytes}
response = requests.post(url,
files=files)

Часть 2: обработка полученных байтов

def predict_image_handler(project=None, publishedName=None):
    try:
        imageData = None
        if ('imageData' in request.files):
            imageData = request.files['imageData']
        elif ('imageData' in request.form):
            imageData = request.form['imageData']
        else:
            imageData = io.BytesIO(request.get_data())

        img = Image.open(imageData)
        results = predict_image(img)
        return jsonify(results)

    except Exception as e:
        print('EXCEPTION:', str(e))
        return 'Error processing image', 500
person Renan V. Novas    schedule 01.08.2019