bytedeco / javacv

Java interface to OpenCV, FFmpeg, and more
Other
7.54k stars 1.58k forks source link

Java's garbage collection can lead to OpenCVFrameConverter There is an issue with ToMat() #2252

Closed zhang22113 closed 3 months ago

zhang22113 commented 3 months ago

import org.bytedeco.javacpp.BytePointer; import org.bytedeco.javacv.CanvasFrame; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.OpenCVFrameConverter; import org.opencv.core.; import org.opencv.imgproc.Imgproc; import javax.swing.; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.List;

public class StartTest8 { static {

    System.load(System.getProperty("user.dir") + "/libs/opencv_java4100.dll");
}

private Mat previousFrame = new Mat();
private Mat currentFrame = new Mat();
private Mat diffFrame = new Mat();
private Mat grayDiff = new Mat();
private Mat binaryDiff = new Mat();
private Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new org.opencv.core.Size(5, 5));
private Mat mat1 = new Mat();
private List<MatOfPoint> contours = new ArrayList<>();
private Point previousPosition = null;

private OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
private Scalar useScalar = new Scalar(0, 255, 0);
private long lastGcCount = 0;

public static void main(String[] args) {
    // 增加JVM堆内存大小
    System.setProperty("java.opts", "-Xmx2g");
    new StartTest6().run();
}

public void run() {
    try {
        PrintStream err = new PrintStream(new FileOutputStream("error.log"));
        System.setErr(err);

        String rtspUrl = "rtsp://192.168.1.22:9554/live?channel=1&subtype=0";
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
        grabber.setOption("rtsp_transport", "tcp");
        grabber.setOption("threads", "1");
        grabber.setOption("nobuffer", "1");
        grabber.setOption("fflags", "nobuffer");
        grabber.setOption("buffer_size", "204800");
        grabber.setOption("rw_timeout", "5000000");
        grabber.setOption("max_delay", "200");
        grabber.setOption("flags", "low_delay");
        grabber.setOption("max_analyze_duration", "500000");
        grabber.setOption("rw_timeout", "15000000");
        grabber.setOption("probesize",  "300000");
        grabber.setOption("analyzeduration", "200000");
        try {
            grabber.start();
            System.out.println("开启拉流器拉流");
        } catch (Exception e) {
            System.out.println("无法打开视频流:" + rtspUrl);
            e.printStackTrace();
            return;
        }

        CanvasFrame canvas = new CanvasFrame("监测物体移动测试", CanvasFrame.getDefaultGamma() / grabber.getGamma());
        canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        canvas.setAlwaysOnTop(true);

        previousFrame = null;

        Runtime runtime = Runtime.getRuntime();
        long startTime = System.currentTimeMillis();
        long maxMemory = runtime.maxMemory();
        System.out.println("最大堆内存: " + maxMemory / 1024 / 1024 + " MB");

        while (canvas.isVisible()) {
            try {
                Frame frame = grabber.grabImage();
                if (frame == null) {
                    System.out.println("缺帧");
                    break;
                }

                if (isGcTriggered()) {
                    System.out.println("GC触发,跳过一帧");
                    previousFrame = new Mat();
                    currentFrame = new Mat();
                    diffFrame = new Mat();
                    grayDiff = new Mat();
                    binaryDiff = new Mat();
                    kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new org.opencv.core.Size(5, 5));
                    mat1 = new Mat();
                    contours = new ArrayList<>();
                    converter = new OpenCVFrameConverter.ToMat();
                    useScalar = new Scalar(0, 255, 0);
                    canvas.showImage(frame);  // 显示原始帧
                    continue;
                }

                Frame result = OpenCV(frame);
                canvas.showImage(result);

                // 记录内存使用情况
                long usedMemory = runtime.totalMemory() - runtime.freeMemory();
                long elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
                System.out.println("已用内存: " + usedMemory / 1024 / 1024 + " MB, 运行时间: " + elapsedTime + "秒");
                if (elapsedTime % 6 == 0) {
                    System.gc();
                }
            } catch (Exception e) {
                System.out.println("处理帧时发生异常:" + e.getMessage());
                e.printStackTrace();
            }
        }

        try {
            grabber.stop();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("停止拉流器时发生异常:" + e.getMessage());
        }
        canvas.dispose();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        System.out.println("日志文件未找到:" + e.getMessage());
    }
}

private Frame OpenCV(Frame frame){

    org.bytedeco.opencv.opencv_core.Mat mat = converter.convertToMat(frame);
    return frame;
}

private boolean isGcTriggered() {
    List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
    long totalGcCount = 0;
    for (GarbageCollectorMXBean gcBean : gcBeans) {
        totalGcCount += gcBean.getCollectionCount();
    }
    boolean gcTriggered = totalGcCount > lastGcCount;
    lastGcCount = totalGcCount;
    return gcTriggered;
}

The original situation was that the used memory would continue to increase, then System. gc() would be automatically triggered, and the process would crash. This is the code I used to simulate the situation. When System. gc() is triggered in the 6th second, the process crashes. The error result is the same

已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 5秒 已用内存: 18 MB, 运行时间: 6秒 GC触发,跳过一帧 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range correctly [swscaler @ 000000006fed10c0] deprecated pixel format used, make sure you did set range c前一帧和当前帧的大小或类型不匹配,或为空 已用内存: 10 MB, 运行时间: 6秒 GC触发,跳过一帧 前一帧和当前帧的大小或类型不匹配,或为空 已用内存: 9 MB, 运行时间: 6秒 GC触发,跳过一帧 前一帧和当前帧的大小或类型不匹配,或为空 已用内存: 7 MB, 运行时间: 6秒 GC触发,跳过一帧 前一帧和当前帧的大小或类型不匹配,或为空

进程已结束,退出代码-1073740940 (0xC0000374)

saudet commented 3 months ago

Please try to use PointerScope: http://bytedeco.org/news/2018/07/17/bytedeco-as-distribution/

saudet commented 3 months ago

Duplicate of #2253