3

我正在从 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)

这实际上有效,只是它很慢。我正在处理一个 1920x1080 的 jpeg 流。在运行 OSX 10.12.6 的 Mac Book Pro 上。调用imdecode大约需要 425000 微秒来处理每个图像

知道如何在没有imdecodeimdecode更快的情况下做到这一点吗?我希望它能够以 60FPS 的速度播放高清视频(至少)。

我正在使用 Python3.7 和 OpenCV4。

4

2 回答 2

2

再次更新

我使用 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标记,因为没有相应开始的帧结束对您来说无论如何都没用 - 它是不完整的。

我的假设可能是错误的,(我以前做过!)但至少如果它们是公开的,比我聪明的人可以检查它们!!!

于 2019-02-25T09:46:00.093 回答
0

我建议使用 turbo-jpeg。它有一个 python API:PyTurboJPEG

于 2019-02-25T06:58:57.480 回答