java - 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>
解决方案
推荐阅读
- elixir - 如何在不保存本地的情况下将数据上传到谷歌存储
- javascript - thymeleaf 中的 JavaScript 脚本在页面加载时不运行?
- javascript - 如何使用 StreamSaver.js 从 Axios 消费下载流?
- javascript - Vuelidate没有显示正确的错误
- teamcity - 在 Razor 页面中禁用 ReSharper HTML 代码检查的正确方法是什么?
- python - 计算与 pandas 的滚动互相关(不是 Pearson)
- swift - 错误:superview:设置 tableView.delegate 和 tableView.datasource 等于 self 时发送到实例的无法识别的选择器
- java - 客户端在 Java 中的套接字应用程序中无法正常工作
- android - MVI 不是用户意图
- flutter - Flutter:如何覆盖函数