Визуализация изображений JavaFX занимает слишком много памяти во время потока веб-камеры, что приводит к сбою приложения - PullRequest
0 голосов
/ 26 апреля 2020

У меня проблема с ОЗУ при рендеринге изображения с веб-камеры (с помощью sarxos-webcam) в потоке JavaFX, что приводит к сбою приложения. Используемый стек:

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

Когда я запускаю потоковую передачу с веб-камеры, потребность в памяти значительно увеличивается, например, с 400 МБ до более 2 ГБ ОЗУ и запуска / остановки камеры несколько раз, но когда камера затем выключается, и я меняю Сцена 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;
        }
    };
}

Как вы можете видеть, я пробовал методы типа "flu sh" внутри или вне потока 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.f xml

<?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>
...