如何将巨大的 TIFF 图像转换为 PNG/JPEG 而不会出现内存不足错误?

2023-12-26

我有一个 tiff 文件 18000 * 18000 尺寸和1.20 GB在尺寸方面。 tiff 的 DPI 为 72。

我想使用 400 DPI 将此 TIFF 转换为 PNG/JPEG。

我正在使用以下代码来做到这一点

public static void ConvertTiffToJpg(String str_TiffUrl,
                String str_JpgFileDestinationUrl) throws Exception {
            try {
                FileSeekableStream obj_FileSeekableStream = new FileSeekableStream(
                        new File(str_TiffUrl));
                ImageDecoder obj_ImageDecoder = ImageCodec.createImageDecoder(
                        "tiff", obj_FileSeekableStream, null);
                RenderedImage obj_RenderedImage = obj_ImageDecoder
                        .decodeAsRenderedImage();
                JAI.create("filestore", obj_RenderedImage,
                        str_JpgFileDestinationUrl, "jpeg");
                obj_RenderedImage = null;
                obj_ImageDecoder = null;
                obj_FileSeekableStream.close();
            } catch (Exception ex) {
                throw ex;
            }

上面的代码非常适合较小的图像,然后指定的图像(例如尺寸小于 5000 * 5000 的 tiff 图像)可以轻松转换为 JPEG / PNG [尽管我需要更改 PNG 编码器],

但是当我尝试对上述文件运行相同的代码时,它会抛出以下异常

Error: One factory fails for the operation "encode"
    Occurs in: javax.media.jai.ThreadSafeOperationRegistry
    java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at javax.media.jai.FactoryCache.invoke(FactoryCache.java:122)
        at javax.media.jai.OperationRegistry.invokeFactory(OperationRegistry.java:1674)
        at javax.media.jai.ThreadSafeOperationRegistry.invokeFactory(ThreadSafeOperationRegistry.java:473)
        at javax.media.jai.registry.RIFRegistry.create(RIFRegistry.java:332)
        at com.sun.media.jai.opimage.FileStoreRIF.create(FileStoreRIF.java:138)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at javax.media.jai.FactoryCache.invoke(FactoryCache.java:122)
        at javax.media.jai.OperationRegistry.invokeFactory(OperationRegistry.java:1674)
        at javax.media.jai.ThreadSafeOperationRegistry.invokeFactory(ThreadSafeOperationRegistry.java:473)
        at javax.media.jai.registry.RIFRegistry.create(RIFRegistry.java:332)
        at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:819)
        at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
        at javax.media.jai.RenderedOp.getRendering(RenderedOp.java:888)
        at javax.media.jai.JAI.createNS(JAI.java:1099)
        at javax.media.jai.JAI.create(JAI.java:973)
        at javax.media.jai.JAI.create(JAI.java:1621)
        at com.vs.graphics.concepts.TiffToJpeg.ConvertTiffToJpg(TiffToJpeg.java:30)
        at com.vs.graphics.svg.SvgRefresh$1.actionPerformed(SvgRefresh.java:106)
        at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
        at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
        at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
        at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
        at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
        at java.awt.Component.processMouseEvent(Component.java:6216)
        at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
        at java.awt.Component.processEvent(Component.java:5981)
        at java.awt.Container.processEvent(Container.java:2041)
        at java.awt.Component.dispatchEventImpl(Component.java:4583)
        at java.awt.Container.dispatchEventImpl(Container.java:2099)
        at java.awt.Component.dispatchEvent(Component.java:4413)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4556)
        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4220)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4150)
        at java.awt.Container.dispatchEventImpl(Container.java:2085)
        at java.awt.Window.dispatchEventImpl(Window.java:2475)
        at java.awt.Component.dispatchEvent(Component.java:4413)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
    Caused by: java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferByte.<init>(DataBufferByte.java:42)
        at java.awt.image.Raster.createInterleavedRaster(Raster.java:253)
        at java.awt.image.Raster.createInterleavedRaster(Raster.java:194)
        at com.sun.media.jai.codecimpl.JPEGImageEncoder.encode(JPEGImageEncoder.java:182)
        at com.sun.media.jai.opimage.EncodeRIF.create(EncodeRIF.java:70)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at javax.media.jai.FactoryCache.invoke(FactoryCache.java:122)
        at javax.media.jai.OperationRegistry.invokeFactory(OperationRegistry.java:1674)
        at javax.media.jai.ThreadSafeOperationRegistry.invokeFactory(ThreadSafeOperationRegistry.java:473)
        at javax.media.jai.registry.RIFRegistry.create(RIFRegistry.java:332)
        at com.sun.media.jai.opimage.FileStoreRIF.create(FileStoreRIF.java:138)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at javax.media.jai.FactoryCache.invoke(FactoryCache.java:122)
        at javax.media.jai.OperationRegistry.invokeFactory(OperationRegistry.java:1674)
        at javax.media.jai.ThreadSafeOperationRegistry.invokeFactory(ThreadSafeOperationRegistry.java:473)
        at javax.media.jai.registry.RIFRegistry.create(RIFRegistry.java:332)
        at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:819)
        at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
        at javax.media.jai.RenderedOp.getRendering(RenderedOp.java:888)
        at javax.media.jai.JAI.createNS(JAI.java:1099)
        at javax.media.jai.JAI.create(JAI.java:973)
        at javax.media.jai.JAI.create(JAI.java:1621)
        at com.vs.graphics.concepts.TiffToJpeg.ConvertTiffToJpg(TiffToJpeg.java:30)
        at com.vs.graphics.svg.SvgRefresh$1.actionPerformed(SvgRefresh.java:106)
        at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
        at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
    Error: One factory fails for the operation "filestore"
    Occurs in: javax.media.jai.ThreadSafeOperationRegistry

这是因为内存不足错误。

是否有可用的平铺图像写入器或片段图像写入器使用它我们一次只转换图像的一部分,因此我们可以使用正常的可用内存 我认为这可能被称为使用图像分割进行转换。

EDIT

使用pngJ直接写入png文件。

我的目的是将 SVG 画布转码为 400 DPI 的 PNG

如果我使用PNG转码器 http://xmlgraphics.apache.org/batik/javadoc/org/apache/batik/transcoder/image/PNGTranscoder.html为此,它会针对提到的图像大小引发内存不足异常。

所以我用过平铺图像转码器 http://svn.apache.org/repos/asf/xmlgraphics/batik/trunk/contrib/tiledTranscoder/TiledImageTranscoder.java它使用以下代码将 SVG 转码为图像。

protected void transcode(Document document, String uri,
            TranscoderOutput output) throws TranscoderException {

        // Sets up root, curTxf & curAoi
        super.transcode(document, uri, output);

        Filter f = this.root.getGraphicsNodeRable(true);

        RenderContext rc = new RenderContext(curTxf, null, null);
        RenderedImage img = f.createRendering(rc);

        // prepare the image to be painted
        int w = img.getWidth();
        int h = img.getHeight();

        try {
            int bands = img.getSampleModel().getNumBands();
            int[] off = new int[bands];
            for (int i = 0; i < bands; i++)
                off[i] = i;
            SampleModel sm = new PixelInterleavedSampleModel(
                    DataBuffer.TYPE_BYTE, w, (100000 + w - 1) / w, bands, w
                            * bands, off);

            RenderedImage rimg = new FormatRed(GraphicsUtil.wrap(img), sm);

            TIFFImageEncoder enc = new TIFFImageEncoder(output
            .getOutputStream(), null);
                    enc.encode(rimg);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

正如您在这里看到的,上面的代码最终使用 TIFFImageEncoder 逐步写入磁盘,并在我的例子中生成 1.30 GB 的 TIFF 文件。

这就是为什么我需要将此生成的文件转换为 PNG 文件。

我的问题是专门针对@leonbloy的

我们可以使用 pngJ 库中的 PNGWriter 直接使用 400 DPI 写入 png 文件,而不会出现内存不足错误,这样我们也可以节省时间并避免不必要的转换。

or

我们可以覆盖吗PNG图像编写器 http://xmlgraphics.apache.org/batik/javadoc/org/apache/batik/ext/awt/image/codec/png/PNGImageWriter.html的 writeImage 方法与 pngJ 库这样我们就可以实现我们的目标吗?

谢谢 米希尔·帕雷克


您可能会尝试找到一些支持渐进式(例如一次一行)处理的 TIFF 解码器和 JPEG/PNG 编码器。这TIFF解码器 http://painterly.costar.sfu.ca/browser/trunk/libs/jiu/net/sourceforge/jiu/codecs/tiff/TIFFDecoder.java?rev=372似乎支持它;PNGJ https://code.google.com/p/pngj/支持它。

更新:尝试将 PNGJ 插入 PNGTrasncoder 似乎是可行的方法,但这并不那么容易:您(或我或某人)必须对 RenderedImage 格式和 PNGJ 期望的格式之间的桥梁进行编码。 (PNGJ 有意与java.awt.*)。当我有时间的时候,它可能会看看,这似乎是蜡染中包含的一个有趣的替代方案,我预见的唯一限制是我不支持隔行扫描,但我认为这不相关。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将巨大的 TIFF 图像转换为 PNG/JPEG 而不会出现内存不足错误? 的相关文章

随机推荐