JavaCVを使って動画ファイルから静止画ファイルを生成する方法

木曜日 , 25, 5月 2017 Leave a comment

OpenCVやFFmpegなど画像処理関連のライブラリをJavaで使えるようにしたJavaCV( https://github.com/bytedeco/javacv )を使い、動画ファイルを静止画ファイル群に変換するサンプルプログラムです。

サンプル中では動画ファイルとしてMP4形式を指定していますが、AVI形式やWMV形式なども使用できます。
また、出力する形式についてもJPEG形式以外にもPNG形式やMBP形式なども指定できます。

JavaCVのorg.bytedeco.javacv.FFmpegFrameGrabberクラスには、動画ファイルのフレームレートやサイズ、総フレーム数などを取得するプロパティが用意されているのですが、org.bytedeco.javacv.FFmpegFrameGrabber#startを呼び出すまでは、期待する値が返ってきません。

ループ内でorg.bytedeco.javacv.Java2DFrameConverter#convertを用いて動画からフレームごとの画像を切り出す部分も、かなりの頻度でnullが返ってきます。
これが動画ファイルに起因するものなのか、JavaCVまたはOpenCV/FFmpegなどに起因するものなのか、使用方法の誤りか現在のところ特定できていません。

また、org.bytedeco.javacv.FrameGrabber#getFrameNumberを用いてフレーム番号を取得する処理でも重複したフレーム番号が返ってきたり、番号が飛ぶなどの現象が起きましたが、こちらも原因の特定ができていません。

処理を実行すると、かなりの枚数の画像ファイルが生成されディスク容量を消費します。
実行の際にはディスクの空き容量にご注意ください。

動作確認を行った環境:

Java

Java SE Development Kit 8

JavaCV

1.3.2

ライセンス

NYSL

// This software is distributed under the license of NYSL.
// ( http://www.kmonos.net/nysl/ )

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.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;

public class 動画ファイルから静止画を切り出すプログラム {

	public static void main(String[] args) {
		new 動画ファイルから静止画を切り出すプログラム().slice(new File("./mp4/sample.mp4"), new File("./jpeg"));
	}


	/**
	 * 	動画ファイルをフレームごとに切り出し静止画に変換する
	 *	
	 *	@param srcMoviePath	動画ファイルのパス
	 *	@param destJpegPath	静止画ファイルの出力ディレクトリのパス
	 */
	public void slice(final File srcMoviePath, final File destJpegPath) {
		
		try {
			
			FFmpegFrameGrabber frameGrabber= new FFmpegFrameGrabber(srcMoviePath);
			Java2DFrameConverter frameConverter = new Java2DFrameConverter();
			
			frameGrabber.start();
			
			// FFmpegFrameGrabber#startを実行した後から、getFrameRateやgetLengthInFramesなどのプロパティで値が取得できるようになる 
			System.out.println("format:" + frameGrabber.getFormat() + ", size:" + frameGrabber.getImageWidth() + "x"
					+ frameGrabber.getImageHeight() + ", frame-rate:" + frameGrabber.getFrameRate()
					+ ", length-in-frame:" + frameGrabber.getLengthInFrames());

			int count = 0;
			
			// 現在のフレーム番号が動画ファイルの総フレーム数に到達するまでループ
			while (frameGrabber.getFrameNumber() < frameGrabber.getLengthInFrames()) {
				
				// フレームを取りだして
				BufferedImage img = frameConverter.convert(frameGrabber.grab());
				if (img == null) {
					// BufferedImageが取得できないことがあるので、そのときはスキップ
					continue;
				}
				
				// 静止画で出力
				// フレーム番号が重複することがある
			    ImageIO.write(img, "jpg", new File(destJpegPath, "frame-" + frameGrabber.getFrameNumber() + "-" + count + ".jpg"));
			    
			    count++;
			}
			
			frameGrabber.stop();
			frameGrabber.close();
				
		} catch (FrameGrabber.Exception e) {
			e.printStackTrace();;
		} catch (IOException e) {
			e.printStackTrace();;
		}
	}
}
Tags:, ,

Please give us your valuable comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください