python opencv создать изображение из bytearray

Я снимаю видео с камеры Ricoh Theta V. Он передает видео в формате Motion JPEG (MJPEG) . Увы, чтобы получить видео, вам нужно выполнить HTTP POST, что означает, что я не могу использовать функцию cv2.VideoCapture(url).

Таким образом, способ сделать это для многочисленных сообщений в Интернете и SO выглядит примерно так:

bytes = bytes()
while True:
    bytes += stream.read(1024)
    a = bytes.find(b'\xff\xd8')
    b = bytes.find(b'\xff\xd9')
    if a != -1 and b != -1:
        jpg = bytes[a:b+2]
        bytes = bytes[b+2:]
        i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
        cv2.imshow('i', i)
        if cv2.waitKey(1) == 27:
            exit(0)

Это действительно работает, но медленно. Я обрабатываю поток jpeg 1920x1080. на Mac Book Pro под управлением OSX 10.12.6. Вызов imdecode занимает около 425000 микросекунд для обработки каждого изображения.

Есть идеи, как это сделать без imdecode или сделать imdecode быстрее? Я бы хотел, чтобы он работал со скоростью 60 кадров в секунду с HD-видео (по крайней мере).

Я использую Python3.7 и OpenCV4.


person Bob Woodley    schedule 25.02.2019    source источник
comment
Связанная проблема здесь   -  person Christoffer    schedule 25.02.2019


Ответы (2)


Обновлено снова

Я просмотрел декодирование JPEG из буфера памяти с помощью PyTurboJPEG, код выглядит следующим образом для сравнения с imdecode() OpenCV:

#!/usr/bin/env python3

import cv2
from turbojpeg import TurboJPEG, TJPF_GRAY, TJSAMP_GRAY

# Load image into memory
r = open('image.jpg','rb').read()
inp = np.asarray(bytearray(r), dtype=np.uint8)

# Decode JPEG from memory into Numpy array using OpenCV
i0 = cv2.imdecode(inp, cv2.IMREAD_COLOR)

# Use default library installation
jpeg = TurboJPEG()

# Decode JPEG from memory using turbojpeg
i1 = jpeg.decode(r)
cv2.imshow('Decoded with TurboJPEG', i1)
cv2.waitKey(0)

И ответ в том, что TurboJPEG в 7 раз быстрее! Это 4,6 мс против 32,2 мс.

In [18]: %timeit i0 = cv2.imdecode(inp, cv2.IMREAD_COLOR)                                           
32.2 ms ± 346 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [19]: %timeit i1 = jpeg.decode(r)                                                                
4.63 ms ± 55.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Спасибо @Nuzhny за то, что заметил это первым!

Обновленный ответ

Я провел несколько дополнительных тестов и не смог подтвердить ваше утверждение о том, что быстрее сохранить изображение на диск и прочитать его с помощью imread(), чем использовать imdecode() из памяти. Вот как я тестировал в IPython:

import cv2

# First use 'imread()'

%timeit i1 = cv2.imread('image.jpg', cv2.IMREAD_COLOR)
116 ms ± 2.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# Now prepare the exact same image in memory
r = open('image.jpg','rb').read()  
inp = np.asarray(bytearray(r), dtype=np.uint8)

# And try again with 'imdecode()'
%timeit i0 = cv2.imdecode(inp, cv2.IMREAD_COLOR)
113 ms ± 1.17 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Итак, я обнаружил, что imdecode() примерно на 3% быстрее, чем imread() на моей машине. Даже если я включу np.asarray() в тайминг, он все равно будет быстрее из памяти, чем с диска - и у меня на машине очень быстрые диски NVME со скоростью 3 ГБ / с ...

Исходный ответ

Я не проверял это, но мне кажется, что вы делаете это в цикле:

read 1k bytes
append it to a buffer
look for JPEG SOI marker (0xffdb)
look for JPEG EOI marker (0xffd9)
if you have found both the start and the end of a JPEG frame, decode it

1) Теперь большинство изображений JPEG с любым интересным содержимым, которые я видел, имеют размер от 30 до 300 КБ, поэтому вы собираетесь выполнять 30-300 операций добавления в буфер. Я мало что знаю о Python, но я думаю, что это может привести к перераспределению памяти, что, я думаю, может быть медленным.

2) Далее вы будете искать маркер SOI в первых 1 КБ, затем снова в первых 2 КБ, затем снова в первых 3 КБ, затем снова в первых 4 КБ - даже если вы уже нашел это!

3) Точно так же вы будете искать маркер EOI в первом КБ, в первых 2 КБ...

Итак, я бы посоветовал вам попробовать:

1) выделение большего буфера в начале и получение непосредственно в него по соответствующему смещению

2) не искать маркер SOI, если вы его уже нашли — например. установите его на -1 в начале каждого кадра и попытайтесь найти его только в том случае, если он все еще -1

3) искать маркер EOI только в новых данных на каждой итерации, а не во всех данных, которые вы уже искали на предыдущих итерациях

4) кроме того, на самом деле, не утруждайте себя поиском маркера EOI, если вы уже не нашли маркер SOI, потому что конец кадра без соответствующего начала не является пользы тебе все равно - она ​​неполная.

Я могу ошибаться в своих предположениях, (я был раньше!), но, по крайней мере, если они общедоступны, кто-то умнее меня может их проверить!!!

person Mark Setchell    schedule 25.02.2019
comment
Проблема с производительностью связана с cv2.imdecode. Это вопрос, на который я хочу получить ответ в любом случае. Может я не ясно выразился? Если я пишу на диск и читаю обратно с диска вместо выполнения imdecode, это происходит намного быстрее. Интересно, кто/что делает декодирование операции записи/чтения. ОС? - person Bob Woodley; 25.02.2019
comment
Когда у меня будет время, я еще раз проверю свое утверждение об imread. Но, тем не менее, по вашим же таймингам выше вы не можете получить даже 10 кадров в секунду, используя imdecode. Это не очень хорошо для видео! И мы обрабатываем видеопоток. Это предполагает, что код Python максимально быстр, что может быть не так, как вы говорите. Таким образом, imdecode снова не может обрабатывать видеопоток MJPeg со скоростью более 10 кадров в секунду. - person Bob Woodley; 28.02.2019
comment
Изображение, которое я тестировал, на самом деле имело необычно высокую энтропию. Я только что попробовал еще раз с более низкой энтропией, более нормальным изображением, и оба метода заняли около 30 мс, что соответствует 30+ кадрам в секунду. Кроме того, это просто последовательный код, если вы использовали многопоточность или многопроцессорность, вы могли бы потенциально увеличить частоту кадров, возможно, в 2-4 раза на большинстве современных многоядерных рабочих столов. - person Mark Setchell; 28.02.2019
comment
Я обрабатываю поток jpeg 1920x1080. на макбукпро. ОС X 10.12.6. Для обработки каждого изображения imdecode требуется около 425000 микросекунд. - person Bob Woodley; 28.02.2019
comment
Пожалуйста, забудьте мой комментарий о скорости чтения/записи. Это отвлекающий маневр. Я обновил свой вопрос. - person Bob Woodley; 28.02.2019

Я рекомендую использовать турбо-JPEG. Он имеет API Python: PyTurboJPEG.

person Nuzhny    schedule 25.02.2019
comment
Это может быть комментарий, а не ответ. - person Bahramdun Adil; 25.02.2019