Небольшие PDF-файлы с огромным BufferdImage - PullRequest
8 голосов
/ 07 января 2020

Я пытаюсь выполнить OCR для PDF-файлов. В коде есть 2 шага:

  1. Конвертировать pdf в файлы tiff
  2. Конвертировать 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();

Я пытаюсь запустить этот код с файлом PDF размером 800 Кб, и при проверке памяти после

BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);

он увеличивается до более чем 500 МБ !! если я сохраняю этот BufferedImage на диск, размер вывода составляет 1 МБ ... поэтому при попытке запустить этот код с 8 потоками я получаю исключение java размера кучи также ...

Что мне здесь не хватает? почему файл размером 1 МБ приводит к файлу изображения размером 500 МБ? Я пытался поиграть с DPI и снизить качество, но файл все еще очень большой ... Есть ли какая-нибудь другая библиотека, которая может рендерить pdf в tiff и могла бы выполнить 10 потоков без проблем с памятью?

Шаги для воспроизведения:

  1. Загрузите отсюда файл резюме генерального директора Linkedin - https://gofile.io/?c=TtA7XQ
  2. Я, чем использовал это код:

    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 МБ

До .... Использованная память: 107 МБ

BufferedImage .... Используемая память: 171 МБ

В данном примере это не 500 МБ, а pdf размером 70 КБ , когда я пытаюсь визуализировать только одну страницу, увеличение памяти примерно на 70 МБ ... это не пропорционально ...

Ответы [ 2 ]

0 голосов
/ 13 января 2020

Проблема была решена в обсуждении в PDFBOX-4739 :

  • используйте ImageIOUtils.writeImage() вместо ImageIO.write() (вам потребуется подпроект инструментов), потому что ImageIO не сжимает файлы TIFF. ImageIOUtils пытается использовать LZW или CCITT, в зависимости от исходного изображения.
  • вообще не сохраняет изображение: есть метод doOCR(), который принимает BufferedImage в качестве параметра, поэтому нет необходимости сохранять вообще .
0 голосов
/ 13 января 2020

Размер 3300 X 2550, равный одному байту на пиксель, составляет около 70_000_000 байтов. С разрешением 150 точек на дюйм можно было бы получить 22 на 17 дюймов, что слишком велико.

Так что уменьшите изображение до прибл. 17 МБ памяти:

    float scale = 0.5f;
    BufferedImage bufferedImage = pdfRenderer.renderImage(page, scale, ImageType.BINARY);

Сохраните его как png вместо tiff, чтобы увидеть, имеет ли это значение.

...