Чтение прогрессивно кодированного 9000x9000 JPEG в Java занимает 1 минуту
При использовании javax.imageio.ImageIO
для загрузки JPEG с большим разрешением (9000x9000) с диска это занимает более 1 минуты в моем приложении scala. Я попытался создать проект только для Java, но он по-прежнему занимает слишком много времени - около 30 секунд.
Вот как я загружаю изображение:
File file = new File("/Users/the21st/slow2.jpg");
BufferedImage image = ImageIO.read(file);
Есть ли способ повысить производительность при чтении прогрессивно закодированных JPEG с большим разрешением в Java?
Изображение, о котором идет речь, этот (модераторы, пожалуйста, не повторно загружайте на другой сайт хостинга, чтобы кодирование/качество не было" t)
Ответы
Ответ 1
Хорошо, здесь мои выводы до сих пор (и, честно говоря, они немного беспокоятся...).
Использование стандартного JPEG-плагина для ImageIO в комплекте с Oracle JRE:
BufferedImage image = ImageIO.read(file);
Считывает изображение примерно на 18 секунд на моем компьютере (MacBookPro/2.8 ГГц i7).
Используя мой JPEG-плагин для ImageIO, который использует несколько иной путь кода (т.е. вы, вероятно, можете получить те же результаты, получив ImageReader
и вызывая метод readRaster()
, затем создавая из него BufferedImage
. Код является нетривиальным, поэтому, пожалуйста, обратитесь к странице проекта, если вам нравится код):
BufferedImage image = ImageIO.read(file);
Считывает изображение примерно на 8 секунд на моем компьютере.
Используя мой класс BufferedImageFactory
и AWT Toolkit
:
BufferedImage image = new BufferedImageFactory(Toolkit.getDefaultToolkit().createImage(file.getAbsolutePath())).getBufferedImage();
Считывает изображение в ~ 2,5 секунды на моем компьютере.
Использование устаревшего класса JPEGImageDecoder
из sun.awt.codec
:
BufferedImage image = new JPEGImageDecoderImpl(new FileInputStream(file)).decodeAsBufferedImage();
Считывает изображение на ~ 1,7 секунды на моем компьютере.
Таким образом, это означает, что мы должны иметь возможность читать это изображение менее чем за 2 секунды даже на Java. В этом случае производительность от JPEGImageReader
просто смешна, и мне очень нравится знать, почему. Как уже упоминалось, это похоже на прогрессивное декодирование, но все же оно должно быть лучше этого.
Update:
Просто для удовольствия, я создал быстрый плагин PoC ImageReader
, поддерживаемый LibJPEG-Turbo Java API. Он еще не очень сложный, но он позволяет использовать код как:
BufferedImage image = ImageIO.read(file);
Чтобы прочитать изображение в < 1,5 секунды на моем компьютере.
PS: Я использовал для обертки ImageIO для JMagick (аналогично коду, указанному @Jordan Doyle, но это позволит вам программировать против API ImageIO), однако я остановился, поскольку это было слишком много работы. Возможно, мне придется пересмотреть... По крайней мере, стоит проверить его решение, если вы не возражаете, полагаясь на установку JNI/native кода.
Ответ 2
Одна быстрая альтернатива ImageIO
- ImageMagick
, существуют различные оболочки для взаимодействия ImageMagick
через Java, такие как JMagick
Чтобы получить BufferedImage
из JMagick, вы должны сначала получить экземпляр MagickImage
, который можно сделать следующим образом:
ImageInfo info = new ImageInfo(pathToImage);
MagickImage image = new MagickImage(info);
Теперь вы можете использовать метод, предоставленный нашим собственным Jacob Nordfalk 8 лет назад, чтобы прочитать изображение в BufferedImage здесь
public static BufferedImage magickImageToBufferedImage(MagickImage magickImage) throws Exception
{
Dimension dim = magickImage.getDimension();
int size = dim.width * dim.height;
byte[] pixels = new byte[size * 3];
magickImage.dispatchImage(0, 0, dim.width, dim.height, "RGB", pixels);
BufferedImage bimage = createInterleavedRGBImage(dim.width, dim.height, pixels);
ColorModel cm = bimage.getColorModel();
Raster raster = bimage.getData();
WritableRaster writableRaster = null;
writableRaster = (raster instanceof WritableRaster) ? (WritableRaster) raster : raster.createCompatibleWritableRaster();
BufferedImage bufferedImage = new BufferedImage(cm, writableRaster, false, null);
return bufferedImage;
}
Затем метод createInterleavedRGBImage
:
public static BufferedImage createInterleavedRGBImage(int imageWidth, int imageHeight, byte data[])
{
int[] numBits = new int[3];
int[] bandoffsets = new int[3];
for (int i = 0; i < 3; i++) {
numBits[i] = 8;
bandoffsets[i] = i;
}
ComponentColorModel ccm = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
numBits,
false,
false, //Alpha pre-multiplied
Transparency.OPAQUE,
DataBuffer.TYPE_BYTE
);
PixelInterleavedSampleModel csm = new PixelInterleavedSampleModel(
DataBuffer.TYPE_BYTE,
imageWidth,
imageHeight,
3, //Pixel stride
imageWidth * 3, // Scanline stride
bandoffsets
);
DataBuffer dataBuf = new DataBufferByte(data, imageWidth * imageHeight * 3);
WritableRaster wr = Raster.createWritableRaster(csm, dataBuf, new Point(0, 0));
return new BufferedImage(ccm, wr, false, null);
}