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.