带有巨大 BufferdImage 的小型 pdf 文件结果
Posted
技术标签:
【中文标题】带有巨大 BufferdImage 的小型 pdf 文件结果【英文标题】:Small pdf files results with huge BufferdImage 【发布时间】:2020-04-24 20:56:06 【问题描述】:我正在尝试对 pdf 执行 OCR。 代码中有2个步骤:
-
将 pdf 转换为 tiff 文件
将 tiff 转换为文本
第一步我使用ghost4j,第二步使用tess4j。 一切都很好,直到我开始多线程运行它,然后发生了奇怪的异常。 我在这里读到:https://sourceforge.net/p/tess4j/discussion/1202293/thread/44cc65c5/ 说ghost4j不适合多线程,所以我把第一步改成使用PDFBox。
所以现在我的代码如下所示:
PDDocument doc = PDDocument.load(this.bytes);
PDFRenderer pdfRenderer = new PDFRenderer(doc);
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "tiff", os);
os.flush();
os.close();
bufferedImage.flush();
我正在尝试使用 800 kb pdf 文件运行此代码,并且在检查内存后
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);
它增加到超过 500 MB!如果我将此 BufferedImage 保存到磁盘,则输出大小为 1 MB...所以当尝试使用 8 个线程运行此代码时,我也会遇到 java 堆大小异常...
我在这里缺少什么?为什么一个 1 MB 的文件会产生一个 500 MB 的图像文件?我尝试使用 DPI 并降低质量,但文件仍然很大...... 是否有任何其他库可以将 pdf 呈现为 tiff,并且我可以执行 10 个线程而不会出现内存问题?
重现步骤:
从这里下载 Linkedin CEO 简历文件 - https://gofile.io/?c=TtA7XQ
我没有使用此代码:
private static void test() throws IOException
printUsedMemory("App started...");
File file = new File("linkedinceoresume.pdf");
try (PDDocument doc = PDDocument.load(file))
PDFRenderer pdfRenderer = new PDFRenderer(doc);
printUsedMemory("Before");
for (int page = 0; page < 1; ++page)
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(page, 76, ImageType.GRAY);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "tiff", os);
os.flush();
os.close();
bufferedImage.flush();
finally
printUsedMemory("BufferedImage");
private static void printUsedMemory(String text)
long freeMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long mb = freeMemory / 1000000;
System.out.println(text + "....Used memory: " + mb + " MB");
输出是:
应用程序已启动.......已用内存:42 MB
之前....已用内存:107 MB
BufferedImage....已用内存:171 MB
在这个例子中,它不是 500 MB,而是 70 kb 的 pdf,当我尝试只渲染一页时,内存增加了大约 70 MB...这不成比例...
【问题讨论】:
请分享PDF文件。也许如果有一个巨大的图像尺寸输出尺寸? 渲染后可以查看BufferedImage
的尺寸吗?
请注意,高内存消耗不一定表示内存泄漏。也许页面包含需要大量内存来解码的位图对象? PDFBox 在以较小尺寸渲染时是否对图像进行二次采样?如果没有,以小尺寸渲染可能无济于事......
Pdfbox 默认不进行二次采样,但可以在 PDFRenderer 中启用。
@NicolasFilotto 在 PDFRenderer 中激活子采样。但是二次采样对于 OCR 来说可能不是一个好主意。
【参考方案1】:
每像素一个字节的 3300 X 2550 尺寸将提供大约 70_000_000 个字节。 使用 150 dpi 时,尺寸为 22 英寸 x 17 英寸,太大了。
所以将图片缩小到大约。 17 MB 内存:
float scale = 0.5f;
BufferedImage bufferedImage = pdfRenderer.renderImage(page, scale, ImageType.BINARY);
将其保存为png
而不是tiff
,看看这是否会有所不同。
【讨论】:
OP想做OCR,所以300dpi是个不错的选择。但是您在图像类型上是对的,我在 PDFBOX-4739 中提出了相同的建议。 (还发现图片是未压缩保存的) @TilmanHausherr 我部分成功地使用 150 dpi 进行 OCR,但实际上 300 dpi 是常态。使用上面的 ByteArrayOutputStream 可能也很昂贵,【参考方案2】:问题已在PDFBOX-4739的讨论中解决:
使用ImageIOUtils.writeImage()
而不是ImageIO.write()
(您将需要工具子项目),因为 ImageIO 不压缩 TIFF 文件。 ImageIOUtils 尝试使用 LZW 或 CCITT,具体取决于源图像。
根本不保存图像:有一个doOCR()
方法以BufferedImage 作为参数,所以根本不需要保存。
【讨论】:
以上是关于带有巨大 BufferdImage 的小型 pdf 文件结果的主要内容,如果未能解决你的问题,请参考以下文章
多个小型 spritesheet 或一个巨大的 spritesheet 以提高性能? ---(java游戏开发)[关闭]