前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenCV Android 之 VideoCapture类

OpenCV Android 之 VideoCapture类

作者头像
zinyan.com
发布2023-07-14 11:10:54
7900
发布2023-07-14 11:10:54
举报
文章被收录于专栏:zinyanzinyan

1. 介绍

首先,需要明确一个根本问题。OpenCV 是一个基于 Apache2.0 许可(开源)发行的跨平台计算机视觉和机器学习软件库。它实际上各种图像处理和计算机视觉方面的通用算法的集中库。

简而言之就是:处理图片。

通常都是使用 OpenCV 来进行各种图片处理和计算。所以它并不是一个视频编解码库。不要想着使用 OpenCV 来进行视频播放

所有使用 OpenCV 进行播放视频,实际上都是将视频转图片了,再一张张图片在切换显示,编解码和效率是远远没有专门的视频播放器效率高的。

如果要播放视频,还是建议使用 FFmpeg 处理。

而我们可以通过OpenCV将视频进行解码成Mat文件,进行操作,并将编辑之后的结果存储为视频。

可以将相机拍摄的结果,进行实时处理之后。存储为视频等操作。

而使用到的就是VideoWriterVideoCapture类了。

以下内容基于:OpenCV 4.6.0 版本API进行的介绍和使用。

2. VideoCapture

用于从视频文件、图像序列或相机捕获视频的类。这个类提供了针对视频的各种捕获方法。

提供了几种方法:

1.获取每一帧数据,转为Mat。

2.获取视频的一些配置信息,例如时长,FPS,帧数,宽高等等。

初始化如下:

代码语言:javascript
复制
 VideoCapture videoCapture = new VideoCapture(); //创建一个VideoCapture对象

我们其实在创建过程中的时候,也可以进行初始化传参。这些构造初始化时传的参数和调用open()方法传的参数实际是一样的。

PS:使用 OpenCV 的方法时,请注意需要提前进行初始化加载 OpenCV 库。否则会出现相关类找不到而崩溃

代码语言:javascript
复制
OpenCVLoader.initDebug(false);//加载OpenCV库

2.1 加载 open() 方法

下面不管是相机加载,还是网络地址加载。我在 Android 端上没有成功。只有加载本地视频成功了。

加载摄像头应该是 Android 本身不支持的原因造成的。尝试了各种 cameraId 值和相关 apiPreference 都失败了。(我们可以使用CameraX加载摄像头并进行处理和存储)

加载网络视频失败我估计,应该是因为 openCV 默认编译的 Android SDK 中没有相关依赖造成的。

(如果是缺少依赖库造成的,希望能够有明白的小伙伴指点一下吧。各种尝试我都失败了)。

代码语言:javascript
复制
 boolean isOpen = videoCapture.open("/storage/emulated/0/Android/data/com.zinyan.demo/files/demo.mp4", Videoio.CAP_ANDROID); //加载本地视频

 boolean isOpen = videoCapture.open(0); //加载摄像头

 boolean isOpen = videoCapture.open("https://host:port/script_name?script_params|auth", Videoio.CAP_ANDROID); //加载网络视频。

open方法传递主要是以下一种参数:

  • String filename:文件地址,可以是Url地址也可以是本地文档地址。
  • int index:相机id, 如果0 会调用设备默认的后置摄像头。
  • int apiPreference: api首选项。该参数为:Videoio.CAP_ANYVideoio.CAP_DSHOW,Videoio.CAP_ANDROID等.

VideoCapture 中传入的apiPrefreence的可选参数列表如下所示:

代码语言:javascript
复制
 // C++: enum VideoCaptureAPIs
    public static final int
            CAP_ANY = 0,
            CAP_VFW = 200,
            CAP_V4L = 200,
            CAP_V4L2 = CAP_V4L,
            CAP_FIREWIRE = 300,
            CAP_FIREWARE = CAP_FIREWIRE,
            CAP_IEEE1394 = CAP_FIREWIRE,
            CAP_DC1394 = CAP_FIREWIRE,
            CAP_CMU1394 = CAP_FIREWIRE,
            CAP_QT = 500,
            CAP_UNICAP = 600,
            CAP_DSHOW = 700,
            CAP_PVAPI = 800,
            CAP_OPENNI = 900,
            CAP_OPENNI_ASUS = 910,
            CAP_ANDROID = 1000,
            CAP_XIAPI = 1100,
            CAP_AVFOUNDATION = 1200,
            CAP_GIGANETIX = 1300,
            CAP_MSMF = 1400,
            CAP_WINRT = 1410,
            CAP_INTELPERC = 1500,
            CAP_REALSENSE = 1500,
            CAP_OPENNI2 = 1600,
            CAP_OPENNI2_ASUS = 1610,
            CAP_GPHOTO2 = 1700,
            CAP_GSTREAMER = 1800,
            CAP_FFMPEG = 1900,
            CAP_IMAGES = 2000,
            CAP_ARAVIS = 2100,
            CAP_OPENCV_MJPEG = 2200,
            CAP_INTEL_MFX = 2300,
            CAP_XINE = 2400;

调用open()方法后,如果加载成功了就会返回true,失败则返回false。

由于,我只是加载本地视频能够实现成功加载。所以下面的介绍也是基于该成功之后进行的。

在Android端中,如果想能够正确的打开视频并进行解析。apiPrefreence的值只有:

Videoio.CAP_ANY 或者 Videoio.CAP_ANDROID才能正确加载视频

返回的isOpen才是true。示例如下:

代码语言:javascript
复制
boolean isOpen = videoCapture.open(fileUrl, Videoio.CAP_ANY); 

boolean isOpen = videoCapture.open(fileUrl, Videoio.CAP_ANDROID);

我有尝试过使用CAP_FFMPEG当做值,进行加载。

代码语言:javascript
复制
 boolean isOpen = videoCapture.open(fileUrl, Videoio.CAP_FFMPEG);

//错误输出如下内容:
com.zinyan.demo E/cv::error(): OpenCV(4.6.0) Error: Requested object was not found (could not open directory: /data/app/com.zinyan.demo-Wr3nLeu2TTtG12e53ogTGw==/base.apk!/lib/arm64-v8a) in glob_rec, file /build/master_pack-android/opencv/modules/core/src/glob.cpp, line 267

应该是默认的OpenCV Android SDK中。并没有FFmpeg相关库。

所以想通过https或者rtsp等协议加载在线视频也失败。原因在于openCV 预编译的Android SDK中,并没有那么多第三方项目。可能是需要我们自己配置吧。

PS:自己配置编译,有点繁琐。我也没有进行过尝试。

当我们加载成功视频之后。就可以进行解析操作了。

2.2 解析 read(),grab()和retrieve()方法

这三个方法主要就是用来获取视频的每一帧的数据,并将帧数据转为Mat对象。

请注意哦,它们获取的Mat对象是BGR格式的。

例如:获取当前帧:

代码语言:javascript
复制
Mat m = new Mat();
videoCapture.read(m);
//我们就能够得到当前帧了。
//官方建议我们不要直接操作获取的Mat对象。我们可以进行拷贝之后再对Mat进行操作
Mat temp =m.clone()

除此之外,还有以下方法也可以获取当前帧:

代码语言:javascript
复制
boolean isFrame =videoCapture.grab(); //从视频文件或捕获设备中抓取下一帧。抓取成功为true,否则为false
Mat tt =new Mat();
boolean isRetrieve =videoCapture.retrieve(tt); //解码并返回抓取的视频帧。如果没有帧返回false。

其实read()grab()+retrieve()方法的合集。

grab()方法只是检测视频帧,不会解析视频帧。所以它速度比较快。

retrieve()方法会进行视频帧的解析。会比grab()方法更耗时。这两个方法通常都是一起使用的。

但是,大部分情况下都是使用read()+循环,遍历整个视频的所有帧,并进行处理。

代码语言:javascript
复制
 while (videoCapture.read(mat)) {
    Mat m = new Mat();
    Imgproc.cvtColor(mat, m, Imgproc.COLOR_BGR2HSV_FULL);
}

read():方法返回的false时,代表视频已经没有下一帧了。也就是解析到最后一帧了。

通过循环的方式,可以快速的解析视频中的每一帧数据,并转为Mat进行处理。

注意,VideoCapture 在调用 read() 获取视频帧之后。一直获取到最后之后。不会回到第一帧获取。我们只能重新调用open()方法再次加载才行。

2.3 修改 set()和get()方法

我们除了可以遍历视频帧数据以外。还可以通过get()方法获取视频的相关信息。

示例如下:

代码语言:javascript
复制
double ftp = videoCapture.get(Videoio.CAP_PROP_FPS);
double width = videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH);
double count = videoCapture.get(Videoio.CAP_PROP_FRAME_COUNT);
double htight = videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT);

这个方法要传入的是 propId 值,该值的取值参数有如下:

代码语言:javascript
复制
Videoio.CAP_PROP_POS_MSEC = 0,
Videoio.CAP_PROP_POS_FRAMES = 1,
Videoio.CAP_PROP_POS_AVI_RATIO = 2,
Videoio.CAP_PROP_FRAME_WIDTH = 3,
Videoio.CAP_PROP_FRAME_HEIGHT = 4,
Videoio.CAP_PROP_FPS = 5,
Videoio.CAP_PROP_FOURCC = 6,
Videoio.CAP_PROP_FRAME_COUNT = 7,
Videoio.CAP_PROP_FORMAT = 8,
Videoio.CAP_PROP_MODE = 9,
Videoio.CAP_PROP_BRIGHTNESS = 10,
Videoio.CAP_PROP_CONTRAST = 11,
Videoio.CAP_PROP_SATURATION = 12,
Videoio.CAP_PROP_HUE = 13,
Videoio.CAP_PROP_GAIN = 14,
Videoio.CAP_PROP_EXPOSURE = 15,
Videoio.CAP_PROP_CONVERT_RGB = 16,
Videoio.CAP_PROP_WHITE_BALANCE_BLUE_U = 17,
Videoio.CAP_PROP_RECTIFICATION = 18,
Videoio.CAP_PROP_MONOCHROME = 19,
Videoio.CAP_PROP_SHARPNESS = 20,
Videoio.CAP_PROP_AUTO_EXPOSURE = 21,
Videoio.CAP_PROP_GAMMA = 22,
Videoio.CAP_PROP_TEMPERATURE = 23,
Videoio.CAP_PROP_TRIGGER = 24,
Videoio.CAP_PROP_TRIGGER_DELAY = 25,
Videoio.CAP_PROP_WHITE_BALANCE_RED_V = 26,
Videoio.CAP_PROP_ZOOM = 27,
Videoio.CAP_PROP_FOCUS = 28,
Videoio.CAP_PROP_GUID = 29,
Videoio.CAP_PROP_ISO_SPEED = 30,
Videoio.CAP_PROP_BACKLIGHT = 32,
Videoio.CAP_PROP_PAN = 33,
Videoio.CAP_PROP_TILT = 34,
Videoio.CAP_PROP_ROLL = 35,
Videoio.CAP_PROP_IRIS = 36,
Videoio.CAP_PROP_SETTINGS = 37,
Videoio.CAP_PROP_BUFFERSIZE = 38,
Videoio.CAP_PROP_AUTOFOCUS = 39,
Videoio.CAP_PROP_SAR_NUM = 40,
Videoio.CAP_PROP_SAR_DEN = 41,
Videoio.CAP_PROP_BACKEND = 42,
Videoio.CAP_PROP_CHANNEL = 43,
Videoio.CAP_PROP_AUTO_WB = 44,
Videoio.CAP_PROP_WB_TEMPERATURE = 45,
Videoio.CAP_PROP_CODEC_PIXEL_FORMAT = 46,
Videoio.CAP_PROP_BITRATE = 47;

但是,我们很多时候使用上面的关键字进行获取的数据,结果值都是0

这是因为 openCV 使用的解析器在获取视频时,如果正确获取了相关配置项参数就会返回具体指。如果没有正确获取就会返回0了。

在我的实际使用过程中,大部分都是取不到真实数据。而宽高等数据,还得读取过一帧数据之后,才能取到值。

代码语言:javascript
复制
videoCapture.set(int propId, double value)

set()方法,就是将这些配置信息修改到 VideoCapture 中。

如果在open()方法中调用的解码器支持的话。就可以将这些配置信息添加到解码器中。进行生效了。

我们如果只是单纯调用 openCV 的 API。那么set()方法使用空间不大了。

2.4 关闭 release()

当我们遍历完毕,可以调用release()方法 关闭文件的加载。释放内存。

同时底层C++代码中的相关方法也会进行释放。

3. 小结

总的来说,我们可以使用VideoCapture进行视频帧的遍历,并在遍历过程中对每一帧数据进行编辑修改操作。

我们如果想使用 openCV 对视频每一帧进行操作之后,再存储为视频。那么就还需要结合VideoWriter 进行存储。

默认情况下Android下,是可以实现视频的每一帧获取,并修改然后存储为新的视频文件的。

通过这些方法可以实现,例如视频添加水印,背景替换,黑白转换等等。图片能实现的一些编辑操作都可以通过获取每一帧,处理完毕后。再将每一帧存储为视频来实现。

openCV 官网说明文档:

https://docs.opencv.org/4.6.0/d4/d15/groupvideoioflags__base.html#ga023786be1ee68a9105bf2e48c700294d

下一篇简单介绍下VideoWriter的相关使用吧。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 zinyan 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 介绍
  • 2. VideoCapture
    • 2.1 加载 open() 方法
      • 2.2 解析 read(),grab()和retrieve()方法
        • 2.3 修改 set()和get()方法
          • 2.4 关闭 release()
          • 3. 小结
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档