笔者以前在捕捉视频图像后,遇到视频图像倾倒的问题,之后通过查阅博文找到解决方案。 但这不是主要的,主要的是javacv的代码笔者当时没有读过。 现在发送有详细评论的文档,分享使用心得。
目录1、首先上传源代码,每行注释2、使用注意事项1、使用javacv前记住依赖关系2、视频屏幕的Rotate属性3、最容易忽略的点:获取帧(帧) )。
一、先上传源代码,每行有评论package com.mu yichen.demo.javacv; import org.bytedeco.javacv.*; importorg.bytedeco.opencv.opencv _ core.iplimage; importorg.spring framework.util.string utils; import javax.imageio.ImageIO; import Java.awt.image.buffered image; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; importstaticorg.bytedeco.opencv.global.opencv _ core.cv flip; importstaticorg.bytedeco.opencv.global.opencv _ core.cv transpose; /** *捕获视频的图像工具类* @ authormuyichen * @ version 1.0 */publicclassframegrabberutil {/* * *捕获视频的图像工具* @ param file @param targetFilePath提取图像的存储路径* @param targetFileName提取图像的名称(全名:当前参数帧数) * @param randomSize取出图像数) @ throwsframegrabber.excer publicstaticvoidrandomgrabberffmpegimage (字符串文件路径,字符串目标String targetFileName,intrandomsize (throwsframegrabber.exception (/基于路径视频文件ffmpegframegrabberff=ffmpegframegframegrabbber rotate属性string rotate=ff.getvideometadata (‘ rotate ); //获取视频中包含多少帧intf flength=ff.getlengthinframes; //随机取5个帧位置的listintegerrandomgrab=random (fflength,randomSize ); //标记最后读取的位置(在此位置退出循环) intmaxrandomgrab=random grab.get (random grab.size ) (- 1 ); //遍历随机选择的帧位置Frame f; int i=0; while(Ifflength )//对应位置的帧f=ff.grabImage ); //如果当前帧是想要获取的帧,则读取该帧,并具有if(randomgrab.contains(I ) ) rotate属性,如果有,则需要对图像进行修正处理的if (! stringutils.isempty(rotate )//CVF帧图像转换器opencvframeconverter.toiplimageconverter=newopencvframeconverter.to////图像对象的修改处理完成后,转换为帧(Frame )对象f=converter.convert ) rotate(src,Inte
ger.parseInt(rotate))); } //将帧对象输出为具体的图片文件 doExecuteFrame(f, targetFilePath, targetFileName, i); } //如果到需要的帧取完了则跳出循环 if (i >= maxRandomGrab) { break; } i++; }; ff.stop(); } /** * 将图片根据rotate属性还原为它原来的状态 * @param src 原始图片对象 * @param rotate 旋转角度 * @return */ public static IplImage rotate(IplImage src, int rotate) { //创建一个和原图片宽高互换的图片框架 IplImage img = IplImage.create(src.height(), src.width(), src.depth(), src.nChannels()); //将原图片中元素的位置进行矩阵转置后放入新创建的图片框架中 cvTranspose(src, img); //将转置好后的图片根据rotate属性的值旋转对应的度数(根据纵轴旋转rotate度数) cvFlip(img, null, rotate); //返回最终生成的图片 return img; } /** * 将帧对象输出为具体的图片文件 * @param f 帧对象 * @param targetFilePath 输出图片文件存放路径 * @param targetFileName 输出图片文件名 * @param index 帧位置(视频的第几帧) */ public static void doExecuteFrame(Frame f, String targetFilePath, String targetFileName, int index) { if (null == f || null == f.image) { return; } //创建一个2D帧转换器 Java2DFrameConverter converter = new Java2DFrameConverter(); //设定图片的输出格式 String imageMat = “png”; //设定图片的全路径+图片名 String FileName = targetFilePath + File.separator + targetFileName + “_” + index + “.” + imageMat; //使用2D帧转换器将视频中的帧转换为图片缓冲对象 BufferedImage bi = converter.getBufferedImage(f); //根据图片的全路径名创建一个图片文件 File output = new File(FileName); try { //使用图片IO将图片缓冲对象写入新创建的图片文件中 ImageIO.write(bi, imageMat, output); } catch (IOException e) { e.printStackTrace(); } } /** * 随机选出指定长度范围类的几个数字 * @param baseNum 随机选出的数字个数 * @param length 指定长度范围 * @return */ public static List<Integer> random(int baseNum, int length) { //创建指定长度的接收数字集合对象 List<Integer> list = new ArrayList<>(length); //当集合对象未满时,循环选出 while (list.size() < length) { //获取指定范围类的随机数 Integer next = (int) (Math.random() * baseNum); //判断集合中是否存在当前随机出来的数,有的话则跳出当前循环进入下一循环 if (list.contains(next)) { continue; } //将指定范围随机数放入集合中 list.add(next); } //对集合进行排序 Collections.sort(list); return list; } public static void main(String[] args) throws Exception { randomGrabberFFmpegImage(“/Users/muyichen/Downloads/0420上传.mp4”, “/Users/muyichen/Downloads”, “screenshot”, 5); }} 二、使用注意事项 1、使用javacv之前记得倒入依赖 <dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.3</version></dependency>
注意各个版本之间的类方法位置可能不同,比如1.3.1版本中的IplImage是一个内部类,但是1.5.3的这个类却独立出来了。
2、注意视屏中的Rotate属性
Roate属性是导致图片由竖屏变横屏的主要原因,上述代码中有着详细的处理方法。
3、最容易忽视的一点:取帧(Frame)
使用FFmpegFrameGrabber的时候,需要将它当成一个流来处理,FFmpegFrameGrabber无法指定从确定位置取帧,只能一帧一帧的读取。grabImage()这个方法相当于流中的read()方法,它每读完一帧后都会从当前读取帧的下一帧位置开始读取。(也就是说如果要读取100帧位置的图片,那么他会先将前面的99帧都先读一遍后才能读到第100帧的图片)