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

 更新时间:2023年08月25日 08:34:55   作者:myprincess003  
这篇文章主要为大家详细介绍了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.properties
    private 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 size
        Rectangle 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.properties
    private 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 size
        Rectangle 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 JavaCV视频截取的资料请关注脚本之家其它相关文章!

相关文章

  • JAVA 使用正则提取A标签以及href链接

    JAVA 使用正则提取A标签以及href链接

    这篇文章主要介绍了JAVA 使用正则提取A标签以及href链接的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • SpringBoot3.x集成nacos并实现多环境配置的操作步骤

    SpringBoot3.x集成nacos并实现多环境配置的操作步骤

    本文详细介绍了如何在Springboot3.x中集成Nacos2.x版本,包括nacos的安装、配置更改,以及在集成过程中遇到的问题,如端口设置、依赖版本调整等,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2024-10-10
  • Java enum关键字不识别的快速解决办法

    Java enum关键字不识别的快速解决办法

    这篇文章主要介绍了Java enum关键字不识别的快速解决办法,非常不错,具有参考借鉴价值,感兴趣的朋友一起看看吧
    2016-09-09
  • Java中List使用stream流转成map的几种方式详解

    Java中List使用stream流转成map的几种方式详解

    Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作,下面这篇文章主要给大家介绍了关于Java中List使用stream流转成map的几种方式,需要的朋友可以参考下
    2023-04-04
  • Java Hashtable机制深入了解

    Java Hashtable机制深入了解

    HashTable是jdk 1.0中引入的产物,基本上现在很少使用了,但是会在面试中经常被问到。本文就来带大家一起深入了解一下Hashtable,需要的可以参考一下
    2022-09-09
  • 部署Nacos的源码环境搭建过程

    部署Nacos的源码环境搭建过程

    这篇文章主要为大家介绍了部署Nacos的源码环境搭建过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • JAVA实现网络/本地图片转BASE64存储代码示例

    JAVA实现网络/本地图片转BASE64存储代码示例

    这篇文章主要给大家介绍了关于JAVA实现网络/本地图片转BASE64存储的相关资料,Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法,需要的朋友可以参考下
    2023-07-07
  • Spring中的@Conditional注解使用和原理详解

    Spring中的@Conditional注解使用和原理详解

    这篇文章主要介绍了Spring中的@Conditional注解使用和原理详解,@Conditional在Spring4.0中被引入,用于开发"If-Then-Else"类型的bean注册条件检查,在@Conditional之前,也有一个注解@Porfile起到类似的作用,需要的朋友可以参考下
    2024-01-01
  • Spring动态加载bean后调用实现方法解析

    Spring动态加载bean后调用实现方法解析

    这篇文章主要介绍了Spring动态加载bean后调用实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • Java中ShardingSphere分库分表实战

    Java中ShardingSphere分库分表实战

    我们做项目的时候,数据量比较大,单表千万级别的,需要分库分表,本文主要介绍了Java中ShardingSphere分库分表实战,感兴趣的可以了解一下
    2021-09-09

最新评论