Estoy tratando de realizar OCR en archivos PDF. Hay 2 pasos en el código:
- Convertir pdf a archivos tiff
- Convertir tiff a texto
Usé ghost4j para el primer paso, y luego tess4j para el segundo. todo funcionó muy bien, hasta que comencé a ejecutarlo con varios subprocesos, y luego ocurrieron extrañas excepciones. Leí aquí: https://sourceforge.net/p/tess4j/discussion/1202293/thread/44cc65c5/ que ghost4j no es adecuado para multihilo, así que cambié el primer paso para trabajar con PDFBox.
Entonces ahora mi código se ve así:
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();
Estoy tratando de ejecutar este código con un archivo pdf de 800 kb, y cuando verifico la memoria después de
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);
¡Se eleva a más de 500 MB! si estoy guardando esta imagen Buffered en el disco, la salida es de 1 MB de tamaño ... así que cuando intento ejecutar este código con 8 hilos, también obtengo la excepción de tamaño de almacenamiento dinámico de Java ...
¿Que me estoy perdiendo aqui? ¿Por qué un archivo de 1 MB da como resultado un archivo de imagen de 500 MB? Traté de jugar con el DPI y reducir la calidad, pero el archivo sigue siendo muy grande ... ¿Hay alguna otra biblioteca que pueda procesar PDF en TIFF y que pueda ejecutar 10 hilos sin problemas de memoria?
Pasos para reproducir:
- Descargue el archivo de currículum de Linkedin CEO desde aquí: https://gofile.io/?c=TtA7XQ
Yo que usé este código:
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"); }
y la salida es:
Aplicación iniciada ....... Memoria usada: 42 MB
Antes .... Memoria usada: 107 MB
BufferedImage .... Memoria usada: 171 MB
En este ejemplo, no son 500 MB, sino un pdf de 70 kb, cuando intento renderizar solo una página, la memoria aumenta en aproximadamente 70 MB ... no es proporcional ...
BufferedImage
después de renderizar?Respuestas:
Una dimensión 3300 X 2550 de un byte por píxel entregaría alrededor de 70_000_000 bytes. Con 150 ppp, uno tendría 22 pulgadas por 17 pulgadas, demasiado grande.
Así que reduzca la imagen a aprox. 17 MB de memoria:
Guárdelo en
png
lugar detiff
ver si eso marca la diferencia.fuente
El problema se resolvió en la discusión en PDFBOX-4739 :
ImageIOUtils.writeImage()
lugar deImageIO.write()
(necesitará el subproyecto de herramientas), porque ImageIO no comprime los archivos TIFF. ImageIOUtils intenta usar LZW o CCITT, dependiendo de la imagen de origen.doOCR()
método que toma una imagen Buffered como parámetro, por lo que no es necesario guardarla.fuente