【问题标题】:Does iText support OCR?iText 是否支持 OCR?
【发布时间】:2020-06-30 15:18:03
【问题描述】:

我想问一个关于 iText 的问题。 我在 PDF 文件中搜索文本时遇到问题。

我可以使用getTextfromPage() 方法创建一个纯文本文件,如以下代码示例所述:

/** The original PDF that will be parsed. */
    public static final String PREFACE = "D:/B.pdf";
    /** The resulting text file. */
    public static final String RESULT = "D:/Result.txt";
public void ParsePDF(String From, String Destination) throws IOException{

        PdfReader reader = new PdfReader(PREFACE);  
        PrintWriter out = new PrintWriter(new FileOutputStream(RESULT));              
        for (int i = 1; i <= reader.getNumberOfPages(); i++) {                          
         out.println(PdfTextExtractor.getTextFromPage(reader, i));  
        }
        out.flush();
        out.close();
        reader.close();

    }

我试图在结果文本中找到特定的String,如下所示:

    public void FindWords(String From) {
        try{
            String ligneLue;            
            LineNumberReader lnr=new LineNumberReader(new FileReader(RESULT));
            try{                
                while((ligneLue=lnr.readLine())!=null){
                    SearchForSVHC(ligneLue,SvhcList);
                }
            }
            finally{                
                lnr.close();
            }
        }
        catch(IOException e){
            System.out.println(e);}
        }   
    public void SearchForSVHC(String Ligne,List<String> List){
        for(String CAS :List){
            if(Ligne.contains(CAS)){
                System.out.print("Yes  "+CAS);
                break;
        }}
    }

我的问题是我正在解析的一些 PDF 包含扫描的图像,这意味着没有真正的文本,只有像素。

iText 是否支持光学字符识别 (OCR) 并作为后续问题:有没有办法确定 PDF 是否包含扫描图像?

【问题讨论】:

标签: java itext


【解决方案1】:

在回答您的问题之前,我已经对您的问题进行了非常彻底的编辑。

当 PDF 包含扫描的图像时,没有要解析的真实文本,只有具有 看起来 像文本的像素的图像。您需要进行 OCR 才能知道在这样的扫描页面上实际写了什么,而 iText 不支持 OCR。

关于后续问题:很难确定 PDF 是否包含扫描图像。第一个赠品是:页面中只有一张图片,没有文字。

但是:由于您对图像的性质一无所知(也许您的 PDF 只包含假日照片),因此很难确定 PDF 是否是一个充满扫描文本页面的文档(即:光栅化文本)。

【讨论】:

  • 非常感谢@Bruno Lowagie 先生,所以在这种情况下不可能警告用户文件包含图像,他必须进行视觉研究! :(
  • 好吧,您可以解析页面中的文本,如果找不到,请通知用户您找不到任何文本,因此应首先对 PDF 进行 OCR 处理。
  • 是的,我有一个包含 5 页的示例,其中 3 个是图像,另一个是文本,在这种情况下,我可以执行 PdfTextExtractor.getTextFromPage(reader, i) 并找出类型此页面的示例图像或文本(我认为是 Pdfbox !!!谢谢你
  • 是的,iText 和 PDFBox 都可以通知您每个页面上是否存在文本和/或图像。
【解决方案2】:

This 支持案例说 iText 不支持 OCR。识别图像是否包含文本就像将图像传递给 OCR 处理器并检查结果是否有意义一样简单。

【讨论】:

  • 谢谢,我的问题是我使用系统搜索。用户对研究和 PDF 格式一无所知.. 在这种情况下是否可以警告用户文件包含图像,他必须进行视觉研究!
【解决方案3】:

可以结合使用 iText 和 Tesseract(一种 google OCR 实现)来完成。

首先我会在 OCR 引擎周围放置一个界面。这让我以后可以换掉它。

public interface IOpticalCharacterRecognitionEngine {

class OCRChunk {
    private Rectangle location;
    private String text;
    public OCRChunk(Rectangle rectangle, String text){
        this.location = rectangle;
        this.text = text;
    }
    public String getText(){ return text; }
    public Rectangle getLocation(){return location;}
}

List<OCRChunk> doOCR(BufferedImage bufferedImage);
}

这个界面本质上说“OCR 引擎返回的对象是位置(矩形)和文本的组合”

然后我们需要创建一个 ITextExtractionStrategy 使用 OCREngine 将 ImageRenderInfo 事件转换为 TextRenderInfo

public class OCRTextExtractionStrategy implements ITextExtractionStrategy {

private final ITextExtractionStrategy innerStrategy;
private final IOpticalCharacterRecognitionEngine opticalCharacterRecognitionEngine;
private final Logger logger = Logger.getLogger(OCRTextExtractionStrategy.class.getSimpleName());

public OCRTextExtractionStrategy(ITextExtractionStrategy innerStrategy, IOpticalCharacterRecognitionEngine opticalCharacterRecognitionEngine){
    this.innerStrategy = innerStrategy;
    this.opticalCharacterRecognitionEngine = opticalCharacterRecognitionEngine;
}

public String getResultantText() {
    return innerStrategy.getResultantText();
}

public void eventOccurred(IEventData iEventData, EventType eventType) {
    // handle images
    if(eventType == EventType.RENDER_IMAGE){

        // extract coordinates
        ImageRenderInfo imageRenderInfo  = (ImageRenderInfo) iEventData;
        float x = imageRenderInfo.getImageCtm().get(Matrix.I11);
        float y = imageRenderInfo.getImageCtm().get(Matrix.I22);

        // attempt to parse image
        try {
            BufferedImage bufferedImage = imageRenderInfo.getImage().getBufferedImage();
            for(IOpticalCharacterRecognitionEngine.OCRChunk chunk : opticalCharacterRecognitionEngine.doOCR(bufferedImage)){
                if(chunk.getText() != null && !chunk.getText().isEmpty()) {
                    chunk.getLocation().translate((int) x, (int) y);
                    TextRenderInfo textRenderInfo = pseudoTextRenderInfo(chunk);
                    if(textRenderInfo !=  null)
                        innerStrategy.eventOccurred( textRenderInfo, EventType.RENDER_TEXT);
                }
            }
        } catch (IOException e) { logger.severe(e.getLocalizedMessage()); }

    }
    // handle anything else
    else {
        innerStrategy.eventOccurred(iEventData, eventType);
    }
}

private TextRenderInfo pseudoTextRenderInfo(IOpticalCharacterRecognitionEngine.OCRChunk chunk){

    // dummy graphics state
    ModifiableGraphicsState mgs = new ModifiableGraphicsState();
    try {
        mgs.setFont(PdfFontFactory.createFont());
        mgs.setCtm(new Matrix(  1,0,0,
                                0,1,0,
                                0,0,1));
    } catch (IOException e) { }

    // dummy text matrix
    float x = chunk.getLocation().x;
    float y = chunk.getLocation().y;
    Matrix textMatrix = new Matrix( x, 0,0,
                                0, y, 0,
                                0,0,0);

    // return TextRenderInfo object
    return new TextRenderInfo(
            new PdfString(chunk.getText(), ""),
            mgs,
            textMatrix,
            new Stack<CanvasTag>()

    );
}

public Set<EventType> getSupportedEvents() { return null; }

}

这个类执行那个翻译。 坐标变换有一些魔力(我可能还没有完全正确)。

这项工作的繁重工作是在pseudoTextRenderInfo 方法中执行的,该方法将IOpticalCharacterRecognitionEngine 给出的结果转换为TextRenderInfo 对象。

为了让它工作,我们需要一个可修改的CanvasGraphicsState。 默认实现不是。 所以让我们扩展默认值。

class ModifiableGraphicsState extends CanvasGraphicsState{

private Matrix ctm;

public ModifiableGraphicsState(){ super(); }

public Matrix getCtm() { return ctm; }
public ModifiableGraphicsState setCtm(Matrix ctm){this.ctm = ctm; return this;};
public void updateCtm(float a, float b, float c, float d, float e, float f) { updateCtm(new Matrix(a, b, c, d, e, f)); }
public void updateCtm(Matrix newCtm) {
    ctm = newCtm.multiply(ctm);
}

}

最后,我们需要一个IOpticalCharacterRecognitionEngine 的实现。 这个具体的实现是使用 Tesseract 完成的(如果您使用的是 Java,则为 tess4j)。

public class TesseractOpticalCharacterRecognitionEngine implements IOpticalCharacterRecognitionEngine {

private Tesseract tesseract;

public TesseractOpticalCharacterRecognitionEngine(File tesseractDataDirectory, String languageCode){
    tesseract = new Tesseract();

    // set data path
    if(!tesseractDataDirectory.exists())
        throw new IllegalArgumentException();
    tesseract.setDatapath(tesseractDataDirectory.getAbsolutePath());

    // set language code
    if(!new File(tesseractDataDirectory, languageCode + ".traineddata").exists())
        throw new IllegalArgumentException();
    tesseract.setLanguage(languageCode);
}

public List<OCRChunk> doOCR(BufferedImage bufferedImage) {
    List<OCRChunk> textChunkLocationList = new ArrayList<>();
    try {
        for(Rectangle rectangle : tesseract.getSegmentedRegions(bufferedImage, ITessAPI.TessPageIteratorLevel.RIL_WORD)){
            String text = tesseract.doOCR(bufferedImage, rectangle);
            textChunkLocationList.add(new OCRChunk(rectangle, text));
        }
    } catch (Exception e) { }
    return textChunkLocationList;
}
}

然后可以如下调用代码:

// initialize tesseract
TesseractOpticalCharacterRecognitionEngine ocrEngine = new TesseractOpticalCharacterRecognitionEngine(new File("tessdata_fast"), "eng");

// create document
PdfDocument pdfDocument = new PdfDocument(new PdfReader(new File("scanned_document.pdf")));

// extract text
SimpleTextExtractionStrategy simpleTextExtractionStrategy = new SimpleTextExtractionStrategy();
OCRTextExtractionStrategy ocrTextExtractionStrategy = new OCRTextExtractionStrategy(simpleTextExtractionStrategy, ocrEngine);
    new PdfCanvasProcessor(ocrTextExtractionStrategy).processPageContent(pdfDocument.getPage(1));

// display
System.out.println(simpleTextExtractionStrategy.getResultantText());

【讨论】:

  • OCRTextExtractionStrategy 中,您忽略了大部分图像转换矩阵,特别是在图像旋转 90° 的情况下,代码会将所有内容折叠到原点。然后按图像尺寸翻译文本块。稍后您将文本块位置用作文本矩阵的比例值。我还没有检查这一切是否都是偶然的,但一开始看起来很奇怪。
  • 我敢肯定,这一切都无法抵消,而且除了非平凡的 PDF 之外,其他任何东西的影响都是可怕的。此代码仅作为实现此目标的一般指南。
【解决方案4】:

截至今天,iText 确实有一个 OCR product,它使用 Tesseract 4.x。您可以在他们的Knowledge Base 上获取其所有文档。

这里列出了一个快速示例,介绍如何将图像 OCR 转换为 PDF/A-3u 文件。

import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.pdfocr.OcrPdfCreator;
import com.itextpdf.pdfocr.tesseract4.Tesseract4LibOcrEngine;
import com.itextpdf.pdfocr.tesseract4.Tesseract4OcrEngineProperties;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;


public class JDoodle {

    private static List LIST_IMAGES_OCR = Arrays.asList(new File("invoice_front.jpg"));
    private static String OUTPUT_PDF = "/myfiles/hello.pdf";
    private static final String DEFAULT_RGB_COLOR_PROFILE_PATH = "profiles/sRGB_CS_profile.icm";

    public static void main(String[] args) throws IOException {
        
        OcrPdfCreatorProperties properties = new OcrPdfCreatorProperties();
        properties.setPdfLang("en"); //we need to define a language to make it PDF/A compliant

        OcrPdfCreator ocrPdfCreator = new OcrPdfCreator(new Tesseract4LibOcrEngine(new Tesseract4OcrEngineProperties()), properties);
        try (PdfWriter writer = new PdfWriter(OUTPUT_PDF)) {
            ocrPdfCreator.createPdfA(LIST_IMAGES_OCR, writer, getRGBPdfOutputIntent()).close();
        }
    }

    public static PdfOutputIntent getRGBPdfOutputIntent() throws FileNotFoundException {
        InputStream is = new FileInputStream(DEFAULT_RGB_COLOR_PROFILE_PATH);
        return new PdfOutputIntent("", "",
                "", "sRGB IEC61966-2.1", is);
    }

}

来晚了,但我希望它有所帮助。

【讨论】:

  • 我遇到了几个 pdf 文件的问题。 Itextpdf 可以很好地读取大部分文档,但有时也会读取奇怪的字符。这也解决了我的问题吗?
  • @NumanKaraaslan,很难在没有看到具体内容的情况下提供帮助。您有可以分享的 PDF 文档吗?
【解决方案5】:

没有 iText 与 OCR 无关。源自扫描书籍的 PDF 可以包含作为图像、文本或(通常是为了保持视觉原件和文本搜索能力)的页面;当然,如果它包含文本,那是因为一些 OCR 已经完成了。

检测 PDF 仅包含图像而没有文本可能是一件棘手的事情,但一个简单的启发式方法是尝试提取文本(请参阅example)并确定 PDF 是否返回纯图像所有(大多数)页面的(几乎)空文本。

【讨论】:

  • 谢谢,我的问题是我使用系统搜索。用户对研究和 PDF 格式一无所知.. 在这种情况下是否可以警告用户文件包含图像,他必须进行视觉研究!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
相关资源
最近更新 更多