如何序列化包含BufferedImage的对象

21
我正在尝试在Java中创建一个简单的图像编辑程序。我创建了一个名为ImageCanvas的对象,它包含有关正在编辑的图像的所有信息(一些基本属性、应用的效果列表、BufferedImage层的列表等)。我想要一种简单的方法将其保存到磁盘上,以便以后可以再次打开。
我认为使用Java的默认Serializable接口可能正是我要寻找的,我可以将整个对象写入文件,然后在稍后的时间重新读取它。但是,ImageCanvas包括一个ArrayList<BufferedImage>,而BufferedImage不可序列化(其他所有内容都可以)。
我知道可以覆盖writeObject()readObject()方法,但我从未这样做过,并且想知道是否有任何简单的方法可以让Java序列化其他所有内容,并使用自定义方式读取/写入BufferedImage到磁盘?或者是否有其他更容易将整个ImageCanvas对象写入磁盘的方法被我忽略了?最终我可能会实现自己的自定义图像文件类型,但是现在我想要一个快速而简单的方法来暂时保存文件,同时进行测试(ImageCanvas类会经常更改,因此在最终确定之前,我不想不断更新我的自定义文件类型)。
2个回答

36
请将你的ArrayList<BufferedImage>设置为瞬态,并实现自定义的writeObject()方法。在此方法中,先写入ImageCanvas的常规数据,然后手动使用PNG格式写出图像的字节数据。
class ImageCanvas implements Serializable {
    transient List<BufferedImage> images;

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(images.size()); // how many images are serialized?
        for (BufferedImage eachImage : images) {
            ImageIO.write(eachImage, "png", out); // png is lossless
        }
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        final int imageCount = in.readInt();
        images = new ArrayList<BufferedImage>(imageCount);
        for (int i=0; i<imageCount; i++) {
            images.add(ImageIO.read(in));
        }
    }
}

我该如何使用它?方法是私有的。 - avicennasoftwarelabs
1
@avicennasoftwarelabs,只需实现writeObject和readObject方法即可告诉Java序列化在序列化/反序列化对象时使用这些方法,即使这些方法是私有的。 - Sam Barnum
5
很不幸,ImageIO中的PNG解码器存在漏洞,这意味着您读回的数据与您写入的数据不同(颜色通常偏差1)。 - mjaggard
这个方法不会抛出有关 BufferedImage 无法序列化的异常吗?因为您使用了 out.defaultWriteObject() - HopefullyHelpful
1
@HopefullyHelpful 不会,因为 BufferedImage 列表是瞬态的,所以不会被序列化。我们使用 ImageIO.write 将原始字节写入流中。 - Sam Barnum

0

序列化在持久化静态数据方面非常直观。 如果您使用序列化方法族中的读/写对象,那么您就在正确的位置上了。 想一想“BufferedImage”是什么。 它是一个缓冲流实现。 要进行序列化,必须将数据刷新到像byte[]数组这样的静态对象中,然后可以将该对象序列化/反序列化为BufferedImage,以便缓冲流现在进出该byte[]数组。


你能否提供一些代码展示如何在我的情况下实现这个功能(如何将BufferedImage转换为byte[]以及如何将其与ImageCanvas序列化的其他部分结合起来)? - scaevity
按照 BufferedImage 上的方法(如果必须使用此类)来处理构造函数。从这里可以使用 WritableRaster(和其他数据)来创建 BufferedImage。BufferedImage 有一个 getWritableTile(x,y) 方法,返回一个 WritableRaster。Raster 类,它是 WritableRaster 继承而来的,有各种 getPixel() 方法,例如可以返回原始 int[] 数组。虽然不是非常简单,但可以反向操作,从原语返回到 BufferedImage。还要考虑使用不同的基本图像类,这些类更容易进行序列化/反序列化。 - Darrell Teague

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接