【问题标题】:Extracting fields from forms with varying structures从具有不同结构的表单中提取字段
【发布时间】:2017-08-21 23:11:22
【问题描述】:

我正在尝试从资产负债表中提取某些字段。例如,我想知道以下资产负债表的“库存”值为 1,277,838:

目前,我正在使用 Tesseract 将图像转换为文本。但是,这种转换会产生文本流,因此很难将字段与其值相关联(因为这些值并不总是紧邻其对应字段的文本)。

经过一番搜索,我读到 Tesseract 可以使用 uzn 文件从图像的区域中读取。但是,资产负债表值的特定区域可能会因表格而异,因此我对任何可以确定“库存”和 1,277,838 在同一条线上的解决方案感兴趣。理想情况下,我想要一个文本的网格结构输出(这样我就可以在空间上告诉哪些文本块在同一行/列中)。

谁能帮助解释我如何才能达到这个结果?

【问题讨论】:

    标签: python ocr tesseract


    【解决方案1】:

    我一直在使用 Tesseract 和 Python(pytesseract 库)执行类似的任务。我已经能够使用 Tesseract 的 .hocr 输出文件 (https://en.wikipedia.org/wiki/HOCR) 在页面上找到我的搜索词(例如“库存”)的位置,然后在页面的一小部分上重新运行 Tesseract,从而使其具有更高的准确性那个区域。这是我用来解析 Tesseract 的 HOCR 输出的代码:

    def parse_hocr(search_terms=None, hocr_file=None, regex=None):
        """Parse the hocr file and find a reasonable bounding box for each of the strings
        in search_terms.  Return a dictionary with values as the bounding box to be used for 
        extracting the appropriate text.
    
        inputs:
            search_terms = Tuple, A tuple of search terms to look for in the HOCR file.
    
        outputs:
            box_dict = Dictionary, A dictionary whose keys are the elements of search_terms and values
            are the bounding boxes where those terms are located in the document.
        """
        # Make sure the search terms provided are a tuple.
        if not isinstance(search_terms,tuple):
            raise ValueError('The search_terms parameter must be a tuple')
    
        # Make sure we got a HOCR file handle when called.
        if not hocr_file:
            raise ValueError('The parser must be provided with an HOCR file handle.')
    
        # Open the hocr file, read it into BeautifulSoup and extract all the ocr words.
        hocr = open(hocr_file,'r').read()
        soup = bs.BeautifulSoup(hocr,'html.parser')
        words = soup.find_all('span',class_='ocrx_word')
    
        result = dict()
    
        # Loop through all the words and look for our search terms.        
        for word in words:
    
            w = word.get_text().lower()
    
            for s in search_terms:
    
                # If the word is in our search terms, find the bounding box
                if len(w) > 1 and difflib.SequenceMatcher(None, s, w).ratio() > .5:
                    bbox = word['title'].split(';')
                    bbox = bbox[0].split(' ')
                    bbox = tuple([int(x) for x in bbox[1:]])
    
                    # Update the result dictionary or raise an error if the search term is in there twice.
                    if s not in result.keys():
                        result.update({s:bbox})
    
                else:
                    pass
    
        return result 
    

    这使我可以在 HOCR 文件中搜索适当的术语并返回该特定单词的边界框。然后我可以稍微扩展边界框以在页面的一个非常小的子集上运行 Tesseract。这比仅对整个页面进行 OCR 具有更高的准确性。显然,其中一些代码是我使用的特定代码,但它应该为您提供一个开始的地方。

    This page 非常有助于找到合适的参数以提供给 Tesseract。我发现页面分割模式对于获得图像小部分的准确结果非常重要。

    【讨论】:

    • 谢谢,我最终将您的方法用于 PIL 和 pyocr 库。
    • 我很高兴它有帮助!我应该提到我在我的例子中使用了 pytesseract。我发现它是在 Python 中使用 Tesseract 的最简单的接口。
    • 感谢您的帮助,但是您是如何在图像的特定边界框或部分运行 tesseract 的?
    • @YashGupta,使用边界框的坐标裁剪原始图像。
    • 我不知道如何让 Tesseract 只查看图像的子集。我过去的做法是在运行 Tesseract 之前裁剪图像(例如使用 PIL)。您可能想就这个主题提出另一个关于 SO 的问题。此外,请查看this link 以获取一些提示。也许设置页面分割方法会对您的结果有所帮助?
    【解决方案2】:

    正如 gaw89 已经提到的,Tesseract 可以输出比仅作为流的文本更多的信息。 hocr fileformat 还为您提供每个段落、行、单词的位置(边界框):

    $ tesseract 4LV05.png out -l eng hocr
    

    然后你可以通过简单的方法找到“Inventory”这个词的边界框

    $ grep 'Inventory' out.hocr
     <span class='ocr_line' id='line_1_5' title="bbox 23 183 112 204; baseline 0 -5; x_size 21; x_descenders 5; x_ascenders 4"><span class='ocrx_word' id='word_1_15' title='bbox 23 183 112 204; x_wconf 93'>Inventory</span>
    

    因此,这个词的边界框垂直跨度从 183 到 204,对于这个标签的对应值,我们现在必须在相同的垂直空间中搜索框。例如,这可以通过

    来实现
    $ grep 'bbox [0-9]* 18[0-9]' out.hocr
    <p class='ocr_par' id='par_1_4' lang='eng' title="bbox 23 183 112 204">
     <span class='ocr_line' id='line_1_5' title="bbox 23 183 112 204; baseline 0 -5; x_size 21; x_descenders 5; x_ascenders 4"><span class='ocrx_word' id='word_1_15' title='bbox 23 183 112 204; x_wconf 93'>Inventory</span>
     <span class='ocr_line' id='line_1_30' title="bbox 1082 183 1178 202; baseline 0 -3; x_size 22; x_descenders 5.5; x_ascenders 5.5"><span class='ocrx_word' id='word_1_82' title='bbox 1082 183 1178 202; x_wconf 93'>1,277,838</span>
     <span class='ocr_line' id='line_1_54' title="bbox 1301 183 1379 202; baseline 0 -3; x_size 22; x_descenders 5.5; x_ascenders 5.5"><span class='ocrx_word' id='word_1_107' title='bbox 1301 183 1379 202; x_wconf 95'>953,675</span>
    

    第二个结果包含目标值。可以比较bbox的纵坐标,确保提取出第一列。

    命令grep 在这个例子中就足够了,但当然还有其他方法可以做类似的事情。另请注意,正则表达式可能应替换为其他计算,具体取决于页面的倾斜程度。

    或者,您可以尝试开源Tabula,它会尝试从 pdf 中提取表格数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-20
      • 2022-07-08
      • 2018-10-05
      • 1970-01-01
      • 1970-01-01
      • 2023-02-23
      • 2019-01-02
      • 1970-01-01
      相关资源
      最近更新 更多