Python OpenCV создать изображение из Bytearray

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

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

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 Быстрее? Я хотел бы, чтобы он работал на 60FPS с HD-видео (по крайней мере).

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

2 ответа

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

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

#!/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() Что касается времени, то он все же быстрее из памяти, чем с диска - и у меня на моей машине серьезно быстрые 3Гб / с диски NVME...

Оригинальный ответ

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

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 в первых 1 КБ, первых 2 КБ...

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

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

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

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

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

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

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

Другие вопросы по тегам