How do I convert an enormous TIFF image to PNG/JPEG without out of memory error?
我有一个 18000 * 18000 尺寸和 1.20 GB 大小的 tiff 文件。 tiff 有 72 DPI。
我想使用 400 DPI 将此 TIFF 转换为 PNG/JPEG。
我正在使用下面的代码来做到这一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 编码器],
但是当我尝试为上述文件运行相同的代码时,它会引发以下异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 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 |
这是因为内存不足错误。
是否有可用的 Tiled Image Writer 或 Fragment Image Writer 使用它,我们一次只转换图像的一部分,因此我们可以使用可用的普通内存
我认为它可能被称为使用图像分割进行转换。
编辑
使用pngJ.
直接写入png文件
我的目的是将 SVG 画布转码为 400 DPI 的 PNG
如果我为此使用 PNGTranscoder,它会针对上述图像大小抛出内存不足异常。
所以我使用了 TiledImageTranscoder,它使用以下代码将 SVG 转码为 Image。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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文件而不会出现内存不足的错误,这样我们也可以节省时间并避免不必要的转换。
或
我们可以用 pngJ 库覆盖 PngImageWriter 的 writeImage 方法,从而实现我们的目标吗?
谢谢你
米希尔·帕瑞克
您可能会尝试找到一些支持渐进式(例如一次一行)处理的 TIFF 解码器和 JPEG/PNG 编码器。这个 TIFF 解码器似乎支持它; PNGJ 支持。
更新:尝试将 PNGJ 插入 PNGTrasncoder 似乎是可行的方法,但这并不容易:您(或我或其他人)必须对 RenderedImage 格式和 PNGJ 期望的格式之间的桥梁进行编码。 (PNGJ 有意与