OpenCV on Raspberry Pi - Using Java(7)- 使用 OpenCV 截取與顯示影片 by Michael | CodeData
top

OpenCV on Raspberry Pi - Using Java(7)- 使用 OpenCV 截取與顯示影片

分享:

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)應用程式兩個按鈕:

OpenCVJava_07_001

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

OpenCVJava_07_002

下列是完整的範例應用程式:

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運作:

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

劉建宏12/22

您好: 如果WebCam有麥克風是否有什麼方式,可以一並將聲音錄下來呢?
謝謝。

關於作者

張益裕。目前的工作是講師與作者,專長是教育訓練課程規劃、教材編製與課程推廣,技術書籍與專欄寫作。涵蓋的領域有OOAD、Java程式設計、JavaFX、Java Embedded、Android與SQL。已出版電子書Google Play圖書Pubu

熱門論壇文章

熱門技術文章