private static void creatFrame() {
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
// ========== 1. 创建主窗口 ==========
JFrame mainFrame = new JFrame("电脑杀毒(自动采集)");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 使用BorderLayout,左侧采集区域,右侧控制面板
mainFrame.setLayout(new BorderLayout());
// 采集画面原始分辨率
int originalWidth = 1920;
int originalHeight = 1080;
double aspectRatio = 16.0 / 9.0;
// 修正:考虑标题栏高度的窗口尺寸计算
int captureDisplayWidth = 800; // 左侧采集区域显示宽度
int captureDisplayHeight = 450; // 左侧采集区域显示高度 (800×450 = 16:9)
int rightPanelWidth = 200; // 右侧面板宽度(根据图片估算)
// 窗口内容区域尺寸
int contentWidth = captureDisplayWidth + rightPanelWidth;
int contentHeight = captureDisplayHeight;
// 设置窗口内容区域尺寸
mainFrame.setSize(contentWidth, contentHeight);
// 修正:获取窗口Insets(边框和标题栏尺寸)
mainFrame.pack();
Insets insets = mainFrame.getInsets();
int totalWindowWidth = contentWidth + insets.left + insets.right;
int totalWindowHeight = contentHeight + insets.top + insets.bottom;
// 重新设置窗口尺寸(包含标题栏和边框)
mainFrame.setSize(totalWindowWidth, totalWindowHeight);
mainFrame.setLocationRelativeTo(null);
// ========== 2. 左侧:采集区域(动态保持16:9比例) ==========
JFXPanel fxPanel = new JFXPanel();
// 创建采集容器 - 关键:使用AspectRatioPanel保持16:9比例
JPanel captureContainer = new JPanel(new BorderLayout()) {
@Override
public Dimension getPreferredSize() {
// 获取父容器可用空间
Container parent = getParent();
if (parent != null) {
int availableWidth = parent.getWidth();
int availableHeight = parent.getHeight();
// 计算保持16:9比例的最大尺寸
int width, height;
// 方案1:优先保证宽度填充,允许上下留白
width = availableWidth;
height = (int) (width / aspectRatio);
// 如果计算出的高度超过可用高度,则采用方案2:优先保证高度填充,允许左右留白
if (height > availableHeight) {
height = availableHeight;
width = (int) (height * aspectRatio);
}
return new Dimension(width, height);
}
return new Dimension(captureDisplayWidth, captureDisplayHeight);
}
};
captureContainer.setBackground(Color.BLACK);
// 使用包装面板实现居中显示(允许留白时居中)
JPanel captureWrapper = new JPanel(new GridBagLayout());
captureWrapper.setBackground(Color.BLACK);
captureWrapper.add(captureContainer);
// 确保JFXPanel填充整个采集容器
fxPanel.setLayout(new BorderLayout());
captureContainer.add(fxPanel, BorderLayout.CENTER);
// 将采集包装器添加到左侧
mainFrame.add(captureWrapper, BorderLayout.CENTER);
// ========== 3. 右侧:控制面板(自适应高度) ==========
JPanel rightPanel = new JPanel();
rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
rightPanel.setBackground(new Color(245, 245, 245));
rightPanel.setBorder(BorderFactory.createEmptyBorder(15, 10, 15, 10));
rightPanel.setPreferredSize(new Dimension(rightPanelWidth, contentHeight));
mainFrame.add(rightPanel, BorderLayout.EAST);
// ========== 4. 控制面板内容(精确匹配图片) ==========
// Administrator标签
JLabel adminLabel = new JLabel("Administrator", JLabel.CENTER);
adminLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
adminLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
adminLabel.setMaximumSize(new Dimension(Short.MAX_VALUE, 25));
rightPanel.add(adminLabel);
rightPanel.add(Box.createRigidArea(new Dimension(0, 15)));
// QQ= 标签和输入框
JPanel qqPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
qqPanel.setBackground(new Color(245, 245, 245));
qqPanel.setMaximumSize(new Dimension(Short.MAX_VALUE, 30));
JLabel qqLabel = new JLabel("QQ=");
qqLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
JTextField qqField = new JTextField(10);
qqField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
qqField.setHorizontalAlignment(JTextField.LEFT);
qqPanel.add(qqLabel);
qqPanel.add(qqField);
rightPanel.add(qqPanel);
rightPanel.add(Box.createRigidArea(new Dimension(0, 20)));
// 当前角色
JPanel currentRolePanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
currentRolePanel.setBackground(new Color(245, 245, 245));
currentRolePanel.setMaximumSize(new Dimension(Short.MAX_VALUE, 35));
JLabel currentRoleLabel = new JLabel("当前角色:");
currentRoleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
Constant.text_jusse_now_num = new JTextField("1");
Constant.text_jusse_now_num.setFont(new Font("微软雅黑", Font.BOLD, 14));
Constant.text_jusse_now_num.setHorizontalAlignment(JTextField.CENTER);
Constant.text_jusse_now_num.setPreferredSize(new Dimension(50, 25));
currentRolePanel.add(currentRoleLabel);
currentRolePanel.add(Constant.text_jusse_now_num);
rightPanel.add(currentRolePanel);
rightPanel.add(Box.createRigidArea(new Dimension(0, 10)));
// 所有角色
JPanel allRolePanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
allRolePanel.setBackground(new Color(245, 245, 245));
allRolePanel.setMaximumSize(new Dimension(Short.MAX_VALUE, 35));
JLabel allRoleLabel = new JLabel("所有角色:");
allRoleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
JTextField allRoleValue = new JTextField("27");
allRoleValue.setFont(new Font("微软雅黑", Font.BOLD, 14));
allRoleValue.setHorizontalAlignment(JTextField.CENTER);
allRoleValue.setPreferredSize(new Dimension(50, 25));
allRoleValue.setEditable(false);
allRoleValue.setBackground(new Color(245, 245, 245));
allRolePanel.add(allRoleLabel);
allRolePanel.add(allRoleValue);
rightPanel.add(allRolePanel);
rightPanel.add(Box.createRigidArea(new Dimension(0, 25)));
// 测试功能按钮
JButton testButton = new JButton("测试功能");
testButton.setFont(new Font("微软雅黑", Font.BOLD, 16));
testButton.setBackground(new Color(70, 130, 180));
testButton.setForeground(Color.WHITE);
testButton.setAlignmentX(Component.CENTER_ALIGNMENT);
testButton.setMaximumSize(new Dimension(Short.MAX_VALUE, 40));
testButton.setFocusPainted(false);
rightPanel.add(testButton);
rightPanel.add(Box.createVerticalGlue());
// ========== 5. 按钮事件处理 ==========
testButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// MS2130FXCapture.startCapture(fxPanel);
}
});
// ========== 6. 窗口设置 ==========
// 窗口图标
try {
Image icon = Toolkit.getDefaultToolkit()
.getImage(new File(Constant.desktopPathImages + "icon.ico").toURI().toURL());
mainFrame.setIconImage(icon);
} catch (Exception e) {
e.printStackTrace();
}
// ========== 7. 关键:窗口缩放时动态调整比例 ==========
mainFrame.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
// 获取窗口内容区域尺寸(不含标题栏和边框)
int contentWidth = mainFrame.getContentPane().getWidth();
int contentHeight = mainFrame.getContentPane().getHeight();
// 动态计算右侧面板宽度(固定或按比例)
int newRightPanelWidth = rightPanelWidth; // 可以改为按比例计算
// 计算采集区域可用宽度
int availableCaptureWidth = contentWidth - newRightPanelWidth;
int availableCaptureHeight = contentHeight;
// 计算保持16:9比例的采集区域尺寸
int captureWidth, captureHeight;
// 优先尝试宽度填充(可能产生上下留白)
captureWidth = availableCaptureWidth;
captureHeight = (int) (captureWidth / aspectRatio);
// 如果高度超出,改为高度填充(可能产生左右留白)
if (captureHeight > availableCaptureHeight) {
captureHeight = availableCaptureHeight;
captureWidth = (int) (captureHeight * aspectRatio);
}
// 设置采集容器尺寸
captureContainer.setPreferredSize(new Dimension(captureWidth, captureHeight));
captureContainer.revalidate();
captureContainer.repaint();
// 调整右侧面板尺寸
rightPanel.setPreferredSize(new Dimension(newRightPanelWidth, contentHeight));
rightPanel.revalidate();
}
});
// 显示窗口
mainFrame.setVisible(true);
// 自动开始采集
new Thread(() -> {
MS2130FXCapture.startCapture(fxPanel);
}).start();
// SwingUtilities.invokeLater(() -> {
// MS2130FXCapture.startCapture(fxPanel);
// });
}
这是我的creatFrame()方法在dnf类的main方法中执行
package com.dnf.main;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.SwingUtilities;
import org.opencv.core.Mat;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.VideoWriter;
import org.opencv.videoio.Videoio;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
public class MS2130FXCapture {
private static ImageView imageView;
private static VideoCapture videoCapture;
private static volatile boolean isRunning = false;
private static volatile boolean isFXInitialized = false;
private static final Lock initLock = new ReentrantLock();
private static Thread captureThread;
// 复用对象(兼容Java 8,移除PixelBuffer)
private static Mat frameMat = new Mat();
private static BufferedImage bufImg; // 复用缓冲图像
private static WritableImage fxImg; // 复用JavaFX图像
private static byte[] pixelBuffer; // 复用像素数组
/**
* 启动采集
*/
public static void startCapture(JFXPanel fxPanel) {
System.out.println("[自动采集] 开始初始化采集");
if (isRunning) {
stopCapture();
}
int deviceIndex = findValidDeviceIndex();
if (deviceIndex == -1) {
System.err.println("[错误] 未找到MS2130设备");
return;
}
if (!isFXInitialized) {
initLock.lock();
try {
if (!isFXInitialized) {
System.out.println("[FX初始化] 首次启动JavaFX平台");
SwingUtilities.invokeLater(() -> {
Platform.runLater(() -> {
try {
Platform.startup(() -> {
isFXInitialized = true;
initFXCapture(fxPanel, deviceIndex);
});
} catch (IllegalStateException e) {
System.out.println("[FX初始化] 平台已存在,直接绑定");
isFXInitialized = true;
initFXCapture(fxPanel, deviceIndex);
}
});
});
}
} finally {
initLock.unlock();
}
} else {
Platform.runLater(() -> initFXCapture(fxPanel, deviceIndex));
}
}
/**
* 初始化UI组件(兼容Java 8)
*/
private static void initFXCapture(JFXPanel fxPanel, int deviceIndex) {
imageView = new ImageView();
imageView.setPreserveRatio(true);
imageView.setSmooth(false); // 降低CPU开销
imageView.setCache(false); // 避免内存缓存累积
StackPane root = new StackPane(imageView);
root.setBackground(
new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, javafx.geometry.Insets.EMPTY)));
imageView.fitWidthProperty().bind(root.widthProperty());
imageView.fitHeightProperty().bind(root.heightProperty());
fxPanel.setScene(new Scene(root));
openDeviceAndStartCapture(deviceIndex);
}
/**
* 设备枚举(快速释放资源)
*/
private static int findValidDeviceIndex() {
for (int i = 0; i < 4; i++) {
Mat testFrame = new Mat();
try (AutoCloseableVideoCapture testCapture = new AutoCloseableVideoCapture(i)) {
VideoCapture capture = testCapture.getVideoCapture();
if (capture.isOpened()) {
if (capture.read(testFrame) && !testFrame.empty()) {
return i;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
testFrame.release();
}
}
return -1;
}
/**
* 打开设备并配置参数(兼容低版本OpenCV)
*/
private static void openDeviceAndStartCapture(int deviceIndex) {
if (videoCapture != null) {
if (videoCapture.isOpened())
videoCapture.release();
videoCapture = null;
}
videoCapture = new VideoCapture(deviceIndex + Videoio.CAP_DSHOW);
if (!videoCapture.isOpened()) {
System.err.println("[错误] 无法打开设备");
return;
}
// 降低分辨率减少内存占用
videoCapture.set(Videoio.CAP_PROP_FRAME_WIDTH, 1980);
videoCapture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 1080);
// 兼容低版本OpenCV的编码格式设置
videoCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('M', 'J', 'P', 'G'));
resetReusableObjects();
isRunning = true;
captureThread = new Thread(() -> captureAndDisplayLoop());
captureThread.setDaemon(true);
captureThread.start();
// videoCapture.release();
}
/**
* 重置复用对象
*/
private static void resetReusableObjects() {
if (frameMat != null) {
frameMat.release();
frameMat = new Mat();
}
bufImg = null;
fxImg = null;
pixelBuffer = null;
}
/**
* 采集循环(强化资源释放)
*/
private static void captureAndDisplayLoop() {
long lastFrameTime = 0;
final long frameInterval = 33; // 30fps
while (isRunning) {
if (Thread.currentThread().isInterrupted())
break;
// 优先抓取最新帧
if (!videoCapture.grab()) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
continue;
}
// 释放上一帧内存
if (!frameMat.empty()) {
frameMat.release();
}
// 读取新帧
if (!videoCapture.retrieve(frameMat) || frameMat.empty()) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
continue;
}
// 控制帧率
long currentTime = System.currentTimeMillis();
if (currentTime - lastFrameTime < frameInterval)
continue;
lastFrameTime = currentTime;
// 处理帧(兼容Java 8的图像转换)
processFrame(frameMat);
}
cleanupResources();
System.out.println("[采集线程] 循环结束");
}
/**
* 处理帧(移除PixelBuffer,用传统方式转换)
*/
private static void processFrame(Mat mat) {
int width = mat.cols();
int height = mat.rows();
int channels = mat.channels();
int bufferSize = width * height * channels;
// 复用像素数组
if (pixelBuffer == null || pixelBuffer.length != bufferSize) {
pixelBuffer = new byte[bufferSize];
}
mat.get(0, 0, pixelBuffer);
// 复用BufferedImage(兼容颜色格式)
if (bufImg == null || bufImg.getWidth() != width || bufImg.getHeight() != height) {
// 用TYPE_3BYTE_BGR兼容OpenCV的BGR格式
bufImg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
}
// 更新缓冲图像像素
byte[] imgPixels = ((DataBufferByte) bufImg.getRaster().getDataBuffer()).getData();
System.arraycopy(pixelBuffer, 0, imgPixels, 0, bufferSize);
// 复用JavaFX图像
if (fxImg == null || fxImg.getWidth() != width || fxImg.getHeight() != height) {
fxImg = new WritableImage(width, height);
}
// 转换为JavaFX图像(兼容Java 8)
SwingFXUtils.toFXImage(bufImg, fxImg);
// 更新UI
Platform.runLater(() -> {
if (imageView != null && isRunning) {
imageView.setImage(fxImg);
}
});
}
/**
* 清理资源
*/
private static void cleanupResources() {
// 释放OpenCV资源
if (videoCapture != null) {
if (videoCapture.isOpened())
videoCapture.release();
videoCapture = null;
}
if (frameMat != null) {
frameMat.release();
frameMat = null;
}
// 释放Java对象
Platform.runLater(() -> {
if (imageView != null)
imageView.setImage(null);
});
bufImg = null;
fxImg = null;
pixelBuffer = null;
}
/**
* 停止采集
*/
public static void stopCapture() {
isRunning = false;
if (captureThread != null && captureThread.isAlive()) {
captureThread.interrupt();
try {
captureThread.join(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
captureThread = null;
}
cleanupResources();
System.out.println("[停止采集] 资源已释放");
}
}
这是MS2130FXCapture.java类
现在有个问题运行程序后,内存占用很快到1200MB一直维持在这里了,怎么优化减少内存的使用,VideoCapture这个对象好像很占用内存怎么释放不需要的之前的帧
最新发布