当前位置: 首页 > news >正文

springboot(JavaCV )实现视频截取第N帧并保存图片

springboot(JavaCV )实现视频截取第N帧并保存图片

现在视频网站展示列表都是用img标签展示的,动图用的是gif,但是我们上传视频时并没有视屏封面,就这需要上传到服务器时自动生成封面并保存

本博客使用jar包的方式实现上传视频文件并且截取视频第一帧,保存到阿里云的OSS(也可以保存到本地获取其他任何地方)。

JavaCV 是一款开源的视觉处理库,基于GPLv2协议,对各种常用计算机视觉库封装后的一组jar包,

封装了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等计算机视觉编程人员常用库的接口。

此方法的好处是不需要再服务器上安装插件,直接代码中就可以实现视频截取。

我们需要截取视频第一帧,主要用到了ffmpeg和opencv。

一 , 引入jar包
我用到的maven的目前最新javacv版本,1.4.3,它应该支持jdk1.7及以上,我项目用的还是jdk1.8.

不过需要注意的是在使用的过程当中 , maven引入jar的时候 会引入所有平台的版本

全部引入大小在五百兆左右(不建议使用)

<!--视频截取第一帧--><dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId><version>1.4.3</version></dependency><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg-platform</artifactId><version>4.0.2-1.4.3</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>RELEASE</version></dependency>

二 , java 代码实现

public class ImgTools {//util调用application.propertiesprivate final static ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle("application");private final static String aliyuVideonImg = RESOURCE_BUNDLE.getString("aliyun.video.img");//    public static void main(String[] args) throws Exception {
//        ImgTools imgTools = new ImgTools();
//        System.out.println(imgTools.randomGrabberFFmpegVideoImage
//                ("视频地址,可以是网络视频,也可以是本地视频"));
//    }/*** 获取视频缩略图** @param filePath:视频路径* @throws Exception*/public String randomGrabberFFmpegVideoImage(String filePath) throws Exception {String targetFilePath = "";FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath);ff.start();//判断是否是竖屏小视频String rotate = ff.getVideoMetadata("rotate");int ffLength = ff.getLengthInFrames();Frame f;int i = 0;int index = 3;//截取图片第几帧while (i < ffLength) {f = ff.grabImage();if (i == index) {if (null != rotate && rotate.length() > 1) {targetFilePath = doExecuteFrame(f, true);   //获取缩略图} else {targetFilePath = doExecuteFrame(f, false);   //获取缩略图}break;}i++;}ff.stop();return targetFilePath;  //返回的是视频第N帧}/*** 截取缩略图,存入阿里云OSS(按自己的上传类型自定义转换文件格式)** @param f* @return* @throws Exception*/public String doExecuteFrame(Frame f, boolean bool) throws Exception {if (null == f || null == f.image) {return "";}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(f);if (bool == true) {Image image = (Image) bi;bi = rotate(image, 90);//图片旋转90度}ByteArrayOutputStream os = new ByteArrayOutputStream();ImageIO.write(bi, "png", os);byte[] sdf = os.toByteArray();InputStream input = new ByteArrayInputStream(os.toByteArray());MultipartFile multipartFile = new MockMultipartFile("temp.jpg", "temp.jpg", "temp.jpg", input);Aliyunoss aliyunoss = new Aliyunoss();//如需了解阿里云OSS,请详读我的另一篇博客("https://blog.csdn.net/weixin_44401989/article/details/105732856")String url = aliyunoss.uploadAli(multipartFile, aliyuVideonImg);return url;}/*** 图片旋转角度** @param src   源图片* @param angel 角度* @return 目标图片*/public static BufferedImage rotate(Image src, int angel) {int src_width = src.getWidth(null);int src_height = src.getHeight(null);// calculate the new image sizeRectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);BufferedImage res = null;res = new BufferedImage(rect_des.width, rect_des.height,BufferedImage.TYPE_INT_RGB);Graphics2D g2 = res.createGraphics();// transform(这里先平移、再旋转比较方便处理;绘图时会采用这些变化,绘图默认从画布的左上顶点开始绘画,源图片的左上顶点与画布左上顶点对齐,然后开始绘画,修改坐标原点后,绘画对应的画布起始点改变,起到平移的效果;然后旋转图片即可)//平移(原理修改坐标系原点,绘图起点变了,起到了平移的效果,如果作用于旋转,则为旋转中心点)g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2);//旋转(原理transalte(dx,dy)->rotate(radians)->transalte(-dx,-dy);修改坐标系原点后,旋转90度,然后再还原坐标系原点为(0,0),但是整个坐标系已经旋转了相应的度数 )g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);//        //先旋转(以目标区域中心点为旋转中心点,源图片左上顶点对准目标区域中心点,然后旋转)
//        g2.translate(rect_des.width/2,rect_des.height/ 2);
//        g2.rotate(Math.toRadians(angel));
//        //再平移(原点恢复到源图的左上顶点处(现在的右上顶点处),否则只能画出1/4)
//        g2.translate(-src_width/2,-src_height/2);g2.drawImage(src, null, null);return res;}/*** 计算转换后目标矩形的宽高** @param src   源矩形* @param angel 角度* @return 目标矩形*/private static Rectangle CalcRotatedSize(Rectangle src, int angel) {double cos = Math.abs(Math.cos(Math.toRadians(angel)));double sin = Math.abs(Math.sin(Math.toRadians(angel)));int des_width = (int) (src.width * cos) + (int) (src.height * sin);int des_height = (int) (src.height * cos) + (int) (src.width * sin);return new java.awt.Rectangle(new Dimension(des_width, des_height));}
}
public class ImgTools {//util调用application.propertiesprivate final static ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle("application");private final static String aliyuVideonImg = RESOURCE_BUNDLE.getString("aliyun.video.img");//    public static void main(String[] args) throws Exception {
//        ImgTools imgTools = new ImgTools();
//        System.out.println(imgTools.randomGrabberFFmpegVideoImage
//                ("视频地址,可以是网络视频,也可以是本地视频"));
//    }/*** 获取视频缩略图** @param filePath:视频路径* @throws Exception*/public String randomGrabberFFmpegVideoImage(String filePath) throws Exception {String targetFilePath = "";FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath);ff.start();//判断是否是竖屏小视频String rotate = ff.getVideoMetadata("rotate");int ffLength = ff.getLengthInFrames();Frame f;int i = 0;int index = 3;//截取图片第几帧while (i < ffLength) {f = ff.grabImage();if (i == index) {if (null != rotate && rotate.length() > 1) {targetFilePath = doExecuteFrame(f, true);   //获取缩略图} else {targetFilePath = doExecuteFrame(f, false);   //获取缩略图}break;}i++;}ff.stop();return targetFilePath;  //返回的是视频第N帧}/*** 截取缩略图,存入阿里云OSS(按自己的上传类型自定义转换文件格式)** @param f* @return* @throws Exception*/public String doExecuteFrame(Frame f, boolean bool) throws Exception {if (null == f || null == f.image) {return "";}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(f);if (bool == true) {Image image = (Image) bi;bi = rotate(image, 90);//图片旋转90度}ByteArrayOutputStream os = new ByteArrayOutputStream();ImageIO.write(bi, "png", os);byte[] sdf = os.toByteArray();InputStream input = new ByteArrayInputStream(os.toByteArray());MultipartFile multipartFile = new MockMultipartFile("temp.jpg", "temp.jpg", "temp.jpg", input);Aliyunoss aliyunoss = new Aliyunoss();//如需了解阿里云OSS,请详读我的另一篇博客("https://blog.csdn.net/weixin_44401989/article/details/105732856")String url = aliyunoss.uploadAli(multipartFile, aliyuVideonImg);return url;}/*** 图片旋转角度** @param src   源图片* @param angel 角度* @return 目标图片*/public static BufferedImage rotate(Image src, int angel) {int src_width = src.getWidth(null);int src_height = src.getHeight(null);// calculate the new image sizeRectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);BufferedImage res = null;res = new BufferedImage(rect_des.width, rect_des.height,BufferedImage.TYPE_INT_RGB);Graphics2D g2 = res.createGraphics();// transform(这里先平移、再旋转比较方便处理;绘图时会采用这些变化,绘图默认从画布的左上顶点开始绘画,源图片的左上顶点与画布左上顶点对齐,然后开始绘画,修改坐标原点后,绘画对应的画布起始点改变,起到平移的效果;然后旋转图片即可)//平移(原理修改坐标系原点,绘图起点变了,起到了平移的效果,如果作用于旋转,则为旋转中心点)g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2);//旋转(原理transalte(dx,dy)->rotate(radians)->transalte(-dx,-dy);修改坐标系原点后,旋转90度,然后再还原坐标系原点为(0,0),但是整个坐标系已经旋转了相应的度数 )g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);//        //先旋转(以目标区域中心点为旋转中心点,源图片左上顶点对准目标区域中心点,然后旋转)
//        g2.translate(rect_des.width/2,rect_des.height/ 2);
//        g2.rotate(Math.toRadians(angel));
//        //再平移(原点恢复到源图的左上顶点处(现在的右上顶点处),否则只能画出1/4)
//        g2.translate(-src_width/2,-src_height/2);g2.drawImage(src, null, null);return res;}/*** 计算转换后目标矩形的宽高** @param src   源矩形* @param angel 角度* @return 目标矩形*/private static Rectangle CalcRotatedSize(Rectangle src, int angel) {double cos = Math.abs(Math.cos(Math.toRadians(angel)));double sin = Math.abs(Math.sin(Math.toRadians(angel)));int des_width = (int) (src.width * cos) + (int) (src.height * sin);int des_height = (int) (src.height * cos) + (int) (src.width * sin);return new java.awt.Rectangle(new Dimension(des_width, des_height));}
}

相关文章:

springboot(JavaCV )实现视频截取第N帧并保存图片

springboot&#xff08;JavaCV &#xff09;实现视频截取第N帧并保存图片 现在视频网站展示列表都是用img标签展示的&#xff0c;动图用的是gif&#xff0c;但是我们上传视频时并没有视屏封面&#xff0c;就这需要上传到服务器时自动生成封面并保存 本博客使用jar包的方式实现…...

Linux面试笔试题(5)

79、下列工具中可以直接连接mysql的工具有【c 】。 A.xhsell B.plsql C.navicat D.以上都不是 Navicat 是一套可创建多个连接的数据库开发工具&#xff0c; 让你从单一应用程序中同时连接 MySQL、Redis、MariaDB、MongoDB、 SQL Server、Oracle、PostgreSQL和 SQLite 。它与…...

WordPress文章:创建和优化您的网站内容

WordPress是一种流行的内容管理系统&#xff08;CMS&#xff09;&#xff0c;用于创建和管理网站。无论您是个人博客作者、企业网站管理员还是电子商务店主&#xff0c;WordPress都是一个强大而灵活的平台&#xff0c;可帮助您展示和传达您的信息。本文将为您提供一些关于创建和…...

Selenium webdriver_manager根据浏览器版本自动下载对应驱动程序

前言 webdriver_manager是什么&#xff1f; webdriver_manager 是 Python 中的一个库&#xff0c;用于管理 Web 驱动程序。它的作用是自动下载和设置不同浏览器&#xff08;如 Chrome、Firefox、Edge 等&#xff09;的 Web 驱动程序&#xff0c;以便在自动化测试中使用这些浏…...

2023 - java - 强制类型转换和装箱

强制类型转换和装箱&#xff1a; 在 Java 中&#xff0c;(Integer) 和(int) 是两个不同的类型转换操作符&#xff0c;它们的效果是不一样的。 int a (Integer) t.getContent(); 这条语句使用了装箱&#xff08;Boxing&#xff09;操作&#xff0c;将一个整数对象&#xff08;…...

使用ansible自动化部署Kubernetes

使用 kubeasz 部署 Kubernetes 集群 服务器列表&#xff1a; IP主机名角色192.168.100.142kube-master1,kube-master1.suosuoli.cnK8s 集群主节点 1192.168.100.144kube-master2,kube-master2.suosuoli.cnK8s 集群主节点 2192.168.100.146kube-master3,kube-master3.suosuoli…...

k8s v1.27.4 部署metrics-serverv:0.6.4,kube-prometheus

只有一个问题&#xff0c;原来的httpGet存活、就绪检测一直不通过&#xff0c;于是改为tcpSocket后pod正常。 wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml修改后的yaml文件&#xff0c;镜像修改为阿里云 apiVersion: …...

6-模板初步使用

官网: 中文版: 介绍-Jinja2中文文档 英文版: Template Designer Documentation — Jinja Documentation (2.11.x) 模板语法 1. 模板渲染 (1) app.py 准备数据 import jsonfrom flask import Flask,render_templateimport settingsapp Flask(__name__) app.config.from_obj…...

STM32CubeMX配置STM32F103 USB Virtual Port Com(HAL库开发)

1.配置外部高速晶振 2.勾选USB功能 3.将USB模式配置Virtual Port Com 4.将系统主频配置为72M,USB频率配置为48M. 5.配置好项目名称&#xff0c;开发环境&#xff0c;最后获取代码。 6.在CDC_Receive_FS函数中写入USB发送函数。这样USB接收到的数据就好原样发送。 7.将串口助手打…...

RocketMQ与Kafka对比(18项差异)

淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用MySQL作为消息存储媒介,可完全水平扩容,为了进一步降低成本,我们认为存储部分可以进一步优化,2011年初,Linkin开源了Kafka这个优秀的消息中间件,淘宝中间件团队在对Kafka做过充分Review之后,Kafka无限消息…...

英文翻译照片怎么做?掌握这个方法轻松翻译

在现代社会中&#xff0c;英文已经成为了一种全球性的语言&#xff0c;因此&#xff0c;我们在阅读文章或者查看图片时&#xff0c;经常会遇到英文的内容。为了更好地理解这些英文内容&#xff0c;我们需要将其翻译成中文。在本文中&#xff0c;我将探讨图片中英文内容翻译的方…...

Linux介绍

目录 unix linux的版本号 linux对unix的继承 linux特性&#xff1a;安全性高 unix Unix是一个先进的、多用户、多任务的操作系统&#xff0c;被广泛用于服务器、工作站和移动设备。以下是Unix的一些关键特点和组件&#xff1a; 多用户系统&#xff1a;允许多个用户同时访…...

计算机竞赛 卷积神经网络手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…...

[Go版]算法通关村第十三关白银——数组实现加法和幂运算

目录 数组实现加法专题题目&#xff1a;数组实现整数加法思路分析&#xff1a;复杂度&#xff1a;Go代码 题目&#xff1a;字符串加法思路分析&#xff1a;复杂度&#xff1a;Go代码 题目&#xff1a;二进制加法思路分析&#xff1a;复杂度&#xff1a;Go代码 幂运算专题题目&a…...

React笔记[tsx]-解决Property ‘frames‘ does not exist on type ‘Readonly<{}>‘

浏览器报错如下&#xff1a; 编辑器是这样的&#xff1a; 原因是React.Component<any>少了后面的any&#xff0c;改成这样即可&#xff1a; export class CustomFrame extends React.Component<any, any>{............ }...

ThinkPHP6.0+ 使用Redis 原始用法

composer 安装 predis/predis 依赖&#xff0c;或者安装php_redis.dll的扩展。 我这里选择的是predis/predis 依赖。 composer require predis/predis 进入config/cache.php 配置添加redis缓存支持 示例&#xff1a; <?php// -----------------------------------------…...

SRM系统询价竞价管理:优化采购流程的全面解析

SRM系统的询价竞价管理模块是现代企业采购管理中的重要工具。通过该模块&#xff0c;企业可以实现供应商的询价、竞价和合同管理等关键环节的自动化和优化。 一、概述 SRM系统是一种用于管理和优化供应商关系的软件系统。它通过集成各个环节&#xff0c;包括供应商信息管理、询…...

c++选择题笔记

局部变量能否和全局变量重名&#xff1f;可以&#xff0c;局部变量会屏蔽全局变量。在使用全局变量时需要使用 ":: "。拷贝构造函数&#xff1a;参数为同类型的对象的常量引用的构造函数函数指针&#xff1a;int (*f)(int,int) & max; 虚函数&#xff1a;在基类…...

Android2:构建交互式应用

一。创建项目 项目名Beer Adviser 二。更新布局 activity_main.xml <?xml version"1.0" encoding"utf-8"?><LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"…...

ChatGLM-6B微调记录

目录 GLM-130B和ChatGLM-6BChatGLM-6B直接部署基于PEFT的LoRA微调ChatGLM-6B GLM-130B和ChatGLM-6B 对于三类主要预训练框架&#xff1a; autoregressive&#xff08;无条件生成&#xff09;&#xff0c;GPT的训练目标是从左到右的文本生成。autoencoding&#xff08;语言理解…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...