《Java程式碼成精了——會跳薩日朗的火柴人》實現步驟一:讀取視訊檔獲取每一幀圖片

2020-08-12 14:35:16

上次課,我們說明了《會跳薩日朗的火柴人》的思路,本小節我們實現第一步。就是讀取視訊檔,獲取每一幀對應的圖片。

 

1、準備工作

學習本篇文章,需要你事先已經掌握Java基礎知識,比如變數、數據型別、方法、異常、IO操作等。

FFMpegFrameGrabber

在java中的視訊操作,我們採用第三方的包。javacv是視覺領域常用的庫。在javacv封裝的ffmpeg包中,可以通過FFMpegFrameGrabber捕獲image數據和sample數據,而且捕獲的都是譯碼後的。

FFmpeg是一套可以用來記錄、轉換數位音訊、視訊,並能將其轉化爲流的開源計算機程式。

FFmpeg "這個單詞中的 "FF" 指的是 "Fast Forward ff + mpeg

MPEG(Moving Picture Experts Group,動態影象專家組)

BufferedImage

Image是一個抽象類,BufferedImage是其實現類,是一個帶緩衝區影象類,主要作用是將一幅圖片載入到記憶體中(BufferedImage生成的圖片在記憶體裡有一個影象緩衝區,利用這個緩衝區我們可以很方便地操作這個圖片),提供獲得繪圖物件、影象縮放、選擇影象平滑度等功能,通常用來做圖片大小變換、圖片變灰、設定透明不透明等。 圖片只有載入到記憶體中才能 纔能進行進一步的處理。

ImageIO

提供read()和write()靜態方法,讀寫圖片,比以往的InputStream讀寫更方便。

讀取:

public static BufferedImage read(File input)
public static BufferedImage read(InputStream input)
public static BufferedImage read(ImageInputStream stream)
public static BufferedImage read(URL input)
​

寫出:

RenderedImage介面的子類是BufferedImage,因此在這裏可以直接出傳入BufferedImage的範例化物件,將BufferedImage物件直接寫出指定輸出流

public static boolean write(RenderedImage im,String formatName,File output)
public static boolean write(RenderedImage im, String formatName,OutputStream output)
public static boolean write(RenderedImage im, String formatName,ImageOutputStream output)

比如將圖片縮小比例儲存。

範例程式碼:

package day02.demo01;
​
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
​
import javax.imageio.ImageIO;
​
public class Demo09_BufferedImage {
  public static void main(String[] args) throws IOException {
    /**
     * BufferedImage類: Image是一個抽象類,BufferedImage是其實現類,是一個帶緩衝區影象類,
     * 主要作用是將一幅圖片載入到記憶體中(BufferedImage生成的圖片在記憶體裡有一個影象緩衝區,
     * 利用這個緩衝區我們可以很方便地操作這個圖片),提供獲得繪圖物件、影象縮放、選擇影象平滑度等功能,
     * 通常用來做圖片大小變換、圖片變灰、設定透明不透明等。
     * 
     * 圖片只有載入到記憶體中才能 纔能進行進一步的處理。
     * 
     * RGB:R(紅)G(綠)B(藍)色彩模式是工業界的一種顏色標準。在Java中每個RGB畫素所佔的位數爲8.
     */
    // 讀取本地一張圖片
    File file = new File("C:\\Ruby\\pro\\aa\\kakaxi.jpg");// 本地圖片
    System.out.println(file.exists());
    BufferedImage image = (BufferedImage) ImageIO.read(file);
    // 繼而可以對圖片進行操作,比如,獲得圖片的寬度:image.getWidth()
    // 獲取影象的寬度和高度,原始寬度和高度
    int width = image.getWidth();
    int height = image.getHeight();
​
    // 按照比例縮小
    BufferedImage image2 = new BufferedImage(width / 2, height / 2, BufferedImage.TYPE_INT_BGR);// 表示具有8位元RGB顏色分量的影象
    /**
     * 繪製 縮小 後的圖片,如果想要放大圖片,就乘以 Graphics,圖形
     */
    Graphics g = image2.getGraphics();
    File file2 = new File("C:\\Ruby\\pro\\aa\\kakaxi2.jpg");
    g.drawImage(image, 0, 0, width / 2, height / 2, null);
    ImageIO.write(image2, "jpg", file2);
​
  }
}
​

在本地C:\Ruby\pro\aa目錄下,就會有一張縮小了一倍多kakaxi2.jpg圖片。


2、操作思路

首先準備好視訊檔,在本地C:\Ruby\薩日朗目錄下。然後需要建立一個File物件表示這個視訊檔,然後根據視訊檔物件,建立FFmpegFrameGrabber物件,就是按照視訊中每一幀進行抓取圖片的抓取物件。

然後可以獲取該視訊一共有多少幀。通過回圈,抓取每一幀的圖片,通過ImageIO將圖片進行儲存即可。

3、程式碼實現

說明:需要先匯入第三方jar包。如圖:

然後建立一個java專案,並新建一個java檔案,命名爲:D1_VideoUtil.java

範例程式碼:

package demo;
​
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
​
import javax.imageio.ImageIO;
​
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber.Exception;
import org.bytedeco.javacv.Java2DFrameConverter;
​
/**
 * 專案的第一步:讀取薩日朗的視訊檔,獲取每一幀的圖片
 * @author ruby
 *
 */
public class D1_VideoUtil {
​
  public static void main(String[] args) {
    //1.要讀取的視訊檔路徑
    String videoPath = "C:\\Ruby\\薩日朗\\薩日朗原視訊.wmv";
    //2.圖片要儲存的目錄位置
    String picPath = "C:\\Ruby\\薩日朗\\視訊幀\\";
    
    File video = new File(videoPath);
    getVideoPic(video, picPath);
  }
  
  /**
   * 根據讀取到的視訊檔,獲取視訊中的每一幀圖片
   * @param video 視訊檔
   * @param picPath 圖片的儲存路徑
   */
  public static void getVideoPic(File video,String picPath){
    //1.根據一個視訊檔,建立一個按照視訊中每一幀進行抓取圖片的抓取物件
    FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video);
    try {
      ff.start();
      //抓每一幀圖片
      //2.先知道這個視訊一共有多少幀
      int length = ff.getLengthInFrames();
      System.out.println(length);
      //3.讀取視訊中每一幀圖片
      Frame frame = null;
      for(int i=0;i<length;i++){
        frame = ff.grabFrame();
//        System.out.println(frame);
        if(frame.image == null){
          System.out.println("空"+i);
          continue;
        }
        //將獲取的幀,儲存爲圖片
        Java2DFrameConverter converter = new Java2DFrameConverter();//建立一個幀-->圖片的轉換器
        BufferedImage image = converter.getBufferedImage(frame);//轉換
        String img = picPath+i+".jpg";
        System.out.println(img);
        File picFile = new File(img);
        //將圖片儲存到目標檔案中
        ImageIO.write(image, "jpg", picFile);
      }
      
      
      ff.stop();
    } catch (Exception e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    
  }
​
}
​

執行結果如下:

因爲上課需要和學生的操作系統,所以我在自己的mac電腦上安裝了windows虛擬機器。只有一個C槽。

在本地硬碟上:建立目錄結構:C:\Ruby\薩日朗\視訊幀

我們可以看到在該目錄下,已經儲存了從視訊中讀取到的這些圖片。