Java ImageIO безумно медленный

Я делаю программу, которая отправляет экран клиента на сервер и отображает его, но она очень медленная. На один кадр уходит 2-3 секунды, а скорость загрузки/выгрузки не проблема. Есть ли что-то, что я делаю неправильно / что-то, что я могу изменить, чтобы ускорить это?

Сервер:

import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.zip.GZIPInputStream;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class Server {

    public static void main(String[] args) {
        try {
            JPanel panel = new JPanel();
            panel.setLayout(new BorderLayout());

            JLabel label = new JLabel();
            label.setSize(800, 600);
            label.setVisible(true);

            JScrollPane scroll = new JScrollPane();
            scroll.getViewport().add(label);
            panel.add(scroll, BorderLayout.CENTER);

            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(panel);
            frame.setSize(800, 600);
            frame.setVisible(true);

            ServerSocket serverSocket = new ServerSocket(25565);
            Socket socket = serverSocket.accept();
            GZIPInputStream in = new GZIPInputStream(socket.getInputStream());
            BufferedImage image = null;
            while(socket.isConnected()) {
                image = ImageIO.read(in);
                if(image != null) {
                    label.setIcon(new ImageIcon(image));
                    label.repaint();

                }
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Клиент:

import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.io.IOException;
import java.net.Socket;
import java.util.zip.GZIPOutputStream;

import javax.imageio.ImageIO;

public class Client {

    public static void main(String[] args) {
        try {
            Robot robot = new Robot();
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Rectangle screen = new Rectangle((int) toolkit.getScreenSize().getWidth(), (int) toolkit.getScreenSize().getHeight());
            Socket socket = new Socket("127.0.0.1", 25565);
            GZIPOutputStream out = new GZIPOutputStream(socket.getOutputStream());

            while(socket.isConnected()) {
                ImageIO.write(robot.createScreenCapture(screen), "png", out);
            }
            out.close();
        } catch (AWTException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

person user1007883    schedule 10.08.2012    source источник
comment
Что вы уже пытались сделать, чтобы выявить узкое место в производительности? Вы занимались профилированием?   -  person cyroxx    schedule 11.08.2012
comment
Посмотрите здесь: stackoverflow.com/ вопросов/2293556/ или здесь: stackoverflow.com/questions/7726583/   -  person paulsm4    schedule 11.08.2012
comment
Я почти уверен, что это ImageIO, потому что я помню, когда я проверял, какой формат изображения будет лучшим, некоторым из них требовалось до 7 секунд только для записи на диск.   -  person user1007883    schedule 11.08.2012
comment
@ paulsm4 Я бы предпочел не использовать никакие библиотеки, но буду, если не найду другого варианта. Любые другие идеи?   -  person user1007883    schedule 11.08.2012
comment
Вы должны начать с запуска программы под профилировщиком, чтобы увидеть, куда уходит время. Глупо гадать, когда знать так легко. Мне очень нравится интерфейс профилировщика Netbeans.   -  person Gene    schedule 11.08.2012
comment
Это ImageIO, я просто не знаю, как это исправить без использования библиотеки.   -  person user1007883    schedule 11.08.2012


Ответы (3)


Если вы отправляете изображение как png, GZIPStream, вероятно, является плохой идеей. Вы не добьетесь значительного увеличения сжатия, поскольку png уже имеет хорошее сжатие. В зависимости от типа изображения вы также можете использовать jpeg для лучшего сжатия.

Но, вероятно, наиболее важным является обертывание выходного потока с помощью BufferedOuputStream:

BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());

Вы можете сделать то же самое во входном потоке.

person pmoleri    schedule 11.08.2012
comment
PNG на самом деле использует сжатие zip, а использование GZIPInput/OutputStream просто замедлит работу (и, скорее всего, добавит накладные расходы на данные вместо сжатия). - person Harald K; 03.06.2013

Ваш вопрос противоречит сам себе.

С одной стороны, вы спрашиваете об отправке изображений через сокет. На другом вы говорите: «Один кадр занимает 2-3 секунды, и скорость загрузки/выгрузки не проблема».

Я предлагаю вам попытаться профилировать ваше приложение, чтобы определить, находится ли узкое место в захвате изображения, преобразовании изображения, сжатии изображения или вводе-выводе сокета. Любое из этих объяснений является правдоподобным объяснением, и догадки не являются хорошей стратегией для определения реального объяснения.

(И если вы уже знаете, где находится узкое место... не путайте проблему, включив в свой вопрос остальные шаги. Например, если вы уже знаете, что проблема в преобразования изображений, нет необходимости даже упоминать, что вы отправляете изображения по сети. Но если вы не знаете этого, не сообщайте нам потенциально ложную информацию... например, скорость загрузки/скачивания не проблема".)

person Stephen C    schedule 11.08.2012

Я считаю, что все данные ответы не решают проблему. Подозреваю, что проблема не в сетевом подключении, а в методе ImageIO.read(InputStream). Эта версия метода просто безумно медленная. 2-3 секунды также возникнут, если вы попытаетесь загрузить его с локального диска в виде потока (тогда как ImageIO.read(File) работает нормально).

Я решил проблему с:

ImageIO.setUseCache(false);

Подробнее здесь: Медленное преобразование массива байтов в буферизованное изображение

person Wolf    schedule 02.11.2016