OpenCV 获取 RTSP 摄像头视频流保存至本地

介绍

Java OpenCV 是一个强大的开源计算机视觉库,它提供了丰富的图像处理和分析功能,越来越多的应用需要使用摄像头来获取实时视频流进行处理和分析。

在 Java 中使用 OpenCV 打开摄像头的基本步骤如下:

  • 确保已经安装了OpenCV库
  • 使用 OpenCV 的 VideoCapture 类来打开摄像头
  • 使用 Mat 类来存储每一帧的图像
  • 使用循环来不断从摄像头中读取帧,并显示这些帧
  • 处理完毕后,释放摄像头资源

安装 OpenCV

下载地址:https://opencv.org/releases

在这里插入图片描述

从 OpenCV 官网下载适合自己操作系统版本的,然后双击安装(实质就是解压),解压完打开文件夹是:

build/
sources/
LICENSE.txt
LICENSE_FFMPEG.txt
README.md.txt

build 是 OpenCV 使用时要用到的一些库文件,而 sources 中则是 OpenCV 官方为我们提供的一些 demo 示例源码

配置环境变量可以不用配置,直接将用到的 dll(opencv_java411.dllopencv_world411.dllopencv_videoio_ffmpeg411_64.dll) 文件复制到 C:\Windows\System32 下即可。

编码实现

将 OpenCV 库添加到 Java 项目的构建路径中,使用 VideoCapture 类来打开摄像头。

添加依赖

<!--openCV 依赖包-->
<dependency><groupId>org.opencv</groupId><artifactId>opencv</artifactId><version>4.1.1</version><scope>system</scope><systemPath>${project.basedir}/src/main/resources/lib/opencv-411.jar</systemPath>
</dependency><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork> <!-- 如果没有该配置,devtools不会生效 --><includeSystemScope>true</includeSystemScope></configuration></plugin></plugins>
</build>

注:fork、includeSystemScope 不配置,打包不生效。

打开摄像头

package com.demo.utils;import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.VideoWriter;import static org.opencv.videoio.Videoio.CAP_PROP_FRAME_HEIGHT;
import static org.opencv.videoio.Videoio.CAP_PROP_FRAME_WIDTH;@Slf4j
public class RtspRecordingUtil {public static void main(String[] args) {init();}public static void init() {// 加载库System.loadLibrary(Core.NATIVE_LIBRARY_NAME);VideoCapture capture = new VideoCapture();capture.open("rtsp://admin:123456@192.168.1.11/Streaming/Channels/101");log.info("=======isOpen:{}========", capture.isOpened());if (capture.isOpened()) {Mat mat = new Mat();VideoWriter vw = new VideoWriter();Size size = new Size();size.width = capture.get(CAP_PROP_FRAME_WIDTH);size.height = capture.get(CAP_PROP_FRAME_HEIGHT);boolean t = vw.open("F:\\test.avi", VideoWriter.fourcc('M', 'P', '4', '2'), 30, size);// 录制while (capture.read(mat)) {vw.write(mat);}capture.release();vw.release();}log.info("=======结束======");}}

上述示例代码首先加载了 OpenCV 库,并创建了一个 VideoCapture 对象,打开默认摄像头。然后使用一个循环读取每一帧图像写到 VideoWriter 中保存。

打开多个摄像头

要打开多个摄像头,我们可以通过创建多个线程来拉取不同的视频流。

package com.demo.util;import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.VideoWriter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;import static org.opencv.videoio.Videoio.CAP_PROP_FRAME_HEIGHT;
import static org.opencv.videoio.Videoio.CAP_PROP_FRAME_WIDTH;@Slf4j
@Component
public class RtspRecordingUtil {// 视频保存地址@Value("${video.video-path}")private String videoPath;// 录制视频的默认时长@Value("${video.video-recording-duration}")private Long videoRecordingDuration;// 默认开启十个线程private String DEFAULT_THREAD_POOL = 10;ExecutorService executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL);{/*** 将以下三个文件:* opencv_java411.dll、* opencv_world411.dll、* opencv_videoio_ffmpeg411_64.dll* 文件拷贝到 C:\Windows\System32 目录下*/System.loadLibrary(Core.NATIVE_LIBRARY_NAME);// 本地运行可以,打包后找不到文件/**String path = this.getClass().getClassLoader().getResource("").getPath();System.load(path + "lib/dll/opencv_java411.dll");System.load(path + "lib/dll/opencv_world411.dll");System.load(path + "lib/dll/opencv_videoio_ffmpeg411_64.dll");*/}public String recording(String username, String password, String ip, Integer chanelId, String videoName, Integer duration) {executorService.submit(new VideoCaptureTask(username, password, ip, chanelId, videoName, duration));return "ok";}class VideoCaptureTask implements Runnable {// 设备用户名private String username;// 设备密码private String password;// 设备IPprivate String ip;// 设备通道号private Integer chanelId;// 视频名称private String videoName;// 录制时长private Integer duration;public VideoCaptureTask(String username, String password, String ip, Integer chanelId, String videoName, Integer duration) {this.username = username;this.password = password;this.ip = ip;this.chanelId = chanelId;this.videoName = videoName;this.duration = duration;}@Overridepublic void run() {VideoCapture capture = null;VideoWriter vw = null;try {capture = new VideoCapture(videoName);String url = "rtsp://" + username + ":" + password + "@" + ip + "/Streaming/Channels/" + (Objects.nonNull(chanelId) ? chanelId : "1");capture.open(url);log.info("==== VideoCapture 开始....URL: {} ======= isOpened:{}=====", url, capture.isOpened());if (capture.isOpened()) {Mat mat = new Mat();Size size = new Size(capture.get(CAP_PROP_FRAME_WIDTH), capture.get(CAP_PROP_FRAME_HEIGHT));// 视频存储地址vw = new VideoWriter();// 判断存储目录是否存在if (Files.notExists(Paths.get(videoPath))) {Files.createDirectories(Paths.get(videoPath));}vw.open(videoPath + videoName + ".avi", VideoWriter.fourcc('M', 'P', '4', '2'), 30, size);Instant start = Instant.now();long seconds = 0;long _duration = Objects.nonNull(duration) ? duration : videoRecordingDuration;while (capture.read(mat) && seconds <= _duration) {vw.write(mat);seconds = Duration.between(start, Instant.now()).getSeconds();}} log.info("==== VideoCapture 结束....URL: {} =====", url);} catch (Exception e) {log.error("==== VideoCapture 异常:{}", e);} finally {if (Objects.nonNull(capture)) capture.release();if (Objects.nonNull(vw)) vw.release();}}}}

需要处理不同摄像头之间分辨率和帧率的不匹配问题,以及考虑如何有效地管理多个 VideoCapture 实例问题,这里使用视频名称作为摄像头的索引(new VideoCapture(videoName))防止重复实例化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/14485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Raylib 绘制自定义字体的一种套路

Raylib 绘制自定义字体是真的难搞。我的需求是程序可以加载多种自定义字体&#xff0c;英文中文的都有。 我调试了很久成功了&#xff01; 很有用的参考&#xff0c;建议先看一遍&#xff1a; 瞿华&#xff1a;raylib绘制中文内容 个人笔记&#xff5c;Raylib 的字体使用 - …

W801 实现获取天气情况

看了小安派&#xff08;AiPi-Eyes 天气站&#xff09;的源码&#xff0c;感觉用W801也可以实现。 一、部分源码 main.c #include "wm_include.h" #include "Lcd_Driver.h"void UserMain(void) {printf("\n user task \n");Lcd_Init();Lcd_Clea…

MySQL主从复制(五):读写分离

一主多从架构主要应用场景&#xff1a;读写分离。读写分离的主要目标是分摊主库的压力。 读写分离架构 读写分离架构一 架构一结构图&#xff1a; 这种结构模式下&#xff0c;一般会把数据库的连接信息放在客户端的连接层&#xff0c;由客户端主动做负载均衡。也就是说由客户…

RabbitMQ 消息队列安装及入门

市面常见消息队列中间件对比 技术名称吞吐量 /IO/并发时效性&#xff08;类似延迟&#xff09;消息到达时间可用性可靠性优势应用场景activemq万级高高高简单易学中小型企业、项目rabbitmq万级极高&#xff08;微秒&#xff09;高极高生态好&#xff08;基本什么语言都支持&am…

leetcode124 二叉树中的最大路径和-dp

题目 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root &…

【Crypto】Rabbit

文章目录 一、Rabbit解题感悟 一、Rabbit 题目提示很明显是Rabbit加密&#xff0c;直接解 小小flag&#xff0c;拿下&#xff01; 解题感悟 提示的太明显了

redis核心面试题二(实战优化)

文章目录 10. redis配置mysql实战优化[重要]11. redis之缓存击穿、缓存穿透、缓存雪崩12. redis实现分布式session 10. redis配置mysql实战优化[重要] // 最初实现OverrideTransactionalpublic Product createProduct(Product product) {productRepo.saveAndFlush(product);je…

MQTT 5.0 报文解析 05:DISCONNECT

欢迎阅读 MQTT 5.0 报文系列 的第五篇文章。在上一篇中&#xff0c;我们已经介绍了 MQTT 5.0 的 PINGREQ 和 PINGRESP 报文。现在&#xff0c;我们将介绍下一个控制报文&#xff1a;DISCONNECT。 在 MQTT 中&#xff0c;客户端和服务端可以在断开网络连接前向对端发送一个 DIS…

手把手教你搭建一个花店小程序商城

如果你是一位花店店主&#xff0c;想要为你的生意搭建一个精美的小程序商城&#xff0c;以下是你将遵循的五个步骤。 步骤1&#xff1a;登录乔拓云平台进入后台 首先&#xff0c;你需要登录乔拓云平台的后台管理页面。你可以在电脑或移动设备上的浏览器中输入乔拓云的官方网站…

2024.5.26 机器学习周报

目录 引言 Abstract 文献阅读 1、题目 2、引言 3、创新点 4、Motivation 5、naive Lite-HRNet 6、Lite-HRNet 7、实验 深度学习 解读SAM(Segment Anything Model) 1、SAM Task 2、SAM Model 2.1、Patch Embedding 2.2、Positiona Embedding 2.3、Transformer …

互联网医院开发:引领智慧医疗新时代

随着科技的迅猛发展和互联网的普及&#xff0c;传统医疗模式正在迎来一场深刻的变革。互联网医院的崛起&#xff0c;打破了时间和空间的限制&#xff0c;为患者和医疗机构带来了更加便捷、高效、安全的医疗服务体验。本文将从技术角度深入探讨互联网医院的开发&#xff0c;包括…

多线程(八)

一、wait和notify 等待 通知 机制 和join的用途类似,多个线程之间随机调度,引入 wait notify 就是为了能够从应用层面上,干预到多个不同线程代码的执行顺序.( 这里说的干预,不是影响系统的线程调度策略 内核里的线程调度,仍然是无序的. 相当于是在应用程序…

Pod容器资源限制和探针

目录 一、资源限制 1.Pod和容器的资源请求和限制 2.CPU 资源单位 案例一 案例二 二、健康检查&#xff0c;又称为探针&#xff08;Probe&#xff09; 1.探针的三种规则 2.Probe支持三种检查方法 3.探测获得的三种结果 案例一&#xff1a;exec 案例二&#xff1a;htt…

OneMO同行 心级服务:中移物联OneMO模组助力客户终端寒冷环境下的稳定运行

中移物联OneMO模组以客户为中心&#xff0c;基于中国移动心级服务要求&#xff0c;开展“OneMO同行 心级服务 标定一流”高标服务主题活动&#xff0c;升级“服务内容““服务方式”和“服务意识”&#xff0c;为行业客户提供全新的服务体验。 近日&#xff0c;某车载监控设备…

ACM实训第十七天

Is It A Tree? 问题 考试时应该做不出来&#xff0c;果断放弃 树是一种众所周知的数据结构&#xff0c;它要么是空的(null, void, nothing)&#xff0c;要么是一个或的集合满足以下属性的节点之间有向边连接的节点较多。 •只有一个节点&#xff0c;称为根节点&#xff0c;它…

【Crypto】摩丝

文章目录 一、摩斯解题感悟 一、摩斯 很明显莫尔斯密码 iloveyou还挺浪漫 小小flag&#xff0c;拿下 解题感悟 莫尔斯密码这种题还是比较明显的

智能锁千千万,谁是你的NO.1,亲身实测凯迪仕传奇大师K70旗舰新品

智能锁千千万&#xff0c;谁是你的NO.1。欢迎来到智哪儿评测室&#xff0c;这次我们为大家带来了凯迪仕传奇大师K70系列的一款重磅新品。 在科技的浪潮中&#xff0c;家居安全领域正经历着前所未有的变革。智能锁越来越成为家的安全守护神&#xff0c;以及智能生活的得力助手。…

Android 11 Framework实时监听Activity堆栈变化

核心类 Framework中有一个类SystemActivityMonitoringService专门用于监控Activity堆栈变化&#xff0c;属于隐藏Api&#xff0c;应用侧无法调用。此类位于 packages/services/Car/service/src/com/android/car/SystemActivityMonitoringService.java 方法 void registerTa…

大数据Hive中的UDF:自定义数据处理的利器(下)

在上一篇文章中&#xff0c;我们对第一种用户定义函数&#xff08;UDF&#xff09;进行了基础介绍。接下来&#xff0c;本文将带您深入了解剩余的两种UDF函数类型。 文章目录 1. UDAF1.1 简单UDAF1.2 通用UDAF 2. UDTF3. 总结 1. UDAF 1.1 简单UDAF 第一种方式是 Simple(简单…

每日一题《leetcode--382.链表随机结点》

https://leetcode.cn/problems/linked-list-random-node/ 这道题我们首先看到题目中的要求&#xff1a;在单链表中随机选取一个链表中的结点&#xff0c;要使每个结点被选取的概率是一样的。 当我们看到随机这两个字时&#xff0c;应该就会想起rand()这个函数。接着我们把使用这…