JavaFx でWebcam Capture APIを試す

Webcam Capture API

「Webcam Capture API」は、「http://webcam-capture.sarxos.pl/」からダウンロードできます。
カメラ制御APIである。これを使うことでWebカメラ制御・撮影用のJava アプリケーションが簡単にできます。
取得できる基本的なイメージは、BufferedImageです。
ダウンロードしたwebcam-capture-0.3.10-dist.zipの中の次の3つjarファイルをプロジェクトフォルダにコピーし、 「ビルドパス」で参照させて利用します。

JavaFxでの利用

JavaFxのImageViewとCanvasに描画させる例です。 実験で使ったカメラサイズ(176,144)の縦横をそれぞれ4倍にしたイメージビューに表示させる。
またこのイメージは左右を反転したイメージを 生成して使っています。 (createMirrorImageメソッド定義して行う)
なお、オリジナルのサイズのCanvasに、 取得したBufferedImageで描画しています。
またラベルを用意し、4倍にしたイメージビュー上のマウス移動イベントで、 その位置の座標とその画素の色を表示させます。
以上の仕様を実現するソースの例を示します。

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.imageio.ImageIO;

import com.github.sarxos.webcam.Webcam;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

class MyWebcaomPane extends Pane {
	CameraFx app;//作品全体参照用

	ImageView imageViewWebcam = new ImageView();

	Webcam webCam = null;//カメラオブジェクト
	BufferedImage img;//カメラから取得したイメージ
	BufferedImage imgRev;//上記の左右反転イメージ

	Dimension cameraSize;//上記の左右反転イメージ描画用のJavaFxイメージビュー
	ObjectProperty<Image> imageProperty = new SimpleObjectProperty<Image>();//cameraViewカメラアクセス用

	private boolean stopCamera = false;

	MyWebcaomPane(){
		this.getChildren().addAll(imageViewWebcam);
		initCamera();
	}

	//カメラの初期化
	void initCamera(){
		//カメラ名列挙
		List <Webcam> webcomList = Webcam.getWebcams();
		for (Webcam webcam : webcomList) {
			System.out.println(webcam.getName());
		}
		webCam = webcomList.get(0);
		webCam.open();

		this.cameraSize = webCam.getViewSize();

		double height = ((int)cameraSize.getHeight())<<2;//4倍
		double width = ((int)cameraSize.getWidth())<<2;

		imageViewWebcam.setFitHeight(height);
		imageViewWebcam.setFitWidth(width);

		System.out.printf("サイズカメラ:(%d,%d)\n", cameraSize.width, cameraSize.height);

		startWebCamStream();

		imageViewWebcam.setOnMouseMoved(e->{
			int x = (int) e.getX();
			int y = (int) e.getY();
			int color = imgRev.getRGB(x>>2, y>>2);
			String s = String.format("(%3d,%3d)色#%07X", x,y,color&0x0FFFFFF);
			app.label.setText(s);
		});
	}

	protected void startWebCamStream() {

		stopCamera = false;

		Task<Void> task = new Task<Void>() {

			@Override
			protected Void call() throws Exception {

				final AtomicReference<WritableImage> ref = new AtomicReference<>();

				while (!stopCamera) {//カメラを撮る繰り返し
					try {
						img = webCam.getImage();//撮ったイメージを得る
						if (img != null) {

							imgRev = createMirrorImage(img);//イメージの左右逆転

							ImageIO.write(imgRev, "JPG", new File("testfx.jpg"));//ファイルに残す

							ref.set(SwingFXUtils.toFXImage(imgRev, ref.get()));
							img.flush();

							Platform.runLater(new Runnable() {

								@Override
								public void run() {
									imageProperty.set(ref.get());//イメージビューへセット

									GraphicsContext gc =app.canvas.getGraphicsContext2D();
									gc.drawImage(SwingFXUtils.toFXImage(img,null), 0, 0);
								}
							});
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				return null;
			}
		};

		Thread th = new Thread(task);
		th.setDaemon(true);
		th.start();//スレッド実行(上記callを実行)
		imageViewWebcam.imageProperty().bind(imageProperty);//imageProperty変更をイメージビュー反映する設定
	}

	// 鏡に移ったイメージを生成して返す。
	public static BufferedImage createMirrorImage(BufferedImage img){
		int width = img.getWidth();
		int height = img.getHeight();
		int size = width * height;
		int []buf = new int[ size ];
		img.getRGB(0, 0, width, height, buf, 0, width);//イメージを配列に変換

		//bufのイメージ配列で、左右を変換する。
		int x1, x2, temp;
		for(int y = 0; y < size; y+=width){
			x1 = 0;
			x2 = width -1;
			while(x1 < x2){// 交換の繰り返し
				temp = buf[y+x1];
				buf[y+x1++] = buf[y+x2];
				buf[y+x2--] = temp;
			}
		}
		BufferedImage img2= new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
		img2.setRGB(0, 0, width, height, buf, 0, width);//配列をイメージに書き込む
		return img2;
	}
}

public class CameraFx extends Application {
	Stage primaryStage;
	private BorderPane root = new BorderPane();
	private MyWebcaomPane webCamPane = new MyWebcaomPane();
	Label label = new Label("テスト");
	Canvas canvas=new Canvas(176,144);

	@Override
	public void start(Stage primaryStage) throws Exception {
		this.primaryStage = primaryStage;
		root.setCenter(webCamPane);
		root.setTop(label);
		root.setBottom(canvas);
		webCamPane.app=this;
		label.setStyle("-fx-font-size: 20pt; -fx-color: black;");
		primaryStage.setScene(new Scene(root));
		Dimension size = webCamPane.webCam.getViewSize();
		primaryStage.setWidth( webCamPane.cameraSize.width << 2 );
		primaryStage.setHeight(webCamPane.cameraSize.height<< 2 );
		primaryStage.centerOnScreen();
		primaryStage.show();
	}

	public static void main(String... args) throws Exception {
		Application.launch(args);
	}
}