Java音视频处理——JavaCV

目录

简介

Maven

软件环境

JavaCV-Examples

OpenCV Cookbook Examples

概述

示例

OpenCV文档

如何使用JavaCV示例

示例代码的组织结构

示例列表

Why Scala?

学习地址

图像简单处理代码示例

1.打开保存一张图

 2.画直线

3.画圆圈

4.画折现

5.添加文字水印

6.裁剪并局部放大

7.人脸检测

视频简单处理代码示例

1.打开视频文件

2.抓取视频指定时间的帧保存为图像

3.录屏

4.给视频添加水印


简介

JavaCV使用来自JavaCPP预设库的包装器,这些库是计算机视觉领域的研究人员常用的库(OpenCV, FFmpeg, libdc1394, FlyCapture, Spinnaker, OpenKinect, librealsense, CL PS3 Eye Driver, videoInput, ARToolKitPlus, flandmark, Leptonica和Tesseract),并提供实用程序类,使其功能更容易在Java平台(包括Android)上使用。

JavaCV还具有硬件加速全屏图像显示(CanvasFrame和GLCanvasFrame),易于使用的方法在多核上并行执行代码(parallel),用户友好的相机和投影仪的几何和颜色校准(GeometricCalibrator, ProCamGeometricCalibrator, ProCamColorCalibrator),特征点的检测和匹配(ObjectFinder),一组实现投影仪-相机系统的直接图像对齐的类(主要是GNImageAligner,projectvetransformer, projvecolortransformer, ProCamTransformer和ReflectanceInitializer),一个blob分析包(Blobs),以及JavaCV类中的其他功能。其中一些类也有OpenCL和OpenGL对应的类,它们的名字以CL结尾或以GL开头,例如:JavaCVCL, GLCanvasFrame等。

要学习如何使用API,因为目前缺乏文档,请参考下面的示例用法部分以及示例程序,包括两个Android (FacePreview.java和RecordActivity.java),也可以在samples目录中找到。您还可以参考ProCamCalib和ProCamTracker的源代码,以及从OpenCV2 Cookbook和相关wiki页面移植的示例。

Maven

  <dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.5.8</version>
  </dependency>

软件环境

要使用JavaCV,首先需要下载并安装以下软件:

Java SE 7或更新版本的实现:

此外,虽然并不总是必需的,JavaCV的一些功能也依赖于:

最后,请确保所有内容具有相同的位,32位和64位模块在任何情况下都不能混合。

JavaCV-Examples

这个项目包含使用JavaCV和其他javacpp-presets项目中的库包装器的示例。

  • OpenCV_Cookbook - Robert laganiere的书《OpenCV Computer Vision Application Programming Cookbook》中提供的示例的JavaCV版本。Cookbook中的原始示例是用c++编写的,这里它们被翻译为使用JavaCV API。

  • 使用flandmark库的示例。

  • 使用JVM包装flr /Point Grey FlyCapture SDK的示例。

  • 使用JVM包装Flir/Point Grey Spinnaker SDK的示例

OpenCV Cookbook Examples

概述

OpenCV Cookbook示例说明了OpenCV与JavaCV的使用。这些例子是从Robert laganie:1的书《OpenCV 2计算机视觉应用程序编程指南》中移植的c++代码开始的。后来更新为第四版的书“OpenCV计算机视觉应用程序编程 Cookbook第四版”。书中的示例使用OpenCV c++ API。在这里,它们被转换为使用JavaCV和JavaCPP-Presets api。

OpenCV(开源计算机视觉)是一个包含数百种算法的库,用于计算机视觉和视频分析。OpenCV可以通过两种方式在JVM上运行。首先是由OpenCV提供的Java包装器。第二种是基于JavaCPP (JVM的c++包装器引擎)的包装器,称为OpenCV JavaCPP预置。还有其他计算机视觉相关库的JavaCPP预设,如:FFmpeg, libdc1394, PGR FlyCapture, OpenKinect, videoInput, ARToolKitPlus, landmark等。JavaCV结合了JavaCPP预置中的库,并添加了一些额外的功能,使它们更容易在JVM上使用。

OpenCV Cookbook Examples项目演示了通过JavaCV和OpenCV JavaCPP预设使用OpenCV。当前的版本更新是为了匹配Robert laganie:1的书“OpenCV计算机视觉应用程序编程 Cookbook第二版”的第二版。它旨在与OpenCV .4一起使用(JavaCV 1节)。

虽然示例中的代码主要是用Scala编写的,Scala是领先的JVM语言之一。它可以很容易地转换为Java和运行在JVM上的其他语言,例如Groovy。在大多数JVM语言中,JavaCV API的使用非常相似。在Java版本中提供了一些示例。

示例

下面是一个快速预览,将原始c++示例与使用JavaCV包装器的Scala和Java代码进行比较。

下面是一个原始的c++示例,它打开一个图像(没有错误检查),创建一个窗口,在窗口中显示图像,并等待5秒后退出。

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>

int main() {
    // Read an image
    cv::Mat src = cv::imread("data/boldt.jpg");
    display(src, "Input")

	// Apply Laplacian filter
    cv::Mat dest;
    cv::Laplacian(src, dest, src.depth(), 1, 3, 0, BORDER_DEFAULT);
    display(dest, "Laplacian");

    // wait key for 5000 ms
    cv::waitKey(5000);

    return 1;
}

//---------------------------------------------------------------------------

void display(Mat image, char* caption) {
    // Create image window named "My Image"
    cv::namedWindow(caption);

    // Show image on window
    cv::imshow(caption, image);
}

上面的c++示例使用JavaCV包装器转换为Scala:

import javax.swing._
import org.bytedeco.javacv._
import org.bytedeco.opencv.global.opencv_core._
import org.bytedeco.opencv.global.opencv_imgcodecs._
import org.bytedeco.opencv.global.opencv_imgproc._
import org.bytedeco.opencv.opencv_core._

object MyFirstOpenCVApp extends App {

  // Read an image.
  val src = imread("data/boldt.jpg")
  display(src, "Input")

  // Apply Laplacian filter
  val dest = new Mat()
  Laplacian(src, dest, src.depth(), 1, 3, 0, BORDER_DEFAULT)
  display(dest, "Laplacian")

  //---------------------------------------------------------------------------

  /** Display `image` with given `caption`. */
  def display(image: Mat, caption: String): Unit = {
    // Create image window named "My Image."
    val canvas = new CanvasFrame(caption, 1)

    // Request closing of the application when the image window is closed.
    canvas.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)

    // Convert from OpenCV Mat to Java Buffered image for display
    val converter = new OpenCVFrameConverter.ToMat()
    // Show image on window
    canvas.showImage(converter.convert(image))
  }
}

现在用Java表达相同的例子。请注意,JavaCV API的使用与Scala和Java代码中的完全相同。唯一的实际区别是,在Java代码中更冗长,您必须显式地为每个变量提供类型,而在Scala中这是可选的。

import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.opencv.opencv_core.Mat;

import javax.swing.*;
import java.awt.image.BufferedImage;

import static opencv_cookbook.OpenCVUtilsJava.toBufferedImage;
import static org.bytedeco.opencv.global.opencv_core.BORDER_DEFAULT;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.bytedeco.opencv.global.opencv_imgproc.Laplacian;

public class MyFirstOpenCVAppInJava {

    public static void main(String[] args) {

        // Read an image.
        final Mat src = imread("data/boldt.jpg");
        display(src, "Input");

        // Apply Laplacian filter
        final Mat dest = new Mat();
        Laplacian(src, dest, src.depth(), 1, 3, 0, BORDER_DEFAULT);
        display(dest, "Laplacian");
    }

    //---------------------------------------------------------------------------

    static void display(Mat image, String caption) {
        // Create image window named "My Image".
        final CanvasFrame canvas = new CanvasFrame(caption, 1.0);

        // Request closing of the application when the image window is closed.
        canvas.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        // Convert from OpenCV Mat to Java Buffered image for display
        final BufferedImage bi = toBufferedImage(image);

        // Show image on window.
        canvas.showImage(bi);
    }
}

OpenCV文档

如果您正在寻找一个特定的OpenCV操作,请使用OpenCV文档(OpenCV documentation index)。快速搜索框特别有用。该文档包含了C/ c++ OpenCV API可选使用方式的描述。

如何使用JavaCV示例

OpenCV Cookbook示例项目是作为Robert laganie:1的书“OpenCV计算机视觉应用程序编程Cookbook第二版”的伙伴。推荐的方法是在对如何将Cookbook的c++代码转换为JavaCV有疑问时阅读Cookbook并参考JavaCV示例。这本书解释了算法是如何工作的。JavaCV示例只提供了与JavaCV API细节相关的非常简短的注释。

使用JavaCV示例的最简单方法是在线浏览位于[src/main]中的代码。您也可以使用Git或ZIP文件将其下载到计算机上。

通过最小的设置,您可以轻松地在自己的计算机上执行示例。这是JavaCV的好处之一——它提供了在各种平台上运行OpenCV所需的所有二进制文件。在第1章的README中解释了这个设置。

示例代码的组织结构

代码被组织成与Cookbook第1版中的章节相对应的包,例如opencv_cookbook.chapter8。它与第二版非常相似。个别的例子大致对应于书中每一章的章节。

第1章描述了运行这些示例的IDE设置,给出了一个加载和显示图像的基本示例,以及一个执行基本图像处理的基本GUI示例。

示例列表

第一章:使用图像

  • Ex1MyFirstOpenCVApp -加载图像并在窗口中显示它(CanvasFrame)

  • Ex2MyFirstGUIFXApp -使用ScalaFX (JavaFX包装器)构建的简单GUI应用程序。该应用程序在左侧有两个按钮“打开图像”和“处理”。打开的图像显示在中间。当按下“处理”按钮时,图像被翻转过来,其红色和蓝色通道被交换。有关Swing版本,请参阅Ex2MyFirstGUIApp。

  • Ex2MyFirstGUIApp -使用Scala Swing构建的简单GUI应用程序。该应用程序在左侧有两个按钮“打开图像”和“处理”。打开的图像显示在中间。当按下“处理”按钮时,图像被翻转过来,其红色和蓝色通道被交换。有关JavaFX版本,请参阅Ex2MyFirstGUIFXApp。

  • Ex3LoadAndSave -读取,保存,显示和绘制图像。

第二章:操纵像素

  • Ex1Salt -设置个人,随机选择,像素到一个固定的值。使用ImageJ的ImageProcessor来访问像素。

  • Ex2ColorReduce -通过相同的方式修改所有波段的颜色值来减少图像中的颜色。

  • ex3锐化-使用内核卷积锐化图像:filter2D()。

  • Ex4BlendImages -混合两个图像使用加权加法:cvAddWeighted()。

  • ex5roillogo -使用感兴趣的区域将小图像粘贴到较大的图像中:IplROI和cvCopy()。

第三章:用类处理图像

  • Ex1ColorDetector -将RGB颜色与目标颜色进行比较,与目标颜色相似的颜色在输出图像中被分配为白色,其他像素被设置为黑色。

  • Ex2ColorDetectorSimpleApplication—第一个示例的处理过程相同,但演示了一个简单的UI。

  • ex3colordetectormvapplication—相同的处理是第一个示例,但演示了一个更详细的UI。

  • Ex4ConvertingColorSpaces -类似于第一个例子,但是颜色距离是在Lab*颜色空间中计算的。说明cvtColor函数的使用。

第四章:用直方图计算像素

  • Ex1ComputeHistogram -使用实用程序类Histogram1D计算直方图,并将值打印到屏幕上。

  • Ex2ComputeHistogramGraph -显示使用实用程序类Histogram1D创建的直方图的图形。

  • Ex3Threshold -使用OpenCV threshold()方法将图像中的像素分离为前景和背景。

  • Ex4InvertLut—通过反转其查找表来创建倒立的图像。

  • Ex5EqualizeHistogram -使用直方图均衡化增强图像。

  • Ex6ContentDetectionGrayscale -使用灰度图像中区域的直方图来创建“模板”,通过整个图像来检测与该模板相似的像素。举例说明cvCalcBackProject()方法的使用。

  • Ex7ContentDetectionColor -使用区域直方图在彩色图像中创建“模板”,通过整个图像来检测与该模板相似的像素。依赖于工具类ColorHistogram和ContentFinder。

  • Ex8MeanShiftDetector -使用彩色图像中的区域直方图来创建“模板”,使用mean shift算法在另一张图像中找到“模板”的最佳匹配位置。举例说明cvMeanShift()方法的用法。

  • 使用辅助类ImageComparator计算图像相似度度量。

  • 执行直方图和查找表操作的Helper类,对应于OpenCV Cookbook中部分c++类Histogram1D的样例代码。举例说明OpenCV方法的使用:cvLUT(), cvEqualizeHist(), cvCreateHist(), cvCalcHist(), cvQueryHistValue_1D(), cvReleaseHist()。

  • ColorHistogram -帮助类简化了彩色图像cvCalcHist()方法的使用。

  • 使用cvCalcBackProject()方法进行模板匹配的辅助类。

  • ImageComparator -使用cvCompareHist()计算图像相似度的辅助类。

第五章:用形态学操作变换图像

  • 侵蚀和扩张-形态侵蚀和扩张:cvErode()和cvDilate()。

  • Ex2OpeningAndClosing -形态学打开和关闭:cvMorphologyEx()。

  • Ex3EdgesAndCorners -使用形态学过滤器检测边缘和角落。

  • Ex4WatershedSegmentation -图像分割使用分水岭算法。

  • Ex5GrabCut -使用grabCut()从背景中分离对象。

  • MorphoFeatures -等价于同名的c++类,包含形态学角检测的方法。

  • WatershedSegmenter -“使用分水岭分割图像”一节的辅助类。

第六章:过滤图像

  • Ex1LowPassFilter -模糊与高斯滤波器。

  • Ex2MedianFilter -用中值滤波器去除噪声。

  • Ex3DirectionalFilters -使用Sobel边缘检测滤波器。

  • 使用拉普拉斯滤波器的边缘检测。

  • LaplacianZC -计算Laplacian和过零,用于Ex4Laplacian。

第七章:提取线条、轮廓和组件

  • Ex1CannyOperator -检测轮廓与Canny算子。

  • Ex2HoughLines -使用标准Hough变换方法检测线条。

  • Ex3HoughLineSegments -使用概率霍夫变换方法检测线段。

  • Ex4HoughCircles -使用霍夫变换方法检测圆。

  • Ex5ExtractContours -使用连接的组件从二值图像中提取轮廓。

  • Ex6ShapeDescriptors -计算各种形状描述符:边界框,封闭圆,近似多边形,凸壳,质心。

  • LineFinder -使用概率霍夫变换方法检测线段的辅助类,用于Ex3HoughLineSegments。

第八章:发现兴趣点

  • Ex1HarrisCornerMap -计算哈里斯角强度图像。

  • Ex2HarrisCornerDetector -使用Harris角强度图像来检测定位良好的角,用一个角取代几个位置较近的检测(模糊)。使用HarrisDetector辅助类。

  • Ex3GoodFeaturesToTrack -使用GoodFeatures跟踪检测器的例子。

  • Ex4FAST -使用FAST检测器的示例。

  • Ex5SURF -使用SURF检测器的示例。

  • Ex6SIFT -使用SIFT检测器的示例。

  • 帮助类,用于哈里斯角强度图像的检测和定位。距离较近的探测(模糊)被单个探测取代。

第九章:发现兴趣点

  • Ex2TemplateMatching -从第一个图像(模板)和第二个图像中找到一个小补丁之间的最佳匹配。

  • ex7descripbingsurf -计算SURF特征,提取它们的描述符,并在同一物体的两幅图像之间找到最佳匹配的描述符。

第十章:估计图像中的投影关系

  • Ex1FindChessboardCorners -演示了相机校准步骤之一,在校准板中检测棋盘模式。

  • Ex2CalibrateCamera -相机校准示例,展示了如何校正光学器件可能引入的几何变形。使用CameraCalibrator助手类。

  • Ex3ComputeFundamentalMatrix -使用两幅图像之间检测和匹配的特征,计算描述两幅图像之间投影关系的基本矩阵。

  • Ex4MatchingUsingSampleConsensus -说明RANSAC(随机抽样共识)策略的使用。大多数计算是由RobustMatcher helper类完成的。

  • Ex5Homography -描述两个图像中点之间关系的另一种方法,使用homography。展示了如何将一个对象的部分视图的两个图像拼接在一起的示例。大多数计算是由RobustMatcher helper类完成的。

  • 实现摄像机校准算法的助手类。

  • RobustMatcher -实现基于RANSAC的算法,例如Ex4MatchingUsingSampleConsensus和Ex5Homography。

第十一章:处理视频序列

  • Ex1ReadVideoSequence -读取并显示视频。

  • Ex2ProcessVideoFrames -使用Canny边缘检测器处理视频文件中的帧;在屏幕上显示输出视频。使用辅助类VideoProcessor。

  • Ex3WriteVideoSequence -使用Canny边缘检测器处理视频文件中的帧;将输出写入视频文件。使用辅助类VideoProcessor。

  • Ex4TrackingFeatures -跟踪视频中的移动对象,标记屏幕上显示的视频中的跟踪点。大部分的实现是在FeatureTracker helper类中完成的。

  • Ex5ForegroundSegmenter -通过背景估计检测视频中的移动对象。背景使用“简单”移动平均方法建模,实现在助手类“BGFBSegmenter”中。

  • Ex6MOGMotionDetector -一个更复杂的运动检测器,使用混合高斯方法建模背景。

  • BGFBSEgmenter -通过使用移动平均线对背景建模,将“静态”背景与“移动”前景分开。使用的例子'Ex5ForegroundSegmenter'。

  • FeatureTracker -使用光流算法跟踪移动特征,例如Ex4TrackingFeatures。

  • VideoProcessor -处理视频文件的辅助类,加载和应用处理到单独的帧,例如:Ex2ProcessVideoFrames, Ex3WriteVideoSequence, Ex4TrackingFeatures和Ex5ForegroundSegmenter。

第十五章:OpenCV高级特性

  • Ex1FaceDetection -使用预训练的深度学习神经网络模型检测图像中的人脸。

其他通用技术

  • OpenCVUtils -读取和写入图像文件,显示图像,绘制图像的特征,OpenCV图像和数据表示之间的转换。

Why Scala?

之所以选择Scala,是因为它比Java更具表现力。您可以用更少的代码获得相同的结果。更小的样板代码使示例更容易阅读和理解。编译后的Scala代码速度很快,类似于Java和c++。

与Java或c++不同,Scala支持编写脚本——无需显式编译即可执行的代码。Scala也有一个控制台,称为REPL,可以在其中输入单行代码并当场执行。这两个特性使得在Scala中构建基于opencv的程序比在Java中更容易。最后但并非最不重要的一点是,IDE对Scala的支持达到了成熟的程度,允许轻松地创建、修改和执行Scala代码。特别是,用于JetBrains IDEA的Scala插件工作得非常好。还有对Eclipse和NetBeans的Scala支持。

学习地址

https://github.com/bytedeco/javacv

Welcome to OpenCV Java Tutorials documentation! — OpenCV Java Tutorials 1.0 documentation

GitHub - bytedeco/javacv-examples: Examples of using JavaCV / OpenCV library on Java Virtual Machine

关于类、API等如果有疑问可以看考openCV、FFmpeg的方法介绍,javaCV只不过是对它们的封装,或者直接问ChatGPT吧,简单高效!

图像简单处理代码示例

图像处理的API主要集中在opencv-4.6.0-1.5.8.jar包下,该包有两个目录“bytedeco.opencv”与“opencv”,两个package下有许多同名的类与静态方法,请尽量采用“bytedeco.opencv”package下的类与方法。

1.打开保存一张图

// 打开一张图
Mat image = imread("D:\\2projects_database\\javacvdemo\\src\\main\\java\\com\\example\\img\\future_city.jpg");
// 保存图像       imwrite("D:\\2projects_database\\javacvdemo\\src\\main\\java\\com\\example\\img\\future_city_add_text.jpg", image);

 2.画直线

/**
 * 画直线
 * @param image 图片
 * @param x1 起点横坐标
 * @param y1 起点纵坐标
 * @param x2 终点横坐标
 * @param y2 终点纵坐标
 * @param color 线条颜色
 */
public static void drawLine(Mat image, int x1, int y1, int x2, int y2, Scalar color) {
    Point pt1 = new Point(x1, y1);
    Point pt2 = new Point(x2, y2);
    line(image, pt1, pt2, color);
}

3.画圆圈

/**
 * 画圆圈
 * @param image 图像
 * @param x 圆心横坐标
 * @param y 圆心纵坐标
 * @param radius 半径
 * @param color 线条颜色
 * @param thickness 线条厚度
 * @param lineType 线条类型
 * @param shift 坐标值的小数位数
 */
public static void drawCircle(Mat image, int x, int y, int radius, Scalar color, int thickness, int lineType, int shift) {
    Point center = new Point(x, y);
    circle(image, center, radius, color, thickness, lineType, shift);
}

4.画折现

/**
 * 画折现
 * @param image 图像
 * @param points 端点数组
 * @param color 线条颜色
 */
public static void drawCurve(Mat image, Point[] points, Scalar color) {
    for (int i = 0; i < points.length - 1; i++) {
        line(image, points[i], points[i+1], color);
    }
}

5.添加文字水印

/**
 * 添加文字水印
 * @param image 图像
 * @param text 文字内容
 * @param position 文字位置
 * @param fontFace 字体类型
 * @param fontScale 字体大小
 * @param color 字体颜色
 */
public static void addTextWatermark(Mat image, String text, Point position, int fontFace, double fontScale, Scalar color) {
    putText(image, text, position, fontFace, fontScale, color);
}

6.裁剪并局部放大

/**
 * 裁剪图像并局部放大
 * @param image 图像
 * @param x 起始位置横坐标
 * @param y 起始位置纵坐标
 * @param width 裁剪宽度
 * @param height 裁剪高度
 * @param zoomFactor 放大倍数
 */
public static void cropAndZoomImage(Mat image, int x, int y, int width, int height, int zoomFactor) {
    Rect roi = new Rect(x, y, width, height);
    Mat croppedImage = new Mat(image, roi);
    resize(croppedImage, croppedImage, new Size(width*zoomFactor, height*zoomFactor));
    // 保存图像
    imwrite("D:\\2projects_database\\javacvdemo\\src\\main\\java\\com\\example\\img\\cropAndZoomImage.png", croppedImage);
    System.out.println("resize rows:" + croppedImage.rows());
    System.out.println("resize cols:" + croppedImage.cols());
}

7.人脸检测

package com.example.img.code;

import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_objdetect.CascadeClassifier;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imwrite;
import static org.bytedeco.opencv.global.opencv_imgproc.LINE_8;
import static org.bytedeco.opencv.global.opencv_imgproc.rectangle;

/**
 * @Author yrz
 * @create 2023/5/12 11:22
 * @Description TODO
 */

public class FaceDetector {
    public static void main(String[] args) {
        // Load the image
        Mat image = imread("D:/2projects_database/javacvdemo/src/main/java/com/example/img/face.jpg");

        // Load the face cascade classifier
        CascadeClassifier faceCascade = new CascadeClassifier("D:/2projects_database/javacvdemo/src/main/java/com/example/img/haarcascade_frontalface_alt.xml");

        // Detect faces in the image
        RectVector faceDetections = new RectVector();
        faceCascade.detectMultiScale(image, faceDetections);

        // Draw a rectangle around each detected face
        for (Rect rect : faceDetections.get()) {
            rectangle(image, new Point(rect.x(), rect.y()), new Point(rect.x() + rect.width(), rect.y() + rect.height()),
                    new Scalar(0, 255, 0, 0), 2, LINE_8, 0);
        }
        // Save the image with the detected faces
        imwrite("D:/2projects_database/javacvdemo/src/main/java/com/example/img/face_output.jpg", image);
    }
}

视频简单处理代码示例

视频处理的API主要集中在javacv-1.5.8.jar包下。

1.打开视频文件

/**
 * 打开视频文件
 * @param filename
 */
public static void readDisplayVideo(String filename){
    FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filename);
    // Open video video file
    try {
        grabber.start();
    } catch (FFmpegFrameGrabber.Exception e) {
        e.printStackTrace();
    }
    // Prepare window to display frames
    CanvasFrame canvasFrame = new CanvasFrame("Extracted Frame", 1);
    canvasFrame.setCanvasSize(grabber.getImageWidth(), grabber.getImageHeight());
    // Exit the example when the canvas frame is closed
    canvasFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    long delay = Math.round(1000d / grabber.getFrameRate());
    // Read frame by frame, stop early if the display window is closed
    Frame frame;
    try {
        while ((frame = grabber.grab()) != null && canvasFrame.isVisible()) {
            // Capture and show the frame
            canvasFrame.showImage(frame);
            // Delay
            Thread.sleep(delay);
        }
        // Close the video file
        grabber.release();
    } catch (FFmpegFrameGrabber.Exception e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

2.抓取视频指定时间的帧保存为图像

/**
 * 抓取视频指定时间的帧保存为图像
 * @param videoPath
 * @param imagePath
 * @param timeInSeconds
 */
public static void grabFrameAtTime(String videoPath, String imagePath, long timeInSeconds) {
    FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoPath);
    Java2DFrameConverter converter = new Java2DFrameConverter();
    try {
        grabber.start();
        grabber.setTimestamp(timeInSeconds);
        Frame grab = grabber.grabImage();
        BufferedImage bufferedImage = converter.getBufferedImage(grab, converter.getBufferedImageType(grab) ==
                BufferedImage.TYPE_CUSTOM ? 1.0 : 1.0, false, null);
        saveImage(bufferedImage, imagePath);
        grabber.stop();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public static void saveImage(BufferedImage image, String imagePath) {
    try {
        ImageIO.write(image, "jpg", new File(imagePath));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

3.录屏

/**
 * 录屏
 * @param filename 文件名称
 * @param seconds 时长
 */
public static void recordScreen(String filename, int seconds) {
    final int FRAME_RATE = 30;
    final Dimension SCREEN_SIZE = Toolkit.getDefaultToolkit().getScreenSize();
    // 创建录屏对象,并设置相关属性
    FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(filename, SCREEN_SIZE.width, SCREEN_SIZE.height);
    recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
    recorder.setFormat("mp4");
    recorder.setFrameRate(FRAME_RATE);
    Java2DFrameConverter converter = new Java2DFrameConverter();
    try {
        // 初始化录屏对象
        recorder.start();
        Robot robot = new Robot();
        BufferedImage screenShot;

        // 系统当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        // 30秒后
        LocalDateTime plus = now.plus(seconds, ChronoUnit.SECONDS);
        System.out.println(plus);

        // 开始录制
        while (true) {
            // 获取屏幕截图并写入文件
            screenShot = robot.createScreenCapture(new Rectangle(SCREEN_SIZE));
            recorder.record(converter.getFrame(screenShot));
            // 停止时间
            LocalDateTime time = LocalDateTime.now();
            if(plus.isBefore(time)){
                System.out.println(time);
                break;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 关闭录制器
        try {
            recorder.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.给视频添加水印

/**
     * 给视频添加水印
     * @param filename
     * @param outname
     * @param picName
     * @throws Exception
     */
    public static void addWatermark (String filename, String outname, String picName) throws Exception {
        // Load the video
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filename);
        grabber.start();
        // Create a new video recorder
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outname, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        recorder.setFormat("mp4");
        recorder.setFrameRate(grabber.getFrameRate());
        // 开启录制
        recorder.start();
        // Create a new Java2DFrameConverter
        Java2DFrameConverter converter = new Java2DFrameConverter();
        // Create a new BufferedImage to hold the watermark image
        // 图片水印
        BufferedImage watermarkImage = ImageIO.read(new File(picName));
        // 自定义文字水印
//        BufferedImage watermarkImage = createWatermarkImage("Hello, world!", new Font("Arial", Font.BOLD, 50),
//                Color.WHITE, new Color(0, 0, 0, 0));
        // Loop through each frame in the video
        Frame frame;
        while ((frame = grabber.grabFrame()) != null) {
            // Convert the frame to a BufferedImage
            BufferedImage image = converter.getBufferedImage(frame);
            if(image == null){
                continue;
            }
            // Create a new Graphics2D object to draw the watermark
            Graphics2D g2d = image.createGraphics();
            // Draw the watermark on the image
            g2d.drawImage(watermarkImage, 0, 0, null);
            // Dispose of the Graphics2D object
            g2d.dispose();
            // Convert the BufferedImage back to a Frame and write it to the output video
            recorder.record(converter.convert(image));
        }
        // Stop the grabber and recorder
        grabber.stop();
        grabber.release();
        recorder.stop();
        recorder.release();
    }

    private static BufferedImage createWatermarkImage(String text, Font font, Color foreground, Color background) {
        FontRenderContext frc = new FontRenderContext(null, true, true);
        TextLayout layout = new TextLayout(text, font, frc);
        Rectangle2D bounds = layout.getBounds();

        BufferedImage image = new BufferedImage(
                (int) bounds.getWidth(), (int) bounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = image.createGraphics();

        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

        if (background != null) {
            g2d.setBackground(background);
            g2d.clearRect(0, 0, image.getWidth(), image.getHeight());
        }

        g2d.setFont(font);
        g2d.setColor(foreground);

        layout.draw(g2d, 0, -(float) bounds.getY());

        if (background != null) {
            ColorConvertOp colorConvert = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB),
                    null);
            colorConvert.filter(image, image);
        }

        g2d.dispose();

        return image;
    }

猜你喜欢

转载自blog.csdn.net/qq_27890899/article/details/130968922