Как извлечь данные таблицы из отсканированного PDF? - PullRequest
0 голосов
/ 25 октября 2018

Я создал проект Java, который довольно успешно анализирует PDF-файлы с поиском, имеющие определенную структуру.Таблицы в нем являются сложными, объединяя строки или столбцы, но в каждом таком PDF структура таблиц остается неизменной, изменяется только текст внутри.Мне удалось преодолеть все эти проблемы, вооружившись PDFBox, PDF2Dom и Tabula.

Однако проблема возникла вчера, когда мне предоставили свежий набор PDF-файлов, которые были отсканированы.При сканировании весь контент представлял собой только изображения и не был доступен для поиска.Почувствовав необходимость в оптическом распознавании символов, я начал исследовать Тессеракт.Тем не менее, я обнаружил, что только его использование просто выкашливает весь текст PDF без какого-либо контекста, и флажки будут потеряны.Поэтому я попытался преобразовать PDF-файл в файл с возможностью поиска, используя комбинацию Ghostscript и Tesseract.Я преобразовал отсканированные PDF в изображения jpg с помощью Ghostscript следующим образом:

File pdfFile = new File("D://Tess//inputFile.pdf");
List<Image> images = new ArrayList<Image>();
PDFDocument document = new PDFDocument();
document.load(pdfFile);

SimpleRenderer renderer = new SimpleRenderer();
renderer.setResolution(300);

images = renderer.render(document);

for (int i = 0; i < images.size(); i++) {
    Image img = images.get(i);
    ImageIO.write((RenderedImage) img, "jpg", new File(i + ".jpg"));
}

После этого я преобразовал сгенерированные изображения обратно в PDF с использованием Tesseract.

Tesseract tessInst = new Tesseract();
tessInst.setDatapath("D://Tess//tessdata");
List<RenderedFormat> list = new ArrayList<RenderedFormat>();
list.add(RenderedFormat.PDF);

for (int i = 0; i < images.size(); i++)
    tess.createDocuments(i + ".jpg", "D://Tess//output" + i, list);

PDF-файлы былигенерируется нормально и даже доступен для поиска, но когда я выбираю слово, выделение выделения немного искажается от фактического слова.Кроме того, флажки не могут быть выбраны.Я попытался сгенерировать структуру DOM с использованием PDF2Dom, как я делал это с другими PDF-файлами, которые можно было искать без обработки OCR и получить отличные результаты:

Document document = parser.createDOM(pdf);

Это выдает следующее исключение:

java.io.IOException: java.io.IOException: Multi byte glyph name not supported.

at org.mabb.fontverter.pdf.PdfFontExtractor.convertType0FontToOpenType(PdfFontExtractor.java:217)

at org.fit.pdfdom.FontTable$Entry.loadType0TtfDescendantFont(FontTable.java:193)

at org.fit.pdfdom.FontTable$Entry.getData(FontTable.java:146)

at org.fit.pdfdom.FontTable$Entry.isEntryValid(FontTable.java:162)

at org.fit.pdfdom.FontTable.addEntry(FontTable.java:49)

at org.fit.pdfdom.PDFBoxTree.processFontResources(PDFBoxTree.java:381)

at org.fit.pdfdom.PDFBoxTree.updateFontTable(PDFBoxTree.java:358)

at org.fit.pdfdom.PDFDomTree.updateFontTable(PDFDomTree.java:544)

at org.fit.pdfdom.PDFBoxTree.processPage(PDFBoxTree.java:204)

at org.apache.pdfbox.text.PDFTextStripper.processPages(PDFTextStripper.java:319)

at org.apache.pdfbox.text.PDFTextStripper.writeText(PDFTextStripper.java:266)

at org.fit.pdfdom.PDFDomTree.createDOM(PDFDomTree.java:218)

at com.pv.pdf.PdfExtractor.extractCheckboxValues(PdfExtractor.java:403)

at com.pv.pdf.PdfExtractor.getMedicalRecordDetails(PdfExtractor.java:372)

at com.pv.servlet.OnServletLogin.doPost(OnServletLogin.java:32)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)

at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)

at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)

at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:67)

at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)

at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)

at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)

at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)

at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)

at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)

at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)

at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)

at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)

at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)

at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)

at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)

at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)

at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)

at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)

at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)

at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)

at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)

at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)

at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)

at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)

at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)

at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)

at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)

at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)

at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)

at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)

at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)

at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)

at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)

at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)

at io.undertow.server.Connectors.executeRootHandler(Connectors.java:360)

at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)

at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)

at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)

at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)

at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)

at java.lang.Thread.run(Unknown Source)

Caused by: java.io.IOException: Multi byte glyph name not supported.

at org.mabb.fontverter.converter.PsType0ToOpenTypeConverter.convertCmap(PsType0ToOpenTypeConverter.java:89)

at org.mabb.fontverter.converter.PsType0ToOpenTypeConverter.convert(PsType0ToOpenTypeConverter.java:50)

at org.mabb.fontverter.pdf.PdfFontExtractor.convertType0FontToOpenType(PdfFontExtractor.java:215)

... 57 more

Я обнаружил эту проблему, которая присутствовала в Ghostscript относительно ширины глифа:

https://github.com/tesseract-ocr/tesseract/issues/712

Однако я не уверен, может ли она помочь мне в этом текущем случае использования.Но это также говорит о перекосе выделенных текстовых бликов, как в моем случае.Я использую Ghost4j версии 1.0.1, которая эквивалентна Ghostscript версии 9.25, поэтому описанную здесь проблему следует устранить.

Пожалуйста, помогите мне с этой проблемой.Заранее благодарю.

РЕДАКТИРОВАТЬ

Я не виню Ghostscript за ошибку.Но поскольку при поиске я обнаружил схожую проблему с моей, я предоставил ее здесь, чтобы, если она действительно указывала на коренную проблему, для более образованных людей было бы относительно легко ответить на мою проблему.

РЕДАКТИРОВАТЬ

Я думаю, что моя проблема может быть связана с тем фактом, что Tesseract создает шрифт «без глифов» для выходного PDF, и, поскольку он без глифов, структура DOM не может быть сгенерирована, так каку него нет таблицы поиска глифов для шрифта.Я пытался найти, как изменить выходной шрифт, но не повезло.Самое близкое, что я получил, было это:

https://unix.stackexchange.com/questions/306051/tesseract-is-it-possible-to-change-font-output-in-ocred-pdf/353191#353191

Но я не знаю, какие изменения потребуются для того, чтобы это работало.Tesseract должен был предоставить это в качестве настраиваемого параметра.

1 Ответ

0 голосов
/ 26 октября 2018

Текст Извлечение PDF, созданного с помощью PDF Writer, уже нетривиальная задача.Добавление сложности понимания того, что это изложено в табличной форме, добавляет еще один уровень сложности.Необходимость OCR отсканированных изображений для преобразования их в невидимый текст в PDF добавляет еще один уровень сложности.

Возможно, у программного обеспечения OCR есть проблема точности с тем, где он размещает текст на странице PDF относительно того, гдесимволы лежат в данных изображения, когда изображение накладывается.Это приведет к неправильному выводу текста.Это может быть недостатком в программном обеспечении или просто вам нужно отрегулировать некоторые настраиваемые параметры для точной настройки результатов OCR в этом случае.

Хорошим тестом может быть использование коммерческого предложения, напримерAdobe Acrobat выполняет OCR для определенного PDF-файла, содержащего только изображения, а затем пытается выяснить, действительно ли они получили такое позиционирование, как вы ожидаете, или столкнутся с подобной проблемой.

Что касается вашего точного исключения, мне повезло с отслеживанием этого значения до здесь в java-библиотеке FontVert (не уверен, что вы используете библиотеку напрямую), которая выглядит какдоморощенное предложение.

Возможно, вы могли бы спросить эту компанию / частное лицо, если это просто ограничение дизайна в их программном обеспечении (я думаю, что это потому, что я не понимаю, зачем вам нужно конвертировать форматы шрифтов в этом случае).

Чтение флажков находится вне поддержки OCR и входит в поддержку OMR .Что добавило бы еще один уровень сложности к тому, что вы делаете сегодня.

...