我有一个程序,我必须将 BufferedImage 序列化到磁盘,然后将其读回。目前我使用的是原始图像格式
ImageIO.getImageReadersByFormatName("raw");
经过大量测试后,似乎它在相同数据上的性能差异很大(从不到一秒到 15 秒)。
1. 知道这是怎么回事吗?
2. 在 Java 中完成这项任务最快的图像读取器/写入器是什么?
谢谢
我有一个程序,我必须将 BufferedImage 序列化到磁盘,然后将其读回。目前我使用的是原始图像格式
ImageIO.getImageReadersByFormatName("raw");
经过大量测试后,似乎它在相同数据上的性能差异很大(从不到一秒到 15 秒)。
1. 知道这是怎么回事吗?
2. 在 Java 中完成这项任务最快的图像读取器/写入器是什么?
谢谢
也就是说,我想这png
通常是一个不错的尝试选择。
除非您需要特定程序之外的互操作性,否则通过 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 倍左右。所以你的里程可能会有所不同。