3

我有一个程序,我必须将 BufferedImage 序列化到磁盘,然后将其读回。目前我使用的是原始图像格式

ImageIO.getImageReadersByFormatName("raw");

经过大量测试后,似乎它在相同数据上的性能差异很大(从不到一秒到 15 秒)。
1. 知道这是怎么回事吗?
2. 在 Java 中完成这项任务最快的图像读取器/写入器是什么?

谢谢

4

2 回答 2

2
  1. 很难说是什么导致了如此巨大的性能差异。有各种各样的因素可能促成它。请参阅:如何在 Java 中编写正确的微基准测试?
  2. 这也很难回答,因为它可能取决于图像中的数据、处理器的速度以及硬盘驱动器的速度。真正了解您的数据集的唯一方法是对您的不同选项进行基准测试。

也就是说,我想这png通常是一个不错的尝试选择。

于 2011-04-22T00:01:59.067 回答
1

除非您需要特定程序之外的互操作性,否则通过 ImageIO 进行序列化可能是一个缓慢的解决方案。直接读取和写入 BufferedImage 的像素可能会更快。这是一个完整的解决方案:

public class SerializedImage
    implements Serializable {

    private BufferedImage   mImage;

    public SerializedImage( BufferedImage image ) {

        mImage = image;
    }

    public BufferedImage getImage() {

        return mImage;
    }

    private void writeObject( ObjectOutputStream streamOut )
        throws IOException {

        streamOut.writeInt( mImage.getWidth() );
        streamOut.writeInt( mImage.getHeight() );
        streamOut.writeInt( mImage.getType() );
        streamOut.writeObject( ( (DataBufferInt) mImage.getRaster().getDataBuffer() ).getData() );
    }

    private void readObject( ObjectInputStream streamIn )
        throws IOException, ClassNotFoundException {

        mImage = new BufferedImage( streamIn.readInt(), streamIn.readInt(), streamIn.readInt() );

        int[] savedBuffer = (int[]) streamIn.readObject();
        int[] buffer = ( (DataBufferInt) mImage.getRaster().getDataBuffer() ).getData();

        System.arraycopy( savedBuffer, 0, buffer, 0, savedBuffer.length );
    }
}

更新: @haraldK 要求提供一些基准。像所有的微基准一样,你应该对它们持保留态度。正如@haraldK 指出的那样,如果您将序列化形式写入磁盘,I/O 可能成为限制因素。始终针对您自己的用例进行测试。

但是进行内存中序列化/反序列化(到字节数组)的简单基准测试速度要快 10 倍以上。这似乎不是不合理的,因为 a) PNG 压缩是一项不平凡的任务;b) 大概PNG压缩至少要读取像素数据,然后对其进行大量处理,而SerializedImage只需要读取像素数据就可以了。

这是代码:

    // Create

    BufferedImage image = new BufferedImage( 100, 200, BufferedImage.TYPE_INT_ARGB );
    Graphics2D graphics = (Graphics2D) image.getGraphics();
    graphics.setColor( Color.red );
    graphics.fillRect( 1, 1, 50, 50 );
    SerializedImage serializedImage = new SerializedImage( image );

    long start = System.currentTimeMillis();

    for( int loop = 0; loop < 10_000; loop++ ) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // Write

        try ( ObjectOutputStream objectOutputStream = new ObjectOutputStream( outputStream ) ) {
            objectOutputStream.writeObject( serializedImage );
        }

        // Read

        try ( ObjectInputStream objectInputStream = new ObjectInputStream( new ByteArrayInputStream( outputStream.toByteArray() ) ) ) {
            ( (SerializedImage) objectInputStream.readObject() ).getImage();
        }
    }

    long end = System.currentTimeMillis() - start;
    System.out.println( "SerializedImage took " + end + "ms" );

    start = System.currentTimeMillis();

    for( int loop = 0; loop < 10_000; loop++ ) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // Write

        ImageIO.write( image, "png", outputStream );

        // Read

        ImageIO.read( new ByteArrayInputStream( outputStream.toByteArray() ));
    }

    end = System.currentTimeMillis() - start;
    System.out.println( "ImageIO took " + end + "ms" );

出于好奇,我改用 FileOutputStream/FileInputStream 尝试了这个。它仍然比 ImageIO 快,但只快 4 倍左右。@haraldK 的一些实验(如下)仅快 2 倍左右。所以你的里程可能会有所不同。

于 2014-09-12T00:23:48.260 回答