OpenCV on Raspberry Pi – Using Java(6)- 使用 OpenCV 拍攝照片(Camera Module) << 前情
準備工作
- 個人電腦
- 已經安裝好OpenCV
- 已經安裝與設定好NetBeans本地與遠端開發環境
- RASPBERRY PI 3 MODEL B
- 已經安裝好OpenCV
- 可以在個人電腦透過SSH連線到Raspberry Pi
- 已經安裝與設定好WebCam或Camera Module
使用 VideoCapture 截取影片
在應用程式經由攝影設備截取影片,會是一件很複雜的工作,需要處理很多與軟、硬體相關的細節,不過OpenCV把截取影片包裝為簡單而且功能強大的VideoCapture,這個類別可以從攝影設備截取影片,也可以播放影片檔案。
你可以把影片當成是一連串快速顯示的照片,應用程式使用一定的速率讀取影像為Mat物件,再依照需要執行後續的處理。例如在JavaFX應用程式的畫面顯示截取的影片,只要把Mat物件轉換為JavaFX的Image物件後,再設定給JavaFX ImageView元件顯示。另外在執行即時影像處理與辨識的應用,也會採用截取影片的作法。
在建立VideoCapture物件的時候,可以在建構式指定攝影設備的編號。建立VideoCapture物件後使用下列的方法開啟設備與設定解析度:
VideoCapture capture = new VideoCapture();
capture.open(0);
capture.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
capture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
VideoCapture類別提供的「isOpened()」方法,可以判斷指定攝影設備是否開啟成功,再使用下列的方法讀取與處理影像:
if (capture.isOpened()) {
Mat image = new Mat();
capture.read(image);
if (!image.empty()) {
// 處理影像
}
}
使用 JavaFX 顯示截取的影片
Java在桌面應用程式的部份,目前適合使用比較新、而且功能強大的JavaFX。搭配OpenCV VideoCapture的應用程式,使用一個簡單的視窗顯示截取的影片,提供開始/停止(Start/Stop)與結束結束(Exit)應用程式兩個按鈕:

按下開始按鈕以後,VideoCapture開始截取影片,經過轉換後顯示在視窗:

下列是完整的範例應用程式:
package net.macdidi5.opencv;
import java.io.ByteArrayInputStream;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
public class VideoDemo01 extends Application {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
// 是否開始截取畫面
private boolean isStart = false;
// 截取畫面物件
private VideoCapture capture;
// 截取畫面控制物件
private ScheduledExecutorService timer;
// JavaFX 元件
private BorderPane root;
private VBox vboxCenter;
private ImageView frame;
private HBox hboxBottom;
private Button videoButton, exitButton;
@Override
public void start(Stage primaryStage) {
// 建立 JavaFX 畫面
initGui();
// 建立截取畫面物件
capture = new VideoCapture();
// 結束按鈕事件
exitButton.setOnAction((ActionEvent event) -> {
System.exit(0);
});
videoButton.setOnAction((ActionEvent event) -> {
// 啟動
if (!isStart) {
// 設定顯示截取畫面元件的寬度
frame.setFitWidth(640);
frame.setFitHeight(480);
frame.setPreserveRatio(true);
// 開啟設備與設定解析度
capture.open(0);
capture.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
capture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
if (capture.isOpened()) {
isStart = true;
// 截取畫面執行緒
Runnable frameGrabber = new Runnable() {
@Override
public void run() {
// 讀取與設定畫面
Image imageToShow = grabFrame();
frame.setImage(imageToShow);
}
};
// 建立與啟動截取畫面執行緒
timer = Executors.newSingleThreadScheduledExecutor();
// 設定 33ms 為截取畫面間隔時間, 一秒 30 frames
timer.scheduleAtFixedRate(frameGrabber,
0, 33, TimeUnit.MILLISECONDS);
videoButton.setText("Stop");
}
else {
System.err.println("Open camera error!");
}
}
// 停止
else {
isStart = false;
videoButton.setText("Start");
try {
timer.shutdown();
timer.awaitTermination(33, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
System.err.println(e);
}
// 清除
capture.release();
frame.setImage(null);
}
});
Scene scene = new Scene(root, 800, 640);
primaryStage.setTitle("Pixel Demo 01");
primaryStage.setScene(scene);
primaryStage.show();
}
// 讀取畫面並傳回 Image 物件
private Image grabFrame() {
Image result = null;
Mat image = new Mat();
if (capture.isOpened()) {
capture.read(image);
if (!image.empty()) {
result = mat2Image(".png", image);
}
}
return result;
}
// 轉換 Mat 為 Image 物件
// String ext 格式, 例如 .png
// Mat image 影像物件
public static Image mat2Image(String ext, Mat image) {
MatOfByte buffer = new MatOfByte();
Imgcodecs.imencode(ext, image, buffer);
return new Image(new ByteArrayInputStream(buffer.toArray()));
}
// 建立 JavaFX 畫面
private void initGui() {
root = new BorderPane();
vboxCenter = new VBox();
vboxCenter.setAlignment(Pos.CENTER);
vboxCenter.setPadding(new Insets(5, 5, 5, 5));
frame = new ImageView();
vboxCenter.getChildren().addAll(frame);
root.setCenter(vboxCenter);
hboxBottom = new HBox();
hboxBottom.setAlignment(Pos.CENTER);
hboxBottom.setPadding(new Insets(5, 5, 5, 5));
videoButton = new Button("Start");
exitButton = new Button("Exit");
hboxBottom.getChildren().addAll(videoButton, exitButton);
root.setBottom(hboxBottom);
}
public static void main(String[] args) {
launch(args);
}
}
參考下列系列文章的說明,執行正確的設定後,讓這個應用程式可以在Windows、Mac OS與Raspberry Pi運作:
|
劉建宏
12/22
您好: 如果WebCam有麥克風是否有什麼方式,可以一並將聲音錄下來呢?
謝謝。