ともさんのHP >プロブラミング >USBカメラをJAVAで制御して定点観測 >Javaで動画ファイルの保存
OpenCVでUSBカメラの画像をMP4動画保存
JavaFXでUSBカメラから得た画像を動画ファイルに保存するアプリを作りましたので紹介します。
顕微鏡に工業用のレンズの外せるCマウントタイプの
UBSカメラ
楽天 を取り付けて観察の様子を動画でPCに表示できるようにしました。
これを録画するためのアプリをJavaFX楽天 で作っています。
OpenCvを使っているので、これをインストールしておく必要があります。
Java
楽天 はバージョン8以上が必要。
録画の概要
まずUSB
カメラ
楽天 を接続したビデオキャプチャーをオブジェクトを作ります。
//OpenCvのライブラリを呼び出す
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//イメージとカメラの初期化
mat_image = new Mat();
//PCに繋がっている0番目のUSBカメラを呼び出す
capture =new VideoCapture(0);
動画保存用にVideoWriterオブジェクト作ります
int fourcc = VideoWriter.fourcc('m', 'p', '4', 'v');
vw = new VideoWriter( "C:\test.mp4" ,fourcc,30,new Size(capture.get( 3 ),capture.get( 4 )));
タイムラインを使って定期的に処理を行います
この定期処理の中で、
ビデオキャプチャーからマットイメージを取り出し、ビデオライターに渡します
//USBカメラから画像を取り込む capture.read(mat_image); //録画作業 vw.write( mat_image );
JavaのStage上で表示するためににマットイメージをJavaのWritableImageに変更して表示させます。
変更するメソッド「matToWritableImage(mat_image);」はネットでいただいてきました。
録画開始はスタートボタンを押すことで始めます。
終了ボタン楽天 を押すとビデオライターが閉じられてファイルが保存されます
注意する点
ビデオキャプチャーで得た画像の大きさと保存時に設定する画像の大きさは一致していないといけません。
一致していない場合は保存されません。エラーも出ないので原因ががなかなかわかりませんでした。
USBカメラの接続は番号で決まります。複数のカメラが繋がれた状態の場合は、先に繋がれたカメラが早い番号になります。
デスクトップであれば一台ですが、カメラが内蔵されているノートパソコンの場合は、内蔵カメラが一番目に来るようです。
その場合はカメラの番号を変更して使います。
タイムラプスとスローモーション収録
タイムラインで行う定期的な処理の処理間隔と保存時に設定するFPSは一致しておくと通常速度になります。
定期処理の間隔を大きくしたりFPSの値を大きくすると早回しの動画ができます。
定期処理の間隔を極端に大きくすれば、タイムラプス動画が作れます。
逆の設定にすればスローモーション動画になりますが、普通のUSBカメラのFPSは30です。30では動画の再生スピードも30FPSが普通なので、スローモーションはちょっと作りづらいです。
私が顕微鏡に取り付けた工業用のUSBカメラは330FPS出せるので、スローモーション撮影も可能です。
撮影してみました
下の動画がこのアプリで実際に撮影したものです。
画像が横に長いのは、USBカメラの設定によるものです。
水槽の青くなった水を少々を組み上げて観察してみました。顕微鏡を使って動くものを撮影するには大変良いツールができたと思います。
応用
今回は単純にボタンを押して録音のオンオフをするようにしましたが、画像から動体検知をして、自動的に録画するようにもできそうです。
また明るさを検知して、暗くなったらオフにするなどの操作もできるでしょう。
暗いところでも撮影できる高感度のカメラが接続できれば、流星を検知して流れたらすぐに録画できるアプリが作れて面白いかなと思います。
今回購入した工業用のUSBカメラは、レンズが外せるので顕微鏡に取り付けが可能です。
また、FPSが普通のUSBカメラの10倍ぐらいあるので、スローモーション撮影もできます。
このUSBカメラを購入したのは、スリットカメラをソフトウェア的に実現するのが目的で、FPS330であれば、1秒間に捜査線を330本得ることができるためです。
特徴的のあるカメラを接続すれば、いろいろ応用した使い方ができそうですね。
ソースコード
全ソースコードは以下の通り
package tomojavalib.usbcamera;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.VideoWriter;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
import javafx.util.Duration;
import tomojavalib.util.Jikoku;
/**
* 顕微鏡撮影用のステージ
*2020/3/4作成開始
* */
public class MicroScopeStage extends Application {
//ImageViewを置くパネル
FlowPane ctpane =null;
FlowPane toppane = null;
BorderPane bpane = new BorderPane();
Stage stage = null;
//撮影開始ボタン
Button stbutton = null;
//撮影開始ボタン
Button stpbutton = null;
//satuei フラグ
int satuei = 0;
//撮影終了フラグ
boolean stop = false;
//captureから受け取った画像
Mat mat_image=null;
//カメラ
VideoCapture capture =null;
//画像の入れ物
ImageView imgView = null;
//ファイル保存用
VideoWriter vw =null;
//タイマー
Timeline timer1=null;
//録画フラグ
boolean saveflug = false ;
boolean savestart = false ;
//---メイン---
public static void main(String[] args) { Application.launch(args); }
/**スタート*/
@Override
public void start( Stage stage ) throws Exception {
//ステージにpane等をレイアウトする
this.layoutPane( stage );
this.setEvent();
//カメラを取得する
//OpenCvのライブラリを呼び出す
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
System.out.println(Core.NATIVE_LIBRARY_NAME);
//イメージとカメラの初期化
mat_image=new Mat();
//PCに繋がっている0番目にUSBカメラを呼び出す
capture =new VideoCapture(0);
//カメラ幅をセット
//capture.set( 3 , 1200 );
//カメラ幅の設定
//capture.set( 4 , 240. );
//FPSのセット
capture.set( 5 , 1. );
System.out.println("Usbカメラ幅 "+capture.get( 3 )+" 高さ "+capture.get( 4 )+" FPS "+capture.get( 5 ));
//アニメーションスタート
timer1Start();
return;
}
/**ビデオライターを起動する*/
private void makeVideoWriter() {
int fourcc = VideoWriter.fourcc('m', 'p', '4', 'v');
Jikoku j = new Jikoku();
String s = j.stringNow()+".mp4";
vw = new VideoWriter( "D:/work/digi_came-kuro/test/" + s ,fourcc,30,new Size(capture.get( 3 ),capture.get( 4 )));
}
/**イベントを設定する*/
private void setEvent() {
EventHandler<MouseEvent> stbuttonClicked = ( event ) -> this.startbuttonClicked( event );
stbutton.addEventHandler( MouseEvent.MOUSE_CLICKED , stbuttonClicked );
EventHandler<MouseEvent> stpbuttonClicked = ( event ) -> this.stopbuttonClicked( event );
stpbutton.addEventHandler( MouseEvent.MOUSE_CLICKED , stpbuttonClicked );
return;
}
/**マウスボタンがクリックされたとき*/
private void startbuttonClicked( MouseEvent e ){
this.saveflug = true;
return;
}
/**マウスボタンがクリックされたとき*/
private void stopbuttonClicked( MouseEvent e ){
this.saveflug = false;
this.savestart = false;
vw.release();
return;
}
/**パネルのレイアウト*/
private void layoutPane( Stage stage ) {
this.stage = new Stage();
ctpane = new FlowPane();
toppane = new FlowPane();
bpane = new BorderPane();
Scene scene = new Scene(bpane, 1300, 800);
bpane.setCenter( ctpane );
bpane.setTop( toppane );
this.stage.setX(100);
this.stage.setY(100);
this.stage.setScene(scene);
this.stage.show();
stbutton = new Button("撮影開始");
toppane.getChildren().add( stbutton );
stpbutton = new Button("撮影終了");
toppane.getChildren().add( stpbutton );
imgView = new ImageView( );
this.ctpane.getChildren().add( imgView );
}
int ix=0;
private void timer1Start() {
timer1 = new Timeline(new KeyFrame(Duration.millis(30), new EventHandler<ActionEvent>(){
@Override
public void handle( ActionEvent event ){
timer1.pause();
//撮影前は画像表示
if( capture.isOpened()){
capture.read(mat_image);
//録画作業
if( saveflug ) {
if( savestart==false) { savestart = true; makeVideoWriter(); }
vw.write( mat_image );
}
WritableImage wimg = matToWritableImage(mat_image);
imgView.setImage( wimg );
}
timer1.play();
}
}));
timer1.setCycleCount(Timeline.INDEFINITE);
timer1.play();
return;
}
/**Matイメージをjavafx.scene.image.WritableImageに変換する*/
public static WritableImage matToWritableImage(Mat matrix) {
int cols = matrix.cols();
int rows = matrix.rows();
int elemSize = (int)matrix.elemSize();
int[] rgb = new int[cols * rows ];
byte[] data = new byte[cols*rows*elemSize];
matrix.get(0, 0, data);
switch (matrix.channels()) {
case 1:
//TYPE_BYTE_GRAY;
break;
case 3:
//TYPE_3BYTE_BGR;
int ix=0;
for(int i=0; i<data.length; i=i+3) {
rgb[ix] = (-1<<8)+data[i+2]; rgb[ix] = rgb[ix]<<16;
int g =(int)data[i+1];g =g<<8;
rgb[ix] = rgb[ix] +g;
rgb[ix] = rgb[ix] +data[i];
ix++;
}
break;
default:
return null;
}
WritableImage image = new WritableImage( cols , rows );
int ix=0;
for(int ii=0;ii<image.getHeight() ;ii++) {
for(int i=0;i<image.getWidth();i++ ) {
image.getPixelWriter().setArgb(i, ii, rgb[ix] );
ix++;
}}
return image;
}
}
最終更新日: 2020-03-05 07:50:52
ともさんのHP >プロブラミング >USBカメラをJAVAで制御して定点観測 >Javaで動画ファイルの保存
ツイート