首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >FFmpeg和video4linux2参数-如何更快地捕获静止图像?

FFmpeg和video4linux2参数-如何更快地捕获静止图像?
EN

Stack Overflow用户
提问于 2021-08-10 23:16:00
回答 3查看 1.4K关注 0票数 0

问题摘要

我已经建立了一个18相机阵列的USB网络摄像头,连接到一个树莓Pi 400作为控制器。我的Python3.8从每个摄像头获取图像的代码很慢,我正在设法加快速度。

FFMPEGvideo4linux2命令行选项使我感到困惑,因此我不确定延迟是否是由于我对参数选择不当造成的,更好的选项集将解决这个问题。

目标

我正试图尽快从每台相机上捕捉到一张图像。

我正在使用video4linux2、FFMPEG、和命令行选项来捕获所有相机的循环中的每个图像,如下所示。

预期结果

我只想从每台相机里得到一个画面。帧速率是30 fps,所以我原以为捕获时间是第二个最坏情况的1/30到1/10。但是性能计时器告诉我每次捕获都需要2-3秒。

此外,我并不真正理解ffmpeg输出,但是这个输出让我担心:

代码语言:javascript
复制
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    1 fps=0.5 q=8.3 Lsize=N/A time=00:00:00.06 bitrate=N/A speed=0.0318x    
video:149kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing  

我不明白为什么"frame=“这一行被重复了4次。在第四次重复中,fps表示0.5,我将其解释为每2秒一帧,而不是我指定的30 fps。

特定问题:

有人能向我解释一下这个ffmpeg输出是什么意思吗?为什么每拍一张图像要花2秒,而不是接近1/30秒?

有谁能向我解释如何在每次捕获的时间更短的时间内捕获图像?

我是否应该为每个ffmpeg调用生成一个单独的线程,以便它们异步运行,而不是串行运行?或者这样做不能在实践中真正节省时间吗?

实际结果

代码语言:javascript
复制
  Input #0, video4linux2,v4l2, from '/dev/video0':
  Duration: N/A, start: 6004.168748, bitrate: N/A
    Stream #0:0: Video: mjpeg, yuvj422p(pc, bt470bg/unknown/unknown), 1920x1080, 30 fps, 30 tbr, 1000k tbn, 1000k tbc
Stream mapping:
  Stream #0:0 -> #0:0 (mjpeg (native) -> mjpeg (native))
Press [q] to stop, [?] for help
Output #0, image2, to '/tmp/video1.jpg':
  Metadata:
    encoder         : Lavf58.20.100
    Stream #0:0: Video: mjpeg, yuvj422p(pc), 1920x1080, q=2-31, 200 kb/s, 30 fps, 30 tbn, 30 tbc
    Metadata:
      encoder         : Lavc58.35.100 mjpeg
    Side data:
      cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    1 fps=0.5 q=8.3 Lsize=N/A time=00:00:00.06 bitrate=N/A speed=0.0318x    
video:149kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown

Captured /dev/video0 image in: 3 seconds
Input #0, video4linux2,v4l2, from '/dev/video2':
  Duration: N/A, start: 6007.240871, bitrate: N/A
    Stream #0:0: Video: mjpeg, yuvj422p(pc, bt470bg/unknown/unknown), 1920x1080, 30 fps, 30 tbr, 1000k tbn, 1000k tbc
Stream mapping:
  Stream #0:0 -> #0:0 (mjpeg (native) -> mjpeg (native))
Press [q] to stop, [?] for help
Output #0, image2, to '/tmp/video2.jpg':
  Metadata:
    encoder         : Lavf58.20.100
    Stream #0:0: Video: mjpeg, yuvj422p(pc), 1920x1080, q=2-31, 200 kb/s, 30 fps, 30 tbn, 30 tbc
    Metadata:
      encoder         : Lavc58.35.100 mjpeg
    Side data:
      cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    1 fps=0.5 q=8.3 Lsize=N/A time=00:00:00.06 bitrate=N/A speed=0.0318x    
video:133kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown

Captured /dev/video2 image in: 3 seconds
...

代码:

代码语言:javascript
复制
list_of_camera_ids = ["/dev/video1","/dev/video2", "/dev/video3", "/dev/video4",
                      "/dev/video5","/dev/video6", "/dev/video7", "/dev/video8",
                      "/dev/video9","/dev/video10", "/dev/video11", "/dev/video12",
                      "/dev/video13","/dev/video14", "/dev/video15", "/dev/video16",
                      "/dev/video17","/dev/video18"
                     ]
for this_camera_id in list_of_camera_ids:
    full_image_file_name = '/tmp/' + os.path.basename(this_camera_id) + 'jpg'
    image_capture_tic = time.perf_counter()
    
    run_cmd = subprocess.run([
                              '/usr/bin/ffmpeg', '-y', '-hide_banner',
                              '-f', 'video4linux2',
                              '-input_format',  'mjpeg',
                              '-framerate', '30',
                              '-i', this_camera_id,
                              '-frames', '1',
                              '-f', 'image2',
                              full_image_file_name
                             ],
                             universal_newlines=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE
                            )  
         print(run_cmd.stderr)
         image_capture_toc = time.perf_counter()       
         print(f"Captured {camera_id} image in: {image_capture_toc - image_capture_tic:0.0f} seconds")

附加数据:针对Mark的回答,即需要更多信息来回答这个问题,我现在在这里详细说明所要求的信息:

摄像机:相机是USB-3摄像机,它们将自己标识为:

代码语言:javascript
复制
idVendor           0x0bda Realtek Semiconductor Corp.
idProduct          0x5829 

我试图为其中一个摄像机添加冗长的lsusb转储,但是这个帖子超过了30000字符的限制。

相机的连接方式: USB 3端口Pi到主USB-3 7端口集线器,3支路7端口集线器(并非所有的端口都被占用)。

相机分辨率:高清格式1920x1080

如果我只想要一个图像,为什么我要设置一个帧速率??

我设置了一个帧速率,这看起来很奇怪,因为它指定了帧之间的时间,但是您只需要一个帧。我这么做是因为我不知道如何从FFMPEG获得一个图像。这是我在网上讨论过的FFMPEG命令选项的一个例子,我可以获得它来成功捕获单个图像。我发现了无数的不起作用的选项!我写这篇文章是因为我的网络搜索没有给出一个对我有用的例子。我希望一个比我更有见地的人能向我展示一种行之有效的方法!

,为什么我要按顺序而不是平行地扫描摄像机呢?

我这么做只是为了先保持简单,然后循环一下列表看起来很简单,而且是奏鸣曲。我很清楚,以后我可能能够为每个FFMPEG调用生成一个单独的线程,并可能以这种方式获得并行的速度。事实上,我欢迎一个如何这样做的例子。

但无论如何,用3秒拍摄的单个图像似乎太长了。

为什么我只使用你的覆盆子上的4个核心中的一个?

我发布的示例代码只是我整个程序的一个片段。当前,图像捕获在子线程中进行,而带有事件循环的窗口GUI则在主线程中运行,因此在成像过程中不会阻塞用户输入。

对于Raspberry Pi 400的内核,或者Raspberry Pi OS (又名Raspbian)如何管理线程分配给内核,或者Python是否能够或应该显式地指示线程在特定内核中运行,我都不太了解。

我欢迎Mark (或其他熟悉这些问题的人)的建议,推荐最佳实践并包括示例代码。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-08-12 22:18:12

首先,感谢https://stackoverflow.com/users/1109017/llogan在下面的评论中为我提供了所需的线索。

我在这里记录这个解决方案,以便于其他可能不会阅读评论的人发现。

以下是我修订的计划:

代码语言:javascript
复制
list_of_camera_ids = ["/dev/video1","/dev/video2", "/dev/video3", "/dev/video4",
                      "/dev/video5","/dev/video6", "/dev/video7", "/dev/video8",
                      "/dev/video9","/dev/video10", "/dev/video11", "/dev/video12",
                      "/dev/video13","/dev/video14", "/dev/video15", "/dev/video16",
                      "/dev/video17","/dev/video18"
                     ]
for this_camera_id in list_of_camera_ids:
    full_image_file_name = '/tmp/' + os.path.basename(this_camera_id) + 'jpg'
    image_capture_tic = time.perf_counter()
    
    run_cmd = subprocess.run([
                              'v4l2-ctl','-d',
                              this_camera_id,
                              '--stream-mmap', 
                              '--stream-count=1',
                              '--stream-to=' +
                              full_image_file_name,"&"
                             ],
                             universal_newlines=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE
                            )  
         print(run_cmd.stderr)
         image_capture_toc = time.perf_counter()       
         print(f"Captured {camera_id} image in: {image_capture_toc - image_capture_tic:0.0f} seconds")

附加注释:,这个代码是一个实质性的加速!

用我以前的方法,每个图像需要3到4秒才能捕捉到。在序列化的循环中,如最初的文章所示,18个图像通常需要45到60秒才能完成。

使用我修改的代码,使用llogan的建议,现在每台摄像机的捕获时间不到1秒。此外,简单地通过在命令中添加一个"&“来生成背景中的每一个,它们就会自动并行运行,而18个摄像机的总时间现在大约是10秒,所以现在每个摄像机的平均时间大约是.55秒。

我怀疑我可能会因为使用"&“方法并行化而产生一些额外的开销。如果我只需要生成线程,而不是完整的进程,那么其中的一些可能会进一步减少。但是这是一个我还没有经验的性能调优级别。

票数 0
EN

Stack Overflow用户

发布于 2021-08-11 20:16:38

您可以使用v4l2-ctl直接捕获帧:

代码语言:javascript
复制
v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=1 --stream-to=output.jpg

您的输入已经是JPG,因此它将捕获它。

有关更多信息,请参见v4l2-ctl --help-streaming

票数 2
EN

Stack Overflow用户

发布于 2021-08-11 00:27:18

这里有很多问题:

  • ,你没有说你用的是什么摄像头,

  • ,以及它们是如何连接的(USB2/3端口)?USB集线器?
  • 或分辨率
  • 您设置的帧速率看起来很奇怪,因为它指定帧间的时间,但是您只需要一个帧
  • ,您只需要顺序扫描相机,而不是并行的
  • ,您只使用Raspberry Pi

F 213上的4个核心中的一个。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68734375

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档