首页 > 解决方案 > JavaFX 图像渲染占用太多内存,而网络摄像头流导致应用程序崩溃

问题描述

在 JavaFX 线程上从网络摄像头(使用 sarxos-webcam)渲染图像时出现 RAM 问题,导致应用程序崩溃。使用的堆栈:

Java 8 / JavaFX 8
Sarxos webcam capture 0.3.12
Camera driver: OpenIMAJ 0.3.12 (currently the only tested driver which provide like 30 FPS on tablet with IMX175)
Windows 10

当我启动网络摄像头流时,内存需求从 400MB 显着增加到超过 2GB RAM 并启动/停止摄像头几次,但是当摄像头关闭并且我更改 JavaFX 场景时,内存不会下降 - 所以应用程序例如一个小时后崩溃(相机模块只是这个应用程序的一部分)。

这是导致问题的代码:

private void startCameraStream() {
    Platform.runLater(() -> {
        webcam = Webcam.getDefault();
        if (!webcam.isOpen()) {
            webcam.setViewSize(WebcamResolution.HD.getSize());
            webcam.open(false);
        }
        Task<Void> task = getImageTask();
        Thread th = new Thread(task);
        th.start();
        image.imageProperty().bind(imageProperty);
    });
}

private Task<Void> getImageTask() {
    return new Task<Void>() {
        @Override
        protected Void call() {
            while (!pauseWebcam) {
                try {
                    if ((grabbedImage = webcam.getImage()) != null) {
                        Platform.runLater(() -> {
                            Image mainImage = SwingFXUtils.toFXImage(grabbedImage, null);
                            imageProperty.set(mainImage);
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    grabbedImage.flush();
                }
            }
            return null;
        }
    };
}

如您所见,我在 JavaFX 线程内部或外部尝试了诸如“刷新”之类的方法,但没有帮助。内存需求在某个级别停止,但应用程序的其余部分变得不可用。

有什么办法可以防止这种情况发生或在场景改变后清除内存?

我还创建了最小的要点示例来显示工作应用程序的问题: https ://gist.github.com/AreXe/2e5dd350ef4076126cc62156e0cb98fa

单个文件:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com</groupId>
    <artifactId>camera</artifactId>
    <version>1.0</version>
    <dependencies>
        <dependency>
            <groupId>com.github.sarxos</groupId>
            <artifactId>webcam-capture</artifactId>
            <version>0.3.12</version>
        </dependency>
        <dependency>
            <groupId>com.github.sarxos</groupId>
            <artifactId>webcam-capture-driver-openimaj</artifactId>
            <version>0.3.12</version>
        </dependency>
    </dependencies>
</project>

src\main\java\Main.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.io.IOException;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setScene(createStartScene());
        primaryStage.show();
    }

    public Scene createStartScene() throws IOException {
        return new Scene(loadStartPane());
    }

    private Pane loadStartPane() throws IOException {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("camera.fxml"));
        Pane startPane = loader.load();
        loader.getController();
        return startPane;
    }
}

src\main\java\CameraController.java

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;
import com.github.sarxos.webcam.ds.openimaj.OpenImajDriver;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;

import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ResourceBundle;

public class CameraController implements Initializable {

    static {
        Webcam.setDriver(new OpenImajDriver());
    }

    @FXML
    StackPane rootPane;
    @FXML
    ImageView image;
    private Webcam webcam;
    private ObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
    private BufferedImage grabbedImage;
    private static BooleanProperty stopWebcam = new SimpleBooleanProperty(false);

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        stopWebcam.setValue(false);
        image.setFitWidth(rootPane.getWidth() - 20d);
        setCameraStopListener();
    }

    @FXML
    private void cameraStart() {
        stopWebcam.setValue(false);
        startCameraStream();
    }

    @FXML
    private void cameraStop() {
        stopWebcam.setValue(true);
    }

    private void startCameraStream() {
        Platform.runLater(() -> {
            webcam = Webcam.getDefault();
            if (!webcam.isOpen()) {
                webcam.setViewSize(WebcamResolution.HD.getSize());
                webcam.open(false);
            }
            Task<Void> task = getImageTask();
            Thread th = new Thread(task);
            th.start();
            image.imageProperty().bind(imageProperty);
        });
    }

    private Task<Void> getImageTask() {
        return new Task<Void>() {
            @Override
            protected Void call() {
                while (!stopWebcam.get()) {
                    try {
                        if ((grabbedImage = webcam.getImage()) != null) {
                            Platform.runLater(() -> {
                                Image mainImage = SwingFXUtils.toFXImage(grabbedImage, null);
                                imageProperty.set(mainImage);
                            });
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        grabbedImage.flush();
                    }
                }
                return null;
            }
        };
    }

    private void setCameraStopListener() {
        stopWebcam.addListener((obs, oldValue, newValue) -> {
            if (newValue && webcam != null && webcam.isOpen()) {
                webcam.close();
            }
        });
    }
}

src\main\resources\camera.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<StackPane fx:id="rootPane" prefHeight="600.0" prefWidth="840.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="CameraController">
    <children>
        <ImageView fx:id="image" fitHeight="480.0" fitWidth="768.0" pickOnBounds="true" preserveRatio="true" StackPane.alignment="TOP_CENTER" />
      <HBox alignment="BOTTOM_CENTER" prefHeight="100.0" prefWidth="200.0" StackPane.alignment="BOTTOM_CENTER">
         <children>
            <Button onAction="#cameraStart" mnemonicParsing="false" prefHeight="50.0" prefWidth="100.0" text="Start" />
            <Button onAction="#cameraStop" mnemonicParsing="false" prefHeight="50.0" prefWidth="100.0" text="Stop" />
         </children>
      </HBox>
    </children>
</StackPane>

标签: javajavafxmemorywebcam

解决方案


推荐阅读