2016-07-30 11 views
0

我正在開發一種算法從閱讀順序中的PDF文件中提取文本和圖像。我爲此使用iText java,基本上我的算法的工作原理如下。iText提取文本塊的錯誤位置

  1. 使用iText提取頁面中每個文本塊的座標。
  2. 使用提取的座標創建矩形對象。在這一步之後,我們有一大堆代表頁面中實際文本塊的矩形對象。
  3. 將矩形分組爲更大的文本塊,這些文本塊將對應於pdf頁面中的實際列。
  4. 按Y對文本塊進行排序,然後輸入X
  5. locationTextExtractionStrategy逐個應用於文本塊。

這種方法使得我的PDF文件的結果大約有80%或略多一些,而且中等到複雜的佈局。我知道,要獲得100%的準確度幾乎是不可能的,因爲PDF文件不會按照閱讀順序存儲信息。

我想要做的是提高我的準確性,但問題是iText阻止我這樣做。我在iText中發現了一個問題。它有時會提取文本塊的錯誤位置,這使得我的算法不正確。以下圖片就是一個很好的例子。

Actual Pdf Page Resulting Rectangles after extraction using iText

你可以看到,在實際的PDF頁面有列之間明顯的差距。但是由此產生的矩形包含了一些有缺陷的矩形,它們之間的差距阻礙了我識別正確的列。

以下是我用來提取文本塊位置的代碼。

package com.InteliText.Extract; 
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.Map.Entry; 

import com.itextpdf.text.Rectangle; 
import com.itextpdf.text.pdf.parser.ImageRenderInfo; 
import com.itextpdf.text.pdf.parser.LineSegment; 
import com.itextpdf.text.pdf.parser.SimpleTextExtractionStrategy; 
import com.itextpdf.text.pdf.parser.TextExtractionStrategy; 
import com.itextpdf.text.pdf.parser.TextRenderInfo; 
import com.itextpdf.text.pdf.parser.Vector; 

/* 
* THIS CLASS ACT AS THE TEXT EXTRACTOR FOR THE PREPROCESSOR 
*/ 
public class PreProcessorStrategy extends SimpleTextExtractionStrategy{ 

    private StringBuilder result = new StringBuilder(); 

    private ArrayList<Double> fontSizes = new ArrayList<Double>(); 
    private ArrayList<Double> lineSpaces = new ArrayList<Double>(); 
    private ArrayList<TextSegment> textSegments = new ArrayList<TextSegment>(); 

    Vector previousBaseLine = null; 

    @Override 
    public void beginTextBlock() { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void endTextBlock() { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void renderImage(ImageRenderInfo arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void renderText(TextRenderInfo renderInfo) { 

     //This code assumes that if the baseline changes then we're on a newline 
     Vector curBaseline = renderInfo.getBaseline().getStartPoint(); 
     Vector topRight = renderInfo.getAscentLine().getEndPoint(); 
     //System.out.println(renderInfo.getText()+"\t"+curBaseline.get(0)+"\t"+topRight.get(0)); 

     if(curBaseline.get(1) < 800 && curBaseline.get(1) > 50) { 
      // Chunk of text as a rectangle 
      Rectangle rect = new Rectangle(curBaseline.get(0), curBaseline.get(1), topRight.get(0), topRight.get(1)); 

      double curFontSize = rect.getHeight(); 
      fontSizes.add(curFontSize); 
      String text = renderInfo.getText(); 
      boolean isBullet = text.contains("•"); 
      if(!(text.equals(" ") || text.equals(" ") || text.equals(" ")) && !isBullet) { 
       double endX = topRight.get(0); 
       if(text.endsWith(" ")) 
        endX -= 8; 

       textSegments.add(new TextSegment(curBaseline.get(0),endX,curBaseline.get(1),topRight.get(1),renderInfo.getText(),curFontSize)); 
      } 

      result.append(renderInfo.getText()); 
     } 
     previousBaseLine = topRight; 
    } 

    @Override 
    public String getResultantText() { 
     // TODO Auto-generated method stub 
     return result.toString(); 
    } 

    public ArrayList<TextSegment> getResultantTextSegments() { 
     return this.textSegments; 
    } 

我使用產生textSegments ArrayList中通過查看存儲在這些textSegments座標以創建矩形對象。我懷疑這可能是iText中的一個錯誤。

正如您目前所看到的,如果該文本塊的內容以空白結尾,我會縮小文本塊的大小。但這是一個臨時性的修復,我不想這樣做,因爲它縮小了正確的文本塊。

那麼有沒有這方面的工作?或者,如果這是我的代碼中的問題,請幫我解決這個問題..

+0

它看起來像文本顯示操作顯示尾部空格,這是您的框的一部分,並超出列邊界。 – mkl

回答

1

我在這裏假設,如果你知道列在哪裏,你可以將每個矩形分配到正確的列。在我看來,如果您沿着右側列的左側邊緣繪製了一條線,則可以基於它們的中心位於該邊的右側還是左側來正確地分配幾乎所有的矩形。因此,問題是在存在異常值的情況下,找到描述數據最佳參數(尤其是最右邊一列的左側邊緣)。

絕對正確的方法可能適合某種統計模型,但我認爲有幾個更簡單的黑客可能工作。

1)圖像中所有重疊的矩形看起來都很小。也許你可以簡單地刪除給定大小以下的矩形,找出列的位置,然後根據它的中心位於右列的左邊緣的左邊還是右邊來分配每個小矩形。

2)有一個通用的策略來擬合被異常值污染的數據,你可以從https://en.wikipedia.org/wiki/RANSAC得出異常值。 2)首先將模型擬合到少量的數據。你會重複2a和2b多次,並選擇最好的結果。您希望爲其中一個案例選擇的初始點完全沒有異常值。請注意,如果有N個異常值,並且將數據分成N + 1個塊,則至少有一個塊必須完全沒有異常值。 2b)一旦你有了最初的擬合,看看所有的數據,並計算出哪些點是異常值,並暫時忽略它們(即拋開k個最差的擬合點)。然後使用剩餘的點再次擬合模型。在許多情況下,你可以證明,如果你無限期地重複這個步驟,它最終會收斂到某個東西,因爲改變被識別爲k個最差擬合的點會提高擬合度,就像重新擬合模型一樣,所以每次迭代都會提高擬合度,直到你沒有任何變化,此時你聲明過程已經收斂。

+0

非常感謝您的建議。你的第一個建議是行不通的,因爲在某些pdf文件中,幾乎所有的矩形都很小。因爲iText有時候每個文本塊只能提取一個字母。所以,如果我忽略了較小的盒子,那麼我可能會錯過所有需要的矩形。 轉到第二個建議,您的答案可能適用於上面提供的示例。但是我看過很多pdf頁面,幾乎所有的文本行都會攔截其他列。在這些情況下,在我看來,忽略異常值不會帶來更好的結果......您怎麼看? –

+0

要做到這一點,正確的方法幾乎可以肯定是爲矩形的生成方式和最終位置創建一個統計模型,然後將此模型應用於可用數據。爲了測試這類事情並開發模型,您需要收集測試數據,特別是如果某些頁面(例如您提供的問題所在的那個頁面)不能代表大多數現實生活中發生的情況。或者使用機器學習。挑出頁面和矩形的哪些特徵是重要的(這是困難的部分),然後使用例如Weka嘗試各種機器學習計劃。 – mcdowella