python opencv从bytearray创建图像
Posted
技术标签:
【中文标题】python opencv从bytearray创建图像【英文标题】:python opencv create image from bytearray 【发布时间】:2019-02-25 05:54:46 【问题描述】:我正在使用 Ricoh Theta V 相机拍摄视频。 It delivers the video as 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)
这确实有效,只是速度很慢。我正在处理一个 1920x1080 的 jpeg 流。在运行 OSX 10.12.6 的 Mac Book Pro 上。对imdecode
的调用大约需要 425000 微秒来处理每个图像
知道如何在没有imdecode
的情况下执行此操作或使imdecode
更快吗?我希望它能够以 60FPS 的速度播放高清视频(至少)。
我正在使用 Python3.7 和 OpenCV4。
【问题讨论】:
相关问题here 【参考方案1】:再次更新
我使用 PyTurboJPEG 从内存缓冲区中查看了 JPEG 解码,代码如下与 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()
在我的机器上比 imread()
快 3% 左右。即使我将np.asarray()
包含在计时中,它仍然比内存更快——而且我的机器上有非常快的 3GB/s 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 图像都在 30kB 到 300kB 之间,因此您将在缓冲区上执行 30-300 次附加操作。我对 Python 了解不多,但我猜这可能会导致重新分配内存,我猜这可能会很慢。
2) 接下来,您将在前 1kB 中查找 SOI 标记,然后在前 2kB 中再次查找,然后在前 3kB 中再次查找,然后在前 4kB 中再次查找 - 即使你已经找到了!
3) 同样,您将在前 1kB、前 2kB 中寻找 EOI 标记...
所以,我建议你试试:
1) 在开始时分配一个更大的缓冲区并在适当的偏移量处直接获取到它
2) 如果您已经找到了 SOI 标记,则不要搜索它 - 例如在每一帧的开头将它设置为-1
,并且只有在它仍然是-1
时才尝试找到它
3) 仅在每次迭代的新数据中查找 EOI 标记,而不是在之前迭代中已经搜索过的所有数据中
4) 此外,实际上,除非您已经找到 SOI 标记,否则不必费心寻找 EOI 标记,因为帧的结尾没有相应的无论如何 start 对你没有用 - 它是不完整的。
我的假设可能是错误的,(我以前曾经做过!)但至少如果它们是公开的,比我更聪明的人可以检查它们!!!
【讨论】:
cv2.imdecode
的性能问题。这是我无论如何都想回答的问题。也许我没有说清楚?如果我写入磁盘并从磁盘读回而不是 imdecode
它会快得多。我想知道谁/什么在对写/读操作进行解码。操作系统?
当我有时间时,我会仔细检查我关于 imread 的声明。但是尽管如此,根据您自己的上述时间,您甚至无法使用 imdecode 获得每秒 10 帧。这对视频来说不是很好!我们正在处理一个视频源。这是假设 python 代码尽可能快,它可能不像你说的那样快。因此,imdecode 再次无法以超过 10 FPS 的速度处理 MJPeg 视频流
我正在测试的图像实际上具有异常高的熵。我只是用更低的熵、更正常的图像再次尝试,这两种方法都花费了大约 30 毫秒,对应于 30+ fps。此外,这只是顺序代码,如果您使用多线程或多处理,则在大多数现代多核桌面上,您可能会将帧速率提高 2-4 倍。
我正在处理一个 1920x1080 的 jpeg 流。在 macbookpro 上。 OSX 10.12.6。处理每张图像需要 imdecode 大约 425000 微秒。
请忘记我关于阅读/写作更快的评论。那是一条红鲱鱼。我已经更新了我的问题。【参考方案2】:
我建议使用 turbo-jpeg。它有一个 python API:PyTurboJPEG。
【讨论】:
这可以是评论而不是答案。以上是关于python opencv从bytearray创建图像的主要内容,如果未能解决你的问题,请参考以下文章
Python学习笔记015——序列(字节数组 bytearray)
python序列类型:strbytes bytearray list tuple range