Ich versuche, OCR für PDFs durchzuführen. Der Code besteht aus 2 Schritten:
- Konvertieren Sie PDF in TIFF-Dateien
- Konvertieren Sie tiff in Text
Ich habe Ghost4j für den ersten Schritt und dann Tess4j für den zweiten Schritt verwendet. Alles funktionierte großartig, bis ich anfing, es mit mehreren Threads auszuführen, und dann traten seltsame Ausnahmen auf. Ich habe hier gelesen: https://sourceforge.net/p/tess4j/discussion/1202293/thread/44cc65c5/, dass ghost4j nicht für Multithreading geeignet ist, daher habe ich den ersten Schritt geändert, um mit PDFBox zu arbeiten.
Jetzt sieht mein Code so aus:
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();
Ich versuche, diesen Code mit einer 800-KB-PDF-Datei auszuführen und beim Überprüfen des Speichers nach dem
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);
es erhöht sich auf mehr als 500 MB !! Wenn ich dieses BufferedImage auf der Festplatte speichere, hat die Ausgabe eine Größe von 1 MB. Wenn ich also versuche, diesen Code mit 8 Threads auszuführen, erhalte ich auch die Ausnahme für die Größe des Java-Heapspeichers ...
Was fehlt mir hier? Warum führt eine 1-MB-Datei zu einer 500-MB-Bilddatei? Ich habe versucht, mit der DPI zu spielen und die Qualität zu verringern, aber die Datei ist immer noch sehr groß ... Gibt es eine andere Bibliothek, die PDF in TIFF rendern kann und die ich 10 Threads ohne Speicherprobleme ausführen könnte?
Schritte zum Reproduzieren:
- Laden Sie die Lebenslaufdatei des Linkedin-CEO von hier herunter - https://gofile.io/?c=TtA7XQ
Ich habe dann diesen Code verwendet:
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"); }
und die Ausgabe ist:
App gestartet ....... Verwendeter Speicher: 42 MB
Vorher .... Verwendeter Speicher: 107 MB
BufferedImage .... Verwendeter Speicher: 171 MB
In diesem Beispiel sind es nicht 500 MB, sondern ein PDF von 70 KB. Wenn ich versuche, nur eine Seite zu rendern, erhöht sich der Speicher um ca. 70 MB ... es ist nicht proportional ...
BufferedImage
nach dem Rendern überprüfen ?Antworten:
Eine Dimension 3300 x 2550 von einem Byte pro Pixel würde ungefähr 70_000_000 Bytes liefern. Mit 150 dpi hätte man 22 Zoll mal 17 Zoll, viel zu groß.
Skalieren Sie das Bild also auf ca. 17 MB Speicher:
Speichern Sie es als,
png
umtiff
zu sehen, ob dies einen Unterschied macht.quelle
Das Problem wurde in der Diskussion in PDFBOX-4739 gelöst :
ImageIOUtils.writeImage()
stattImageIO.write()
(Sie benötigen das Teilprojekt tools), da ImageIO keine TIFF-Dateien komprimiert. ImageIOUtils versucht, je nach Quellbild LZW oder CCITT zu verwenden.doOCR()
Methode, die ein BufferedImage als Parameter verwendet, sodass Sie es überhaupt nicht speichern müssen.quelle