OpenCV+V4L实现MJPG格式拉取USB摄像头

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

最近在做一个项目,由于条件限制不能使用RTSP摄像头,所以采用了USB摄像头。由于USB摄像头本身的限制使得它的YUV编码帧率受限,在1920X1080的分辨率下只有可怜的5帧,不能满足要求。我继而转向MJPG,虽然图像质量损失了一些,帧率可以达到30FPS,能够满足要求。

系统:Ubuntu18.04
CPU:aarch64或x86_64
OpenCV:4.5.1


一、为什么YUV帧数低?

我们的摄像头,或者市面上好多类似摄像头都是基于USB2的,USB2的带宽是480Mb/s,算起来就是60MB/s,一张1920X1080的原图大概是3MB左右了,受限于带宽,肯定帧数不会太高。

二、OpenCV+V4l编译

1.安装必要环境

sudo apt-get install -y \
        build-essential \
        cmake \
        git \
        gfortran \
        libatlas-base-dev \
        libavcodec-dev \
        libavformat-dev \
        libavresample-dev \
        libcanberra-gtk3-module \
        libdc1394-22-dev \
        libeigen3-dev \
        libglew-dev \
        libgstreamer-plugins-base1.0-dev \
        libgstreamer-plugins-good1.0-dev \
        libgstreamer1.0-dev \
        libgtk-3-dev \
        libjpeg-dev \
        libjpeg8-dev \
        libjpeg-turbo8-dev \
        liblapack-dev \
        liblapacke-dev \
        libopenblas-dev \
        libpng-dev \
        libpostproc-dev \
        libswscale-dev \
        libtbb-dev \
        libtbb2 \
        libtesseract-dev \
        libtiff-dev \
        libv4l-dev \
        libxine2-dev \
        libxvidcore-dev \
        libx264-dev \
        pkg-config \
        python-dev \
        python-numpy \
        python3-dev \
        python3-numpy \
        python3-matplotlib \
        qv4l2 \
        v4l-utils \
        v4l2ucp \
        zlib1g-dev

上面是我需要的环境,可以根据实际需要删减,我是同时支持ffmpeg+gstreamer+v4l的。如果不想折腾,空间又够的话索性就一次性全装了。

2.下载源代码

OpenCV使用的是4.5.1版本,其他版本请自行测试。我们需要到OpenCV官网下载OpenCV的源代码。

3.开始编译


cmake -D CMAKE_BUILD_TYPE=RELEASE \
           -D CMAKE_INSTALL_PREFIX=/usr/local \
		   -D ENABLE_PRECOMPILED_HEADERS=OFF \
           -D INSTALL_C_EXAMPLES=OFF \
           -D INSTALL_PYTHON_EXAMPLES=OFF \
		   -D BUILD_opencv_python2=OFF \
           -D BUILD_opencv_python3=OFF \
           -D WITH_V4L=ON \
		   -D WITH_LIBV4L=ON  ..

上面是只增加了V4L的库,需要其他的可以自己加上去,比如ffmpeg、gstreamer、cuda等等可以移步我的其他帖子,里面有详细教程,保证成功。

3.测试代码

在此之前你需要先看下摄像头支持哪些参数。这个地方需要使用v4l2-ctl工具,这个工具子啊v4l-utils里面,如果没有的话使用下面的命令安装

sudo apt install -y v4l-utils

接下来看看摄像头支持的流类型,你需要知道自己的摄像头是第几个,如果你只有一个USB摄像头那一般就是/dev/video0,有些系统起始可能不是0(比如RK3399),这个时候你需要使用ffmpeg去获取下摄像头的信息了,如果有信息就说明对了,这里以/dev/video0来示范

ffmpeg -i /dev/video0

正确的话会正常打印信息,报错的话就可能是找错了摄像头。假设是/dev/video0,那么执行下面的命令查看摄像头支持的数据格式

v4l2-ctl -d /dev/video0  --list-formats

执行成功会出现结果,下面的是我的USB摄像头,我是支持YUYV核MJPG的

ioctl: VIDIOC_ENUM_FMT
        Index       : 0
        Type        : Video Capture
        Pixel Format: 'YUYV'
        Name        : YUYV 4:2:2

        Index       : 1
        Type        : Video Capture
        Pixel Format: 'MJPG' (compressed)
        Name        : Motion-JPEG

这个时候需要细看下每种格式都支持哪些分辨率核帧率输入下面的命令

v4l2-ctl -d /dev/video0 --list-formats-ext

出现下面的结果

ioctl: VIDIOC_ENUM_FMT
        Index       : 0
        Type        : Video Capture
        Pixel Format: 'YUYV'
        Name        : YUYV 4:2:2
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 800x600
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 960x540
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.200s (5.000 fps)

        Index       : 1
        Type        : Video Capture
        Pixel Format: 'MJPG' (compressed)
        Name        : Motion-JPEG
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 352x288
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 800x600
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1024x768
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x1024
                        Interval: Discrete 0.033s (30.000 fps)

由于YUYV本身数据很大,在1920X1080的分辨率下只有可怜的5FPS,明显不能满足一些要求高的场景。而MJPG在1920X1080的分辨率下“居然”有30FPS,为什么加引号,注意看’MJPG’旁边的括号(compressed),因为数据压缩了,图像质量损失了,所以这算是一种折中方案吧。

接下来看看你的USB摄像头支持什么附加属性,只需要执行下面的命令即可

 v4l2-ctl -d /dev/video0 --list-ctrls

得到下面的结果

                     brightness 0x00980900 (int)    : min=-64 max=64 step=1 default=0 value=0
                       contrast 0x00980901 (int)    : min=0 max=100 step=1 default=45 value=45
                     saturation 0x00980902 (int)    : min=0 max=100 step=1 default=70 value=70
                            hue 0x00980903 (int)    : min=-180 max=180 step=1 default=0 value=0
 white_balance_temperature_auto 0x0098090c (bool)   : default=1 value=1
                          gamma 0x00980910 (int)    : min=100 max=500 step=1 default=300 value=300
                           gain 0x00980913 (int)    : min=1 max=128 step=1 default=60 value=60
           power_line_frequency 0x00980918 (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature 0x0098091a (int)    : min=2800 max=6500 step=10 default=4600 value=4600 flags=inactive
                      sharpness 0x0098091b (int)    : min=0 max=100 step=1 default=85 value=93
         backlight_compensation 0x0098091c (int)    : min=0 max=2 step=1 default=0 value=1
                  exposure_auto 0x009a0901 (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute 0x009a0902 (int)    : min=50 max=10000 step=1 default=166 value=166 flags=inactive
         exposure_auto_priority 0x009a0903 (bool)   : default=0 value=1

注意,这是我的摄像头的可操作参数,不同的摄像头可能不一样

简单描述下,右边的min是每项的最小值,max是最大值,defalut是默认值,value是当前值。后面我们设置后value会改变。具体每项怎么设置其实方法都一样,只不过设置之前最好研究下每项的意思,要不然可能出来不好的效果。下面我们用亮度来模拟下,将亮度设为1

v4l2-ctl -d /dev/video0 --set-ctrl brightness=1

没有报错就是成功了,用下面的命令验证下

v4l2-ctl -d /dev/video0 --get-ctrl brightness
brightness: 1 	

brightness: 1 就是现在的值,说明设置成功了,改回来的话使用同样的方法,最好每次都验证下是否成功,虽然我还没有遇到失败的情况

接下来上C++代码来具体设置下,这里不演示Python的代码了,大同小异。

注意,有些设置对顺序有要求,如果不太懂的话就使用我的顺序就行了

C++代码

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;

int main() {
    VideoCapture capture(0,CAP_V4L);//0是/dev/video0 是几就选几 第二个一定要选CAP_V4L,其他的会报错
    capture.set(CAP_PROP_FRAME_WIDTH, 1920); //帧宽根据自己的摄像头实际参数
    capture.set(CAP_PROP_FRAME_HEIGHT, 1080);//帧高根据自己的摄像头实际参数
    capture.set(CAP_PROP_FPS, 30);//帧率
    capture.set(CAP_PROP_BACKLIGHT, 0);//补光 0 不补光 1 补光强度1 2 补光强度2 (以相机为准)
    capture.set(CAP_PROP_BRIGHTNESS,0);//亮度
    capture.set(CAP_PROP_CONTRAST,45);//对比度
    capture.set(CAP_PROP_SATURATION,70);//饱和度
    capture.set(CAP_PROP_HUE,0);//色相
    capture.set(CAP_PROP_GAMMA,300);//伽马
    capture.set(CAP_PROP_GAIN,60);//增益
    capture.set(CAP_PROP_SHARPNESS,93);//锐度
    capture.set(CAP_PROP_WB_TEMPERATURE,4600);//白平衡色温
    capture.set(CAP_PROP_AUTO_WB,0);//启用/禁用自动白平衡 0 关闭 1 打开
    capture.set(CAP_PROP_FOURCC, VideoWriter::fourcc('M', 'J', 'P', 'G'));//视频流格式


    std::cout<<"width: "<<capture.get(CAP_PROP_FRAME_WIDTH)<<std::endl;
    std::cout<<"height: "<<capture.get(CAP_PROP_FRAME_HEIGHT)<<std::endl;
    std::cout<<"fps: "<<capture.get(CAP_PROP_FPS)<<std::endl;
    std::cout<<"backlight: "<<capture.get(CAP_PROP_BACKLIGHT)<<std::endl;
    std::cout<<"brightness: "<<capture.get(CAP_PROP_BRIGHTNESS)<<std::endl;
    std::cout<<"contrast: "<<capture.get(CAP_PROP_CONTRAST)<<std::endl;
    std::cout<<"saturation: "<<capture.get(CAP_PROP_SATURATION)<<std::endl;
    std::cout<<"hue: "<<capture.get(CAP_PROP_HUE)<<std::endl;
    std::cout<<"gamma: "<<capture.get(CAP_PROP_GAMMA)<<std::endl;
    std::cout<<"gain: "<<capture.get(CAP_PROP_GAIN)<<std::endl;
    std::cout<<"sharpness: "<<capture.get(CAP_PROP_SHARPNESS)<<std::endl;
    std::cout<<"wb_temperature: "<<capture.get(CAP_PROP_WB_TEMPERATURE)<<std::endl;
    std::cout<<"wb_auto: "<<capture.get(CAP_PROP_AUTO_WB)<<std::endl;
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(Opencv4_Test)
set(CMAKE_CXX_STANDARD 11)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})

总结

1、方法还是比较简单的,如果使用USB摄像头一定要把V4L加上,不然可能导致一些功能失效。如果是rtsp摄像头V4L就不是必须的了。
2、不建议盲目改参数,最好研究下每个参数的意义。

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值