从视频中截取指定帧图片

前言:
我们在很多时候需要对视频文件进行分析,或者对视频产生缩略图。因此视频截取技术必不可少。

从本地文件中读取视频帧

导包

 		<dependency><groupId>org.jcodec</groupId><artifactId>jcodec</artifactId><version>0.2.5</version></dependency><dependency><groupId>org.jcodec</groupId><artifactId>jcodec-javase</artifactId><version>0.2.5</version></dependency><!-- http://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>

读取视频帧工具类

package com.wkl.testdemo.vedio;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.jcodec.api.FrameGrab;
import org.jcodec.api.JCodecException;
import org.jcodec.common.model.Picture;
import org.jcodec.scale.AWTUtil;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.UUID;/*** 视频操作工具类*/
@Slf4j
public class VideoUtils {/*** 图片格式*/private static final String FILE_EXT = "jpg";/*** 帧数-第几帧*/private static final int THUMB_FRAME = 500;/*** 获取指定视频的帧并保存为图片至指定目录** @param videoFilePath 源视频文件路径* @param frameFilePath 截取帧的图片存放路径*/public static void fetchFrame(String videoFilePath, String frameFilePath) throws Exception {File videoFile = new File(videoFilePath);File frameFile = new File(frameFilePath);getThumbnail(videoFile, frameFile);}/*** 获取指定视频的帧并保存为图片至指定目录** @param videoFile  源视频文件* @param targetFile 截取帧的图片*/public static void fetchFrame(MultipartFile videoFile, File targetFile) throws Exception {File file = new File(videoFile.getName());FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);getThumbnail(file, targetFile);}/*** 获取指定视频的帧并保存为图片至指定目录** @param videoFile 源视频文件*/public static File fetchFrame(MultipartFile videoFile) {String originalFilename = videoFile.getOriginalFilename();File file = new File(originalFilename);File targetFile = null;try {FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);int i = originalFilename.lastIndexOf(".");String imageName;if (i > 0) {imageName = originalFilename.substring(0, i);} else {imageName = UUID.randomUUID().toString().replace("-", "");}imageName = imageName + ".jpg";targetFile = new File(imageName);getThumbnail(file, targetFile);} catch (Exception e) {log.error("获取视频指定帧异常:", e);} finally {if (file.exists()) {file.delete();}}log.debug("视频文件 - 帧截取 - 处理结束");return targetFile;}/*** 获取第一帧缩略图** @param videoFile  视频路径* @param targetFile 缩略图目标路径*/public static void getThumbnail(File videoFile, File targetFile) {try {// 根据扩展名创建一个新文件路径Picture picture = FrameGrab.getFrameFromFile(videoFile, THUMB_FRAME);BufferedImage bufferedImage = AWTUtil.toBufferedImage(picture);ImageIO.write(bufferedImage, FILE_EXT, targetFile);} catch (IOException | JCodecException e) {e.printStackTrace();log.error("获取第一帧缩略图异常:", e);}}public static void main(String[] args) {try {long startTime = System.currentTimeMillis();getThumbnail(new File("D:\\Videos\\2023112911533869304715.mp4"), new File("D:\\Videos\\test1.jpg"));System.out.println("截取图片耗时:" + (System.currentTimeMillis() - startTime));} catch (Exception e) {e.printStackTrace();}}public static void saveImage(String imageUrl, String destinationFile) throws IOException {URL url = new URL(imageUrl);InputStream is = url.openStream();OutputStream os = new FileOutputStream(destinationFile);byte[] b = new byte[2048];int length;while ((length = is.read(b)) != -1) {os.write(b, 0, length);}is.close();os.close();}}

从网络url 读取视频帧-ffmpeg

导包

 <!-- 获取视频第一帧依赖 --><dependency><groupId>org.bytedeco</groupId><artifactId>javacpp</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.bytedeco.javacpp-presets</groupId><artifactId>ffmpeg-platform</artifactId><version>3.4.2-1.4.1</version></dependency>

工具类

package com.wkl.testdemo.vedio;import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;/*** 视频抽帧工具*/
@Slf4j
public class VideoFrame {//传入包含特定时间的Frame ,读取图片public static BufferedImage doExecuteFrame(Frame frame, int index) {if (frame == null || frame.image == null) {return null;}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(frame);return bi;}/** @description:  读取视频第n秒的图片帧* @author: wangkanglu* @date: 2023/12/8 15:05* @param: [videoUrl, stepSecond]* @return: java.awt.image.BufferedImage**/public static BufferedImage doExecuteFrameByTime(String videoUrl, Integer stepSecond) {FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);ff.setOption("timeout", "40000000");long timestamp =  stepSecond * 1000000L;    //视频是按照微秒计算的,10的6次方分之一秒try {ff.start();ff.setTimestamp(timestamp);Frame frame = ff.grabImage();if (frame == null || frame.image == null) {return null;}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(frame);ff.stop();return bi;} catch (FrameGrabber.Exception e) {throw new RuntimeException(e);}}/*** 视频文件边下载边抽帧1秒1帧** @param videoUrl   网络视频文件URL* @param stepSecond 每隔几秒取一帧,默认1s* @param count      需要截取的帧个数* @return*/public static Map<Integer, BufferedImage> videoUrlIntercept(String videoUrl, Integer stepSecond, Integer count) {Map<Integer, BufferedImage> files = new HashMap<>();stepSecond = stepSecond == null ? 1 : stepSecond;FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);// 设置超时时间为40秒ff.setOption("timeout", "40000000");
//        ff.setOption("user_agent", UserAgent.getUserAgent());try {ff.start();long timeLength = ff.getLengthInTime();Frame frame = ff.grabImage();long startTime = frame.timestamp;long timestamp = 0; //视频的当前时长int second = 0;  //过了几个时间间隔int picNum = 0;//第n张帧while (timestamp <= timeLength) {log.info("抽取第{}帧,video_url:{}",picNum,videoUrl);timestamp = startTime + second * 1000000L;    //视频是按照微秒计算的,10的6次方分之一秒ff.setTimestamp(timestamp);frame = ff.grabImage();if (frame != null) {if (frame.image != null) {BufferedImage bufferedImage = doExecuteFrame(frame, picNum);if (bufferedImage != null) {files.put(picNum, bufferedImage);}picNum++;if (count != null && picNum == count) {break;}}}second += stepSecond;if(picNum > 60) {break;}}ff.stop();} catch (Exception e) {log.error("下载抽帧失败,ipPort:{},videoUrl:{},msg:{}", null, videoUrl, e.getMessage());e.printStackTrace();}return files;}/*** 视频文件指定时间段的帧截取** @param videoUrl  视频文件URL* @param start     视频开始的帧* @param count     需要截取的帧个数* @param isAvgTime 在截帧时 是否均匀分布计算时间* @return*/public static Map<Integer, BufferedImage> videoIntercept(String videoUrl, int start, int count, boolean isAvgTime) {log.info("开始抽取视频帧数,videoUrl:{}",videoUrl);Frame frame = null;//<时间, 图片流>Map<Integer, BufferedImage> files = new HashMap<>();FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(videoUrl);fFmpegFrameGrabber.setOption("timeout", "40000000");try {fFmpegFrameGrabber.start();long frameTime = 1;if (isAvgTime) {frameTime = fFmpegFrameGrabber.getLengthInTime() / count / 1000000L;if (frameTime < 0) {frameTime = 1;}}for (int i = start; i <= count; i++) {fFmpegFrameGrabber.setTimestamp(i * frameTime * 1000 * 1000);frame = fFmpegFrameGrabber.grabImage();BufferedImage bufferedImage = doExecuteFrame(frame, i);if (bufferedImage != null) {files.put(i, bufferedImage);}}fFmpegFrameGrabber.stop();} catch (Exception E) {log.info("下载的视频抽帧失败,msg:" + E.getMessage());E.printStackTrace();}return files;}/** @description:  BufferedImage 转 inputStream* @author: wangkanglu* @date: 2023/12/8 14:52* @param: [image]* @return: java.io.InputStream**/public static InputStream bufferedImageToInputStream(BufferedImage image) {ByteArrayOutputStream os = new ByteArrayOutputStream();try {ImageIO.write(image, "jpg", os);InputStream input = new ByteArrayInputStream(os.toByteArray());return input;} catch (IOException e) {}return null;}public static void main(String[] args) throws IOException {String videoUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";Map<Integer, BufferedImage> integerInputStreamMap = videoUrlIntercept(videoUrl, 1, 13);System.out.println(integerInputStreamMap.size());for (Integer seconds : integerInputStreamMap.keySet()) {BufferedImage bufferedImage = integerInputStreamMap.get(seconds);String fileName = System.currentTimeMillis()+"抖音测试3" + "_" + seconds + ".jpg";String filePath = "D:\\Videos\\"+fileName;//本地磁盘存储// 本地图片保存地址ImageIO.write(bufferedImage, "png", new File(filePath));System.out.println("seconds: " + seconds + ", uploadURL: " + filePath);}}
}

附赠压缩图片工具类


package com.wkl.testdemo.vedio;import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.stereotype.Component;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;@Slf4j
@Component
public class VideoFrameGrabber {public static void main(String[] args) throws Exception {// 视频地址String vedioUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";BufferedImage image = doExecuteFrameByTime(vedioUrl, 10);double targetSize = 10*1024;while (imageToBytes(image).length > targetSize) {float reduceMultiple = 0.5f;image = resizeImage(image, reduceMultiple);}// 本地图片保存地址ImageIO.write(image, "png", new File("D:\\Videos\\test6.jpg"));}/** @description:  读取视频第n秒的图片帧* @author: wangkanglu* @date: 2023/12/8 15:05* @param: [videoUrl, stepSecond]* @return: java.awt.image.BufferedImage**/public static BufferedImage doExecuteFrameByTime(String videoUrl, Integer stepSecond) {FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);ff.setOption("timeout", "40000000");long timestamp =  stepSecond * 1000000L;    //视频是按照微秒计算的,10的6次方分之一秒try {ff.start();ff.setTimestamp(timestamp);Frame frame = ff.grabImage();if (frame == null || frame.image == null) {return null;}Java2DFrameConverter converter = new Java2DFrameConverter();BufferedImage bi = converter.getBufferedImage(frame);ff.stop();return bi;} catch (FrameGrabber.Exception e) {throw new RuntimeException(e);}}/*** 通过BufferedImage图片流调整图片大小* 指定压缩后长宽*/public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {Image resultingImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);return outputImage;}/*** 通过BufferedImage图片流调整图片大小* @param originalImage* @param reduceMultiple 缩小倍数* @return* @throws IOException*/public static BufferedImage resizeImage(BufferedImage originalImage, float reduceMultiple) throws IOException {int width = (int) (originalImage.getWidth() * reduceMultiple);int height = (int) (originalImage.getHeight() * reduceMultiple);Image resultingImage = originalImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);return outputImage;}/*** 压缩图片到指定大小* @param srcImgData* @param reduceMultiple 每次压缩比率* @return* @throws IOException*/public static byte[] resizeImage(byte[] srcImgData, float reduceMultiple) throws IOException {BufferedImage bi = ImageIO.read(new ByteArrayInputStream(srcImgData));int width = (int) (bi.getWidth() * reduceMultiple); // 源图宽度int height = (int) (bi.getHeight() * reduceMultiple); // 源图高度Image image = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = tag.getGraphics();g.setColor(Color.RED);g.drawImage(image, 0, 0, null); // 绘制处理后的图g.dispose();ByteArrayOutputStream bOut = new ByteArrayOutputStream();ImageIO.write(tag, "JPEG", bOut);return bOut.toByteArray();}/*** BufferedImage图片流转byte[]数组*/public static byte[] imageToBytes(BufferedImage bImage) {ByteArrayOutputStream out = new ByteArrayOutputStream();try {ImageIO.write(bImage, "jpg", out);} catch (IOException e) {e.printStackTrace();}return out.toByteArray();}/*** byte[]数组转BufferedImage图片流*/private static BufferedImage bytesToBufferedImage(byte[] ImageByte) {ByteArrayInputStream in = new ByteArrayInputStream(ImageByte);BufferedImage image = null;try {image = ImageIO.read(in);} catch (IOException e) {e.printStackTrace();}return image;}}

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

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

相关文章

http代理和SOCK5代理谁更安全?

在这个网络化的时代&#xff0c;我们常常听到HTTP代理和SOCKS5代理这两个名词&#xff0c;不过很多人并不了解是什么意思。今天&#xff0c;我们将揭开这两种代理的神秘面纱&#xff0c;看看到底HTTP代理和SOCKS5代理哪个更安全&#xff1f; HTTP代理&#xff1a;高效通信的“枢…

Anaconda+Pytorch(GPU版)深度学习环境配置笔记

主要参考以下文章进行配置&#xff1a; https://blog.csdn.net/qq_43757976/article/details/131173301 配置版本略有更新&#xff0c;最新版本时间为2023.12.11 一、准备工作 个人电脑配置&#xff1a;laptop RTX4060 win11 个人配置版本&#xff1a;cuda&#xff08;12.1&…

JS:让2个li标签排列在同一行

前言 在js中&#xff0c;ul元素中li标签是块级元素&#xff0c;现在需要让2个分行的li元素显示在同一行&#xff0c;并且去掉li元素自带的标记符号 li元素处理前的样式如下&#xff1a; 实现 html代码 <div><ul><li>数据1&#xff1a;</li><li&…

微服务和无服务器架构时代的持续测试

软件开发中对速度和敏捷性的追求催生了超越传统界限的方法和实践。持续测试是现代 DevOps 实践的基石&#xff0c;它已经发展到满足加速软件交付的需求。在本文中&#xff0c;我们将探讨持续测试的最新进展&#xff0c;重点关注它如何与微服务和无服务器架构相结合。 一、持续…

第十六届山东省职业院校技能大赛中职组网络安全赛项竞赛正式试题

第十六届山东省职业院校技能大赛中职组网络安全"赛项竞赛试题 一、竞赛时间 总计&#xff1a;360分钟 二、竞赛阶段 竞赛阶段任务阶段竞赛任务竞赛时间分值A、B模块A-1登录安全加固180分钟200分A-2本地安全策略设置A-3流量完整性保护A-4事件监控A-5服务加固A-6防火墙策…

人机交互——自然语言理解

人机交互中的自然语言理解是人机交互的核心&#xff0c;它是指用自然语言&#xff08;例如中文、英文等&#xff09;进行交流&#xff0c;使计算机能理解和运用人类社会的自然语言&#xff0c;实现人机之间的自然语言通信。 自然语言理解在人工智能领域中有着非常重要的地位&a…

【力扣】刷题备忘录-动归-343. 整数拆分

343. 整数拆分 class Solution { public:int integerBreak(int n) {vector<int> dp(n1);dp[2] 1;for (int i 3; i < n; i) {for (int j 1; j < i - 1; j){ // 这里j的最大值去到i-2就可以&#xff0c;这时i - j 2 正好能用初始化的值dp[i] max(dp[i], max(j …

系统报错;由于找不到hid.dll,无法继续执行代码”的解决方案分享

在计算机使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“找不到hid.dll&#xff0c;无法继续执行代码”。这个错误提示通常表示计算机缺少了一个重要的动态链接库文件&#xff0c;即hid.dll。本文将详细介绍hid.dll丢失对电脑的影响以及hid.dll是…

【Python网络爬虫入门教程2】成为“Spider Man”的第二课:观察目标网站、代码编写

Python 网络爬虫入门&#xff1a;Spider man的第二课 写在最前面观察目标网站代码编写 第二课总结 写在最前面 有位粉丝希望学习网络爬虫的实战技巧&#xff0c;想尝试搭建自己的爬虫环境&#xff0c;从网上抓取数据。 前面有写一篇博客分享&#xff0c;但是内容感觉太浅显了…

vite脚手架,配置动态生成路由,添加不同的layout以及meta配置

实现效果&#xff0c;配置了layout和对应的路由的meta 我想每个模块添加对应的layout&#xff0c;下边演示一层layout及对应的路由 约束规则&#xff1a; 每个模块下&#xff0c;添加对应的 layout.vue 文件 每个文件夹下的 index.vue 是要渲染的页面路由 每个渲染的页面路由对…

Appium python自动化测试系列之移动自动化测试!

1.1 移动自动化测试现状 因为软件行业越来越发达&#xff0c;用户的接受度也在不断提高&#xff0c;所以对软件质量的要求也随之提高&#xff0c;当然这个也要分行业&#xff0c;但这个还是包含了大部分。因为成本、质量的变化现在对自动化测试的重视度越来越高&#xff0c;在…

CTF-misc(1)图片隐写

笔记目录 渗透测试工具(1)wireshark渗透测试工具(2)Nmap渗透测试工具(3)BurpsuiteAWD比赛(1)AWD入门攻略大纲CTF-Web(2)SQL注入CTF-Web(3)文件上传漏洞 图片隐写目录 (1)GIf和二维码隐写 二维码补全 二维码绘图 Gif规律分析 (2)文本附加图片隐写 (3)IHDR文件头修复图片宽高 (…

linux端口转发

使用iptables 例如要将本地的8080端口转发到80端口&#xff0c;你可以使用以下命令&#xff1a; sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080这将把进入80端口的流量重定向到8080端口。 使用socat 另一种方法是使用socat工具。首先&am…

⭐Unity 搭建UDP服务端(02)接收客户端消息

客户端在上一篇 由于服务器逻辑写的较为简单 所以直接上代码了~ using System; using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine;public class UdpServer : MonoBehaviour {public static UdpServer instance;private void Awake(){if (…

Springboot管理系统数据权限过滤——ruoyi实现方案

本文主要简述&#xff0c;Ruoyi框架使用的权限过滤实现方案&#xff0c;实现简单易懂。主要知识点有&#xff1a; 注解定义&#xff1b;面向切面编程&#xff0c;在执行有数据权限注解的方法之前获取用户组织权限&#xff0c;拼接到domain对象的params参数中&#xff1b; 1. …

AI:100-基于卷积神经网络的农作物生长状态监测

🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的核心代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新…

基于CMT2300A定制的模组谐波测量及调试事例

1.1 芯片介绍 CMT2300A华普微推出的一款超低功耗 Sub-1GHz 射频收发器&#xff0c;是一款SPI接口射频前端芯片&#xff0c;调制方式支持OOK (G)FSK 、(G)MSK&#xff0c;速率最大可以做到300 kbps&#xff0c;休眠大概1uA&#xff0c;功率最大可以做到20dB&#xff0c;但各国的…

Android 删除浏览器导航页面修改默认主页

Android 删除浏览器导航页面修改默认主页 近来收到客户需求反馈&#xff0c;需要删除浏览器导航页面并将百度设置为默认主页&#xff0c;具体修改参照如下&#xff1a; 删除浏览器导航页面&#xff1a; /vendor/mediatek/proprietary/packages/apps/Browser/src/com/android…

软文怎么写才能让消费者行动起来?媒介盒子分享

软文的本质是营销&#xff0c;做营销文案不是玩文字艺术&#xff0c;它需要洞察用户需求&#xff0c;懂产品&#xff0c;了解卖点&#xff0c;懂营销&#xff0c;懂消费心理&#xff0c;最终让消费者行动起来。有些文案可能在你看起来遣词造句和配图都很一般&#xff0c;但就是…

分布式uuid常用的算法

1、雪花算法介绍 面试官&#xff1a;集群高并发情况下如何实现分布式唯一全局id生成&#xff1f; - 墨天轮 2、百度的UidGenerator 介绍&#xff0c;适合容器化配置&#xff0c;同时兼容springboot&#xff0c;只需要mysql数据库&#xff0c; https://github.com/baidu/uid-…