简体   繁体   English

Java渲染网络摄像头的图像占用太多CPU

[英]Java render webcam's image take too much CPU

I'm writing an application which connect and stream webcam video. 我正在编写一个连接和流式传输网络摄像头视频的应用程序。 To do that, I use Sarxos webcam library (link here ) to get default webcam, then use WebcamPanel to draw image. 为此,我使用Sarxos网络摄像头库( 此处链接)获取默认网络摄像头,然后使用WebcamPanel绘制图像。 The problem arose when I delivered the app to my customer, they tested it on an old machine and complained the app took too much CPU. 当我将应用交付给客户时,问题就出现了,他们在旧计算机上对其进行了测试,并抱怨该应用占用了过多的CPU。

I never noticed that before, and when I tested it again, to my surprise, the app took around 33% CPU, which is too much for a simple app which only connect webcam and draw image with 30 FPS. 我之前从未注意到,当我再次对其进行测试时,令我惊讶的是,该应用程序占用了大约33%的CPU,对于仅连接摄像头并以30 FPS绘制图像的简单应用程序来说,这实在太多了。 Here is my programming environment: Windows 7 64bit, CoreI5-4460 CPU (3.2-3.4Ghz), Zotac Geforce GTX 650 Ti, Java 7u45. 这是我的编程环境:Windows 7 64bit,CoreI5-4460 CPU(3.2-3.4Ghz),Zotac Geforce GTX 650 Ti,Java 7u45。

I have tested to point out which part took the most CPU, and it is the rendering. 我测试指出哪个部分占用了最多的CPU,这就是渲染。 If I fetch webcam images only but not draw them, the CPU takes 6-7%, but when I render them, the CPU jumps to 30-33%. 如果仅获取而不是绘制网络摄像头图像,则CPU占用6-7%,但是渲染时,CPU跳至30-33%。 I took a look in to WebcamPanel class to see maybe something is wrong with them, but so far I found nothing. 我查看了WebcamPanel类,以查看它们是否有问题,但到目前为止我什么都没找到。 The draw method is as below: 绘制方法如下:

    @Override
    public void paintImage(WebcamPanel owner, BufferedImage image, Graphics2D g2) {

        assert owner != null;
        assert image != null;
        assert g2 != null;

        int pw = getWidth();
        int ph = getHeight();
        int iw = image.getWidth();
        int ih = image.getHeight();

        Object antialiasing = g2.getRenderingHint(KEY_ANTIALIASING);
        Object rendering = g2.getRenderingHint(KEY_RENDERING);

        g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_OFF);
        g2.setRenderingHint(KEY_RENDERING, VALUE_RENDER_SPEED);
        g2.setBackground(Color.BLACK);
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, pw, ph);

        // resized image position and size
        int x = 0;
        int y = 0;
        int w = 0;
        int h = 0;

        switch (drawMode) {
            case NONE:
                w = image.getWidth();
                h = image.getHeight();
                break;
            case FILL:
                w = pw;
                h = ph;
                break;
            case FIT:
                double s = Math.max((double) iw / pw, (double) ih / ph);
                double niw = iw / s;
                double nih = ih / s;
                double dx = (pw - niw) / 2;
                double dy = (ph - nih) / 2;
                w = (int) niw;
                h = (int) nih;
                x = (int) dx;
                y = (int) dy;
                break;
        }

        if (resizedImage != null) {
            resizedImage.flush();
        }

        if (w == image.getWidth() && h == image.getHeight() && !mirrored) {
            resizedImage = image;
        } else {

            GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsConfiguration gc = genv.getDefaultScreenDevice().getDefaultConfiguration();

            Graphics2D gr = null;
            try {

                resizedImage = gc.createCompatibleImage(pw, ph);
                gr = resizedImage.createGraphics();
                gr.setComposite(AlphaComposite.Src);

                for (Map.Entry<RenderingHints.Key, Object> hint : imageRenderingHints.entrySet()) {
                    gr.setRenderingHint(hint.getKey(), hint.getValue());
                }

                gr.setBackground(Color.BLACK);
                gr.setColor(Color.BLACK);
                gr.fillRect(0, 0, pw, ph);

                int sx1, sx2, sy1, sy2; // source rectangle coordinates
                int dx1, dx2, dy1, dy2; // destination rectangle coordinates

                dx1 = x;
                dy1 = y;
                dx2 = x + w;
                dy2 = y + h;

                if (mirrored) {
                    sx1 = iw;
                    sy1 = 0;
                    sx2 = 0;
                    sy2 = ih;
                } else {
                    sx1 = 0;
                    sy1 = 0;
                    sx2 = iw;
                    sy2 = ih;
                }

                gr.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);

            } finally {
                if (gr != null) {
                    gr.dispose();
                }
            }
        }

        g2.drawImage(resizedImage, 0, 0, null);

        if (isFPSDisplayed()) {

            String str = String.format("FPS: %.1f", webcam.getFPS());

            int sx = 5;
            int sy = ph - 5;

            g2.setFont(getFont());
            g2.setColor(Color.BLACK);
            g2.drawString(str, sx + 1, sy + 1);
            g2.setColor(Color.WHITE);
            g2.drawString(str, sx, sy);
        }

        if (isImageSizeDisplayed()) {

            String res = String.format("%d\u2A2F%d px", iw, ih);

            FontMetrics metrics = g2.getFontMetrics(getFont());
            int sw = metrics.stringWidth(res);
            int sx = pw - sw - 5;
            int sy = ph - 5;

            g2.setFont(getFont());
            g2.setColor(Color.BLACK);
            g2.drawString(res, sx + 1, sy + 1);
            g2.setColor(Color.WHITE);
            g2.drawString(res, sx, sy);
        }

        if (isDisplayDebugInfo()) {

            if (lastRepaintTime < 0) {
                lastRepaintTime = System.currentTimeMillis();
            } else {

                long now = System.currentTimeMillis();
                String res = String.format("DEBUG: repaints per second: %.1f", (double) 1000 / (now - lastRepaintTime));
                lastRepaintTime = now;
                g2.setFont(getFont());
                g2.setColor(Color.BLACK);
                g2.drawString(res, 6, 16);
                g2.setColor(Color.WHITE);
                g2.drawString(res, 5, 15);
            }
        }

        g2.setRenderingHint(KEY_ANTIALIASING, antialiasing);
        g2.setRenderingHint(KEY_RENDERING, rendering);
    }

I have tried quite a lot of things to optimal my rendering, but there's nothing work. 我已经尝试了很多方法来优化我的渲染,但是没有任何效果。 The things I tried: 我尝试过的事情:

  1. Create compatiable buffered image to render => already done in the code as you can see. 如您所见,创建兼容的缓冲图像以渲染=>已经在代码中完成。
  2. Use DoubleBuffer strategy => as I read, this technique is already done in paintComponent() method. 如我所读,使用DoubleBuffer strategy =>,此技术已在paintComponent()方法中完成。 I also tried to implement it using the code in the answer here , but also no result. 我也尝试使用此处答案中的代码来实现它,但也没有结果。
  3. Turn on, off OpenGL, force DirectDraw using VM parameters. 打开,关闭OpenGL,使用VM参数强制DirectDraw。 No results. 没结果。

I'm considering to change to an OpenGL library to render the image, but it will be the last option, because I've no knowledge about OpenGL, and I think Java2D is more than enough for my application. 我正在考虑更改为OpenGL库以渲染图像,但这将是最后的选择,因为我不了解OpenGL,而且我认为Java2D对我的应用程序绰绰有余。 Can anyone help me solve this problem? 谁能帮我解决这个问题?

After test with various things, include render webcam's images using OpenGL (took me too much time since I have to learned OpenGL from scratch), I have found the solution. 经过各种测试,包括使用OpenGL渲染网络摄像头的图像(花了我太多时间,因为我不得不从头开始学习OpenGL),我找到了解决方案。 These are the things I did: 这些是我所做的事情:

  1. Using OpenImja driver instead of default driver. 使用OpenImja驱动程序而不是默认驱动程序。 I built OpenImja using Maven, after that I took 4 jar files to put to library path: core-1.1.jar ; 我使用Maven构建了OpenImja,之后我将4个jar文件放入了库路径: core-1.1.jar ; core-image-1.1.jar ; core-image-1.1.jar ; core-video-1.1.jar ; core-video-1.1.jar ; core-video-capture-1.1.jar . core-video-capture-1.1.jar
  2. Scale is no go. 规模是行不通的。 In the previous version, I used the best resolution of webcam and scaled it to fit screen size. 在以前的版本中,我使用了网络摄像头的最佳分辨率,并对其进行缩放以适合屏幕尺寸。 Big mistake. 大错。
  3. Force the app to render using DirectDraw with -Dsun.java2d.noddraw=false . 使用DirectDraw和-Dsun.java2d.noddraw = false强制应用呈现。 According to Oracle, Swing use a mix of DirectDraw and GDI pipeline to render. 根据Oracle的说法,Swing混合使用DirectDraw和GDI管道进行渲染。 But since I wrote the app for Window, it is better to only use DirectDraw. 但是由于我是为Window编写应用程序的,所以最好只使用DirectDraw。 For other platforms like Linux, maybe X11 or OpenGL will be better. 对于Linux等其他平台,也许X11或OpenGL会更好。

After that, my CPU percentage for the app dropped from 33% to 11%. 之后,我的应用程序CPU百分比从33%下降到11%。 Hope this will have someone who runs to the same problem with me. 希望这会有一个遇到我同样问题的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM