はじめに
過去に作成しようとして私的な理由によりとん挫したクロマキー合成を利用したWindows Java/Android向けのアプリケーションを作りました。まあまあ作り込んだので眠らせておくのはもったいないので、新たなクロマキー合成アプリとして陽の目を見せてあげようと計画しています。
今回は、このアプリケーションで利用するJavaCVについて簡単に紹介したいと思います。
JavaCVとは?OpenCV + FFmpegのJavaラッパー
JavaCVとは、画像の加工などで使用されるC/C++ライブラリのOpenCVというツールをJavaで利用できるようにしたラッパーです。
さらにこのJavaCVは、前回記事で紹介した動画エンコードツールであるFFmpegのラッパーも含んでいるため、動画合成 && 画像処理を一手に処理することが出来ます。
つまり**これを使うとJava(つまりAndroidアプリケーション)で動画編集を行うことが出来る!**ということに。
日本語の情報サイトは少ないですが、公式のsampleも豊富なのと、結局根っこはOpenCV or FFmpegの技術に紐づくので、豊富なOpenCV or FFmpeg情報から、JavaCVのsampleで利用されている方法が紐づけば、大抵のことは出来ます。
今回の動作環境
開発環境
OS: Windows
開発環境:Eclipse IDE for Java Developers
ソフトウェアバージョン
開発当時:
JavaCV: 1.3.3
opencv in JavaCPP: 3.2.0-1.3
ffmpeg in JavaCPP: 3.2.0-1.3
※以下でも簡単に動作確認済み
JavaCV: 1.4.2
opencv in JavaCPP: 3.4.2-1.4.2
ffmpeg in JavaCPP: 4.0.1-1.4.2
JavaCVのインストールはこちらを参照ください。
eclipseは、、、頑張って!(笑)
JavaCVでのFFmpeg機能(動画の入出力)
FFmpegではffmpeg -i 入力動画 オプション色々設定 出力動画
という風に1手で動画の入力⇒出力を実行していましたが、JavaCVでは細かな制御が出来るように入力, 出力それぞれようのクラスが存在し、1フレーム毎に制御を行う仕様となっています。
JavaCVでの動画入力
FFmpegFrameGrabber
クラスを使います。基本は3手順
1.動画の読み込み開始
import org.bytedeco.javacv.FFmpegFrameGrabber;
mInputGrabber = new FFmpegFrameGrabber(inputFile);
mInputGrabber.start();
2.フレーム毎にフレーム取得(音声データも動画データもどちらも"Frame"として扱われます)
frame=mInputGrabber.grab();
grabImageとgrabSamplesで動画だけ、音声だけの取得も可能です。
3.必要な読み込みが終わったらclose
mInputGrabber.stop();
mInputGrabber.release();
動画情報は1フレームごとに取得できるので、OpenCV機能を使って加工した上で動画に保存するのがいいでしょうね。
その他、フレームレートなどの基本情報が取得できます。
framerate = mInputGrabber.getFrameRate());
JavaCVでの動画出力
こちらもJavaCVでの動画入力と同様、1フレームごとの操作となります。
1. FFmpegFrameRecorderクラスを作成する。(FFmpegのエンコードパラメーターを渡すと、動画を保存できる口だけ返してくれる)
以下を見てもらえるとわかるのですが、FFmpegのオプションを色々使用することが出来ます。filterも一部使用可能(本記事で記述)
FFmpegFrameRecorder mRecorder = new FFmpegFrameRecorder(streamUrl, imageWidth, imageHeight, audioSample);//パス、サイズ、音声を使うかを指定
mRecorder.setFormat(format);//動画の拡張子
mRecorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);//動画の拡張子
mRecorder.setVideoQuality(videoQuality);//映像品質
mRecorder.setFrameRate(frameRate);//フレームレート
if(audioSample != 0) {
mRecorder.setAudioQuality(audioQuality);//音声フレームレート
mRecorder.setSampleRate(sampleAudioRateInHz);//音声サンプルレート
mRecorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);//音声コーデック。大体AAC
}
mRecorder.setVideoOption("preset", "ultrafast");//エンコード速度設定。FFmpegならはるかに速くなりますが、こちらでどこまで効くかは未検証
mRecorder.setTimestamp(0);//フレーム制御なのでtimestampで動画時間を調整します
mRecorder.setGopSize((int)((double)frameRate*gop));//GoP長(Iフレーム間隔) 長いとサムネが取れないけど動画サイズが大きくなるジレンマ
mRecorder.start();//録画開始
2. 同クラスにOpenCVの機能で作成したフレーム情報を登録
画像フレームの場合は以下。setTimestapmでタイムスタンプを設定するのがポイントです。
frame.mTimestamp = 1000 * (System.currentTimeMillis() - startTime);
//保存
if (mRecorder.getTimestamp() < frame.mTimestamp) {
mRecorder.setTimestamp(frame.mTimestamp);
}
mRecorder.record(mYuvImage);
フレーム番号をずらしていくという方法もあります。こっちの方が安定するかな
mRecorder.setFrameNumber(mFrameNumber++);
mRecorder.record(frame);
音声フレームの場合は以下。methodが違うんですね。動かしている感じsetTimestapmはいらないです。
mRecorder.recordSamples(audio);
後は処理が重いので、WindowsならまだいいですがAndroidなら音声と動画の保存を分けてmulti thread化するなど、負荷分散の仕組みを実施するといいですね。
JavaCVでのopenCV機能(画像処理)
こちらはMatと呼ばれるOpenCVで使用される画像データを管理するためのクラスへの変換処理がいるだけで、後は大体OpenCVと同じことが出来ます。
(一部古い機能が非サポートだったりしますが)
Matは、OpenCVで使用される画像データを管理するためのクラスです。唯の配列なんですが、画像の色空間等によってデータの配列が変わる為画像処理、特に色空間に関する知識が必要です。
1.FrameをMatに変換する。
FrameはJavaCV独自のクラスなので、OpenCVの恩恵を受けるためにFrame⇒Matに変換, MatでOpenCVの機能を利用、再度Frame変換という手順が必要です。
確かMatはnewするけど、convert自体はshallowコピーだったはず。
ここだけに限らずMat操作は結構shallow copy/deep copyの違いでメモリリーク or ぬるぽが発生するのがハマるポイントです。Frame変換があるから余計に厄介。
//Frame⇒Mat
OpenCVFrameConverter.ToMat toMat = new OpenCVFrameConverter.ToMat();
toMat.convert(frame);
//Mat⇒Frame
OpenCVFrameConverter.ToMat toMat = new OpenCVFrameConverter.ToMat();
toMat.convert(frame);
2.OpenCVでのMatを利用した画像処理
Matを使った操作は色々なものがあります。私が利用した機能を抜粋して軽く紹介。
クロマキー合成のメイン処理については次回詳細を紹介します。
メソッド | 機能 | 備考 |
---|---|---|
copyTo(srcMat, mRetMat); | Matの上書き | 同サイズでないと使用できない |
Rect(x, y, width, height); | 画像の部分抜き出し | 上記制限に利用 |
cvtColor(srcMat, retMat,色空間); | 色空間の変換 | |
threshold(gray, retMat, mThresholdValue, mMaxValue, mThresholdType ); | 画像を白と黒の2値化する | |
addWeighted(effectMat, mSrcWeight, srcMat, mEffectWeight, mAllWeight, mDistMat); | 全体の透過 |
JavaCVを利用して編集した動画例
これらの機能を利用すれば、前回記事で紹介した動画合成や、クロマキー合成や透過合成が出来ます。
参考までに前回のクロマキー合成動画も乗せておきます。微妙にですけど出力の感じが違うのがわかるかと思います。
次回
ここまででJavaCVでの動画編集で使用した基本的な機能を紹介しました。ここで勘のいい方なら、**「JavaCVでFFmpeg使えるなら、フィルター機能を使えばいいじゃん!」**と思ったかもしれません。しかし、少なくとも自分の環境では色々試してみてもFFmpegのfilter_complexでの綺麗な合成が出来なかったんですよね。
(色空間がぐちゃぐちゃになっていしまいました)
なのでクロマキー処理は自作しています。次に計画してるアプリももうちょっとクロマキーを工夫したい部分もあるので新バージョンでの検証もしないです。
フィルターが使えない話、クロマキー処理の話については、次回の記事で説明したいと思います。
⇒こちらに記載しました
参考
色空間などの参考。画処理を学んでいた後輩に教えてもらった教科書です。やっぱり教科書は基本的な考えを抑えるには便利ですね。
デジタル画像処理(書籍)
ゴリラさんのサイトは情報の整理された親切なサイトです。
ゴリラになる知識 色の扱い
サイズ変更等の参考
画像処理¶
Mat仕様参考
Class opencv_core.Mat
その他様々なOpenCVに詳しい方のサイト達。沢山参考にしたのですが、サイト情報記録がありませんでした、申し訳ありません。。
…関係ないですが、眠れないからってQiitaの記事を書くのはよくないですね。振り返ると色々気になる点が出てくるから余計眠れなくなる。