JAVACV+ffmpeg+OpenCV实现视频叠加合成(AR效果视频生成)
opencv
OpenCV: 开源计算机视觉库
项目地址:https://gitcode.com/gh_mirrors/opencv31/opencv
免费下载资源
·
同时抓取两个视频的帧,在叠加合并时进行一些图像处理,并将合并后的帧写入输出文件,最后释放资源。
具体实现步骤:
-
准备工作:
- 导入所需的库,这里主要是 OpenCV 和 JavaCV 相关的库。
- 创建一个
OpenCVFrameConverter.ToIplImage
对象converter
,它用于将 JavaCV 帧转换为 OpenCV 的IplImage
对象。
-
设置视频抓取器:
- 创建两个
FFmpegFrameGrabber
对象,分别用于原始视频文件和待合并的视频文件。 - 设置连接超时时间和读取数据超时时间。
- 启动两个抓取器并获取视频的长度(单位:微秒),用于后续的处理。
- 创建两个
-
设置视频参数:
- 检查并调整两个视频的帧率,确保它们相同。
- 设置输出视频的宽度和高度。
- 计算视频的宽高比例,并计算目标高度。
-
设置临时文件和输出文件:
- 获取系统临时文件目录,创建临时文件对象。
- 设置输出文件路径,并准备好文件名。
-
设置录制器参数:
- 创建一个
FFmpegFrameRecorder
对象,用于录制合并后的视频。 - 设置输出编码器、音频采样率、帧率、视频比特率、视频编码器等参数。
- 创建一个
-
抓取和处理视频帧:
- 循环抓取原始视频的帧,同时抓取待合并视频的帧。
- 如果当前帧是图像帧,则进行叠加处理:
- 将 JavaCV 帧转换为 OpenCV 的 Mat 对象,进行图像边界处理。
- 将 JavaCV 帧转换为 BufferedImage,并创建 Graphics2D 对象。
- 抓取待合并视频的帧,进行相应处理。
- 将待合并视频的帧叠加到当前帧上,并写入输出文件。
- 如果当前帧包含音频,则也将音频写入输出文件。
-
释放资源:
- 停止并释放所有的抓取器和录制器。
- 关闭颜色转换过滤器。
OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage(); System.out.println(mergeFilePath); FFmpegFrameGrabber frameGrabber1 = new FFmpegFrameGrabber(file); FFmpegFrameGrabber frameGrabber2 = new FFmpegFrameGrabber(mergeFilePath); frameGrabber2.setOption("timeout", "5000000"); // 设置连接超时时间 frameGrabber2.setOption("stimeout", "20000000"); // 设置读取数据超时时间 Frame frame = null; Frame mergeFrame1= null; Frame mergeFrame = null; FFmpegFrameRecorder recorder = null; String fileName = null; try { frameGrabber1.start(); frameGrabber2.start(); int videoLengthInSeconds = (int) (frameGrabber1.getLengthInTime() / 1000000); int videoLengthInSeconds2 = (int) (frameGrabber2.getLengthInTime() / 1000000); System.out.println("秒数"+videoLengthInSeconds+"-----------"+videoLengthInSeconds2); System.out.println(frameGrabber1.getImageWidth()+"11111111111111"+frameGrabber1.getImageHeight()); double frameRate = frameGrabber2.getFrameRate(); if (frameRate != frameGrabber1.getFrameRate()) { frameGrabber1.setFrameRate(frameRate); } frameGrabber2.setFrameRate(24); frameGrabber1.setFrameRate(24); // 计算两个视频的长度(单位:秒) // 设置帧率 frameGrabber1.setImageWidth(1080); frameGrabber1.setImageHeight(1920); int outputWidth = 1080; // 合成后视频的宽度 int outputHeight = 1920; // 根据视频的宽高比例计算目标高度 double videoRatio1 = (double) frameGrabber1.getImageWidth() / frameGrabber1.getImageHeight(); double videoRatio2 = (double) frameGrabber2.getImageWidth() / frameGrabber2.getImageHeight(); // 获取图像宽和高 int imageWidth = outputWidth; int imageHeight = outputHeight; int imageWidth1 = frameGrabber2.getImageWidth();; int imageHeight1 = frameGrabber2.getImageHeight(); long sourceVideoDuration = frameGrabber1.getLengthInTime(); long mergeVideoDuration = frameGrabber2.getLengthInTime(); FFmpegFrameFilter colorConvertFilter = new FFmpegFrameFilter("format=rgb24", imageWidth, imageHeight); colorConvertFilter.setPixelFormat(avutil.AV_PIX_FMT_RGB24); colorConvertFilter.start(); FFmpegFrameFilter filter = new FFmpegFrameFilter("colorkey=black", imageWidth1, imageHeight1); filter.setPixelFormat(avutil.AV_PIX_FMT_RGB24); filter.start(); // 设置时间戳,确保两个视频的起始点一致 // 随机生成一个新的文件名 Random random = new Random(); long name1 = System.currentTimeMillis() + RandomUtils.nextInt(100, 9999); // 获取系统默认的临时文件目录 String tempDir = System.getProperty("java.io.tmpdir"); // 创建临时文件对象8 File tempFile = new File(tempDir); String path = tempFile.getAbsolutePath(); fileName = path +File.separator+ String.valueOf(name1) + ".mp4"; ajaxResult.put("fileUrl", domainName + String.valueOf(name1) + ".mp4"); ajaxResult.put("filePath",fileName); System.out.println("--文件名-->>" + fileName); // 设置输出编码器、音频采样率、帧率、时间戳、视频比特率、视频编码器 // 创建临时文件流记录器,将输出流作为参数传入 recorder = new FFmpegFrameRecorder(fileName, imageWidth, imageHeight, frameGrabber1.getAudioChannels()); recorder.setFormat("mp4"); recorder.setSampleRate(frameGrabber1.getSampleRate()); recorder.setVideoBitrate(frameGrabber1.getVideoBitrate()); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFrameRate(24); recorder.setTimestamp(frameGrabber2.getTimestamp()); // 启动录制器和另一个视频的抓取器 frameGrabber2.setVideoCodec(avcodec.AV_CODEC_ID_H264); frameGrabber2.setPixelFormat(avutil.AV_PIX_FMT_RGB24); frameGrabber2.setVideoBitrate(frameGrabber1.getVideoCodec()); frameGrabber2.setFrameRate(frameGrabber1.getVideoCodec()); recorder.start(); int index = 0; // 获取视频的总帧数 int totalFrameCount = frameGrabber2.getLengthInFrames(); System.out.println("视频总帧数:" + totalFrameCount); int totalFrameCount1 = frameGrabber1.getLengthInFrames(); System.out.println("视频总帧数:" + totalFrameCount1); int xum =700; while (index < 500) { // 抓取当前视频的一帧 frame = frameGrabber1.grabFrame(); if (frame == null) { break; } // 如果当前帧是图像,则进行叠加 if (frame.image != null) { // 将JavaCV帧转换为OpenCV的Mat对象,方便后续处理颜色通道 Mat mat = converter.convertToMat(frame); // 进行图像边界处理,例如裁剪掉边界黑线条 // 将JavaCV帧转换为BufferedImage,并创建Graphics2D对象 BufferedImage buffImg = Java2DFrameUtils.toBufferedImage(frame); Graphics2D graphics = buffImg.createGraphics(); // 抓取另一个视频的一帧 mergeFrame1 = frameGrabber2.grabFrame(); if(mergeFrame1!=null){ Mat mat1 = converter.convertToMat(mergeFrame1); int borderSize = 1; mat1 = mat1.apply(new Rect(borderSize, borderSize, mat1.cols() - 2 * borderSize, mat1.rows() - 2 * borderSize)); // 将处理后的 Mat 对象转换回帧对象 mergeFrame = converter.convert(mat1); } frameGrabber2.setImageMode(FrameGrabber.ImageMode.COLOR); if (mergeFrame != null) { // 如果当前帧是空帧,则一直循环直到抓取到带图像的帧 while (mergeFrame.image == null) { mergeFrame = frameGrabber2.grabFrame(); frameGrabber2.setImageMode(FrameGrabber.ImageMode.COLOR); } // 进行颜色空间转换(例如,将图像从BGR转换为RGB) // cvtColor(mat, mat, COLOR_BGR2RGB); // 判断颜色空间是否需要转换 // if (mat.channels() > 1) { // cvtColor(mat, mat, COLOR_BGR2RGB); // } // 将另一个视频的帧应用颜色过滤器,并将结果转换为BufferedImage filter.push(mergeFrame); Frame filteredFrame = filter.pullImage(); BufferedImage mergeBuffer = Java2DFrameUtils.toBufferedImage(filteredFrame); // 进行颜色空间转换(例如,将图像从BGR转换为RGB // cvtColor(converter.convertToMat(filteredFrame), converter.convertToMat(filteredFrame), COLOR_BGR2RGB); // 进行直方图均衡化或颜色映射等处理(根据具体需求进行选择和调整) // 将另一个视频的帧叠加到当前帧上 graphics.drawImage(mergeBuffer, 0, 0, outputWidth, outputHeight, null); // graphics.drawImage(mergeBuffer, 0, 0, frameGrabber1.getImageWidth(), frameGrabber1.getImageHeight(), null); }else { break; } // 将叠加后的JavaCV帧写入输出文件 Frame newFrame = Java2DFrameUtils.toFrame(buffImg); recorder.record(newFrame); } // 如果当前帧包含音频,则将其写入输出文件 if (frame.samples != null) { recorder.recordSamples(frame.sampleRate, frame.audioChannels, frame.samples); } if(mergeFrame1 != null){ if (mergeFrame1.samples != null) { recorder.recordSamples(mergeFrame1.sampleRate, mergeFrame1.audioChannels, mergeFrame1.samples); } } System.out.println("帧值=" + index); index++; } // 停止并释放资源 recorder.stop(); recorder.release(); filter.stop(); colorConvertFilter.stop(); frameGrabber2.stop(); frameGrabber1.stop(); //在业务侧写代码时记得处理掉这个临时文件 // if (file!=null){ // file.deleteOnExit(); // } } catch (Exception e) { e.printStackTrace(); }
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.5.2</version> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacpp</artifactId> <version>1.5.2</version> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>opencv</artifactId> <version>4.1.2-1.5.2</version> <classifier>windows-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>openblas</artifactId> <version>0.3.7-1.5.2</version> <classifier>windows-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>4.2.1-1.5.2</version> <classifier>windows-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>opencv</artifactId> <version>4.1.2-1.5.2</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>openblas</artifactId> <version>0.3.7-1.5.2</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>4.2.1-1.5.2</version> <classifier>linux-x86_64</classifier> </dependency>
GitHub 加速计划 / opencv31 / opencv
162
15
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:4 个月前 )
796adf5d
Avoid adding value to nullptr 15 小时前
1a6ef7e0
Fixed Android build with Vulkan support. 18 小时前
更多推荐
已为社区贡献1条内容
所有评论(0)