批量OCR许多PDF文件


20

一年前已经在这里讨论过:

批量处理许多PDF文件的OCR(还不是OCRed)?

有没有办法批处理尚未使用OCRed的OCR PDF?我认为这是当前处理两个问题的状态:

批量OCR PDF

视窗

  • Acrobat –这是将对OCR进行批处理的最直接的ocr引擎。唯一的问题似乎是1)它不会跳过已经是OCRed的文件2)尝试向其扔一些PDF(有些旧)并看着它崩溃。这有点越野车。它会警告您遇到的每个错误(尽管您可以告诉软件不通知。但是,同样,它会在某些类型的PDF上严重死亡,因此您的行程可能会有所不同。

  • ABBYY FineReader(批处理/扫描快照),Omnipage –这些必须是人类已知的最差的编程软件。如果您可以找到如何完全自动化(无提示)批处理具有相同名称PDF的 OCR,请在此处发布。看来,我能找到的唯一解决方案在某个地方失败了-重命名,不完全自动化等。充其量,有一种方法可以做到这一点,但是文档和编程是如此恐怖,以至于您永远找不到。

  • ABBYY FineReader EngineABBYY识别服务器 -这些确实是更多的企业解决方案,您可能最好让acrobat在文件夹上运行并尝试清除会给您带来错误/崩溃程序的pdf文件,而不是像尝试那样麻烦安装评估软件(假设您是简单的最终用户)。对于小型用户而言,似乎没有成本竞争力。

  • ** Autobahn DX工作站**该产品的价格是如此昂贵,您可能可以购买6份杂技演员。并不是真正的最终用户解决方案。如果您是企业设置,这对您来说值得。

的Linux

  • WatchOCR –不再开发,基本上不可能在现代Ubuntu发行版上运行
  • pdfsandwich –不再开发,基本上不可能在现代Ubuntu发行版上运行
  • ** ABBY LINUX OCR **-这应该可以编写脚本,并且似乎有一些不错的效果:

http://www.splitbrain.org/blog/2010-06/15-linux_ocr_software_comparison

但是,就像它们按页面收费的许多其他这些ABBYY产品一样,您最好使Acrobat Batch OCR正常工作。

  • ** Ocrad,GOCR,OCRopus,tesseract,** –这些可能有用,但存在一些问题:

    1. 对于其中的一些,OCR结果不如acrobat好(请参见上面的链接)。
    2. 这些程序均不接受PDF文件并输出PDF文件。您必须创建一个脚本并首先分解PDF,然后在每个脚本上运行程序,然后将文件重新组合为pdf。
    3. 完成后,您可能会像我一样发现(tesseract)创建了一个移过的OCR层。因此,如果您搜索单词“ the”,则会在该单词旁边看到一个突出的部分。
  • 批处理DjVu →转换为PDF –尚未研究,但似乎是一个可怕的解决方案。

线上

  • PDFcubed.com –来了,不是真正的批处理解决方案。
  • ABBYY Cloud OCR-不确定这是否真的是一个批处理解决方案,无论哪种方式,您都必须按页面付费,这可能会变得很昂贵。

识别非OCRed PDF

这是一个稍微容易一些的问题,可以在Linux中轻松解决,而在Windows中则更容易解决。我能够编写一个Perl脚本pdffont来识别是否嵌入了字体,以确定哪些文件不是OCRed。


当前的“解决方案”

  1. 使用脚本来识别非OCRed PDF(这样就不会重新运行成千上万的OCRed PDF)并将其复制到临时目录(保留正确的目录树)中,然后在Windows上使用Acrobat在这些目录上运行以希望较小的批次不会崩溃。

  2. 使用相同的脚本,但是获得Linux OCR工具之一才能正常工作,这会降低OCR的质量。

我想我将尝试#1,我只是过于担心Linux OCR工具的结果(我想没有人做过比较),将文件拆开再重新拼接在一起似乎如果Adobe实际上可以在不使目录阻塞的情况下对OCR进行批处理,则无需进行编码。

如果您想要一个完全免费的解决方案,则必须使用脚本来识别非OCRed的pdf(或仅在OCRed上重新运行),然后使用一种Linux工具尝试对其进行OCR。Teseract似乎取得了最佳效果,但是,再次说明,在现代版本的Ubuntu中,其中一些工具不被很好地支持,尽管如果您可以对其进行设置并解决我遇到的图像层与文本匹配层不匹配的问题(使用tesseract),那么您将拥有一个非常可行的解决方案,然后再次选择Linux> Windows。


您是否有一个可行的解决方案,可以完全自动化,批量处理OCR PDF,并以高质量跳过已经存在相同名称的OCRed文件?如果是这样,我将非常感谢您的投入。


Perl脚本将非OCRed文件移动到临时目录。无法保证可以正常工作,并且可能需要重写,但是如果有人使它正常工作(假设它不工作)或效果更好,请告诉我,我将在此处发布更好的版本。


#!/usr/bin/perl

# move non-ocred files to a directory
# change variables below, you need a base dir (like /home/joe/), and a sourcedirectory and output
# direcotry (e.g books and tempdir)
# move all your pdfs to the sourcedirectory

use warnings;
use strict;

# need to install these modules with CPAN or your distros installer (e.g. apt-get)
use CAM::PDF;
use File::Find;
use File::Basename;
use File::Copy;

#use PDF::OCR2;
#$PDF::OCR2::CHECK_PDF   = 1;
#$PDF::OCR2::REPAIR_XREF = 1;

my $basedir = '/your/base/directory';
my $sourcedirectory  = $basedir.'/books/';
my @exts       = qw(.pdf);
my $count      = 0;
my $outputroot = $basedir.'/tempdir/';
open( WRITE, >>$basedir.'/errors.txt' );

#check file
#my $pdf = PDF::OCR2->new($basedir.'/tempfile.pdf');
#print $pdf->page(10)->text;



find(
    {
        wanted => \&process_file,

        #       no_chdir => 1
    },
    $sourcedirectory
);
close(WRITE);

sub process_file {
    #must be a file
    if ( -f $_ ) {
        my $file = $_;
        #must be a pdf
        my ( $dir, $name, $ext ) = fileparse( $_, @exts );
        if ( $ext eq '.pdf' ) {
            #check if pdf is ocred
            my $command = "pdffonts \'$file\'";
            my $output  = `$command`;
            if ( !( $output =~ /yes/ || $output =~ /no/ ) ) {
                #print "$file - Not OCRed\n";
                my $currentdir = $File::Find::dir;
                if ( $currentdir =~ /$sourcedirectory(.+)/ ) {
                    #if directory doesn't exist, create
                    unless(-d $outputroot.$1){
                    system("mkdir -p $outputroot$1");
                    }
                    #copy over file
                    my $fromfile = "$currentdir/$file";
                    my $tofile = "$outputroot$1/$file";
                    print "copy from: $fromfile\n";
                    print "copy to: $tofile\n";
                    copy($fromfile, $tofile) or die "Copy failed: $!";
#                       `touch $outputroot$1/\'$file\'`;
                }
            }

        }

    }
}

您好,能否请您共享Windows脚本以识别非OCRed pdf(...)并将其复制到临时目录(保留正确的目录树)?预先感谢;)
Erb 2012年

@David好的,起来了。我警告您,它可能第一次无法正常运行。这根本不会损坏您的pdf文件(它只是复制而不会接触原始文件),但是我的意思是您可能必须修改脚本。如果您知道perl,这将是一件轻而易举的事情,如果没有让我知道,或者您可以自己进行调试并进行必要的较小编辑。

非常感谢。我将尝试使其工作(即使我是perl的新手)。谢谢。
Erb 2012年

也许是Windows中的另一个想法(适用于XP)?我过去曾使用过此命令,以便“从文件夹(带有子文件夹)中删除所有没有密码的pdf文件”。想法是保留所有受密码保护的pdf文件。使用Syncback免费软件将所有pdf(及其相关子文件夹)复制到一个新文件夹(C:\ 5 \“)中。添加pdftotext.exe,此文本文件在del_pdf_no_password.bat中重命名。其内容:” FOR / RC:\ 5 \% %×IN(* .PDF)DO(pdftotext %% X NUL && DEL %% X)”,其中“C:\ 5 \”是文件夹以变化然后启动pdftotext.exe才把bat文件
Erb 2012年

更多详细信息:您将需要使用免费软件重命名器(如示例:Alternativeto.net/software/renamer)删除任何文件夹名称内的空格(+特殊字符,如“,” ... )。否则,它不适用于所有子文件夹!附言:我没有写过这个脚本(2004年某人帮助我!)
Erb 2012年

Answers:


3

我也一直在寻找一种自动进行批处理OCR的方法,但运气不佳。最后,我想出了一个与您相似的可行解决方案,将Acrobat与以下脚本一起使用:

  1. 将所有相关的PDF复制到特定目录。

  2. 删除已经包含文本的PDF(假设它们已经是OCRd或已经是文本-我知道这并不理想,但目前已经足够了)。

  3. 使用AutoHotKey自动运行Acrobat,选择特定目录,并对所有文档进行OCR,然后在文件名后附加“ -ocr”。

  4. 使用“ -ocr.pdf”文件将OCRd PDF返回到其原始位置,以确定它是否成功。

有点像Heath Robinson,但实际上效果很好。


如果Acrobat已经对目录进行批处理,为什么还需要使用AutoHotKey?如果您担心acrobat崩溃会重复该过程,则文件修改的时间戳会告诉您停止的位置。如果您想保留原件,则只需复制目录即可。如果只想在末尾使用-ocr,则只需在完成后更改批名称即可。
2012年

1
您好,很幸运,您可以分享Windows中第2点和第3点的操作方法吗?在此先感谢;)
Erb 2012年

2

我相信您需要认识到ABBYY FineReader是一种最终用户解决方案,旨在提供快速,准确的即用型OCR。

根据我的经验,OCR项目每次都有非常不同的细节,并且无法为每个独特的案例创建现成的灵魂,但是我可以建议您使用更专业的工具来为您完成工作:

我是上面指定的云服务的前端开发团队的一员,并在必要时可以提供有关它的更多信息。

考虑到在PDF中查找文本层,因此我无法提供任何建议,因为此任务是我的专长,而不是OCR,因此我发现使用外部脚本的方法非常合理。也许您会发现此讨论很有帮助:http : //forum.ocrsdk.com/questions/108/check-if-pdf-is-scanned-image-or-contains-text


1
至少我们知道,ABBYY缺乏文档或功能(可在Acrobat中找到)来轻松批处理OCR pdf文件夹。非OCRed文档文件夹的简单批处理OCR是非常需要的功能(远远超过ABBYY的其他一些功能)。只是谷歌,以找出这种欲望是多么普遍,如果没有,我可以提供引用。感谢其他选项,我将深入研究它们,但是现在让任何来这里寻求完成这项非常常见任务(可提供参考)的人都知道,我们从马口中得知ABBYY无法做到这一点。
2012年

批处理OCR在ABBYY FineReader Professional中可用。在您的问题中,您指出需要完全自动化OCR。现在,您只需要进行批处理即可。请明确您的实际需求。
尼古拉

阅读以上部分。我说了“ EASILY批处理OCR”,“文件夹的简单批处理ocr”。进一步:“如果您能找到如何完全自动化(无提示)批处理OCR ..”。很明显我想要什么。因此,让访问此页面的任何人都清楚:*如果您要使用繁琐且复杂的界面以及大量的用户密集型过程中可怕的保存选项来“批量处理” pdf文件夹,ABBYY可能会为您服务*到“简易批处理OCR”,“简单批处理ocr”,像Acrobat一样,几乎没有像数千其他用户那样的用户交互,ABBYY Finereader不适合您。
2012年

2

在Linux上

最好和最简单的使用方法pypdfocr不会改变pdf

pypdfocr your_document.pdf

最后,您可以your_document_ocr.pdf通过可搜索的文本找到想要的另一种方式。该应用程序不会改变图像的质量。通过添加覆盖文本,稍微增加文件的大小。

批量PDF

ls ./p*.pdf | xargs -L1 -I {}  pypdfocr {}

如果PDF位于子文件夹中:

tree -fai . | grep -P ".pdf$" | xargs -L1 -I {}  pypdfocr {}

更新2018年11月3日:

pypdfocr自2016年以来不再受支持,我注意到由于未得到维护而出现了一些问题。ocrmypdfmodule)做辅助工作,可以这样使用:

ocrmypdf in.pdf out.pdf

安装:

pip install ocrmypdf

要么

apt install ocrmypdf

所以命令会变成

tree -fai . | grep -P ".pdf$" | xargs -L1 -I {}  ocrmypdf {} {}_ocr.pdf 

1

2015年初,我在Windows上使用Nuance OmniPage Ultimate进行了完全放手的批量OCR,取得了一些成功。不是免费的,标价500美元。使用附带的批处理程序“ DocuDirect”。它有一个选项“无需任何提示即可运行作业”,这似乎是对原始问题的直接答案。

我使用DocuDirect为每个输入图像输出一个可搜索的PDF文件(即,不可搜索的)。可以告诉它在输出文件夹中复制输入目录树以及原始输入文件名(几乎-见下文)。也使用多个核心。准确性是我评估过的软件包中最好的。将跳过受密码保护的文档(不停止作业,而不会显示对话框)。

注意事项1:几乎所有原始文件名-后缀“ .PDF”变为“ .pdf”(即,从大写到小写),因为嘿,在Windows上都一样。(啊。)

注意事项2:没有日志文件,因此要重新诊断哪些文件在识别期间会失败(它们确实会这样做)。DocuDirect会很高兴产生乱码输出,就像整个页面完全丢失一样。我使用PyPDF2模块编写了一个python脚本来实施粗略的验证:测试输出页数是否与输入页数匹配。见下文。

警告3:模糊,模糊的输入图像文件将导致OmniPage永久挂起,而不使用任何CPU。它永远无法恢复。这确实使批处理脱轨,我没有找到任何解决方法。我也将此事报告给了Nuance,但是一无所获。

@Joe对软件的编程和记录不正确是正确的。我注意到OmniPage 的核心具有惊人的字符识别魔术技术,但是外壳(GUI和批处理)足以使您脱颖而出。

我赞同@Joe和@Kiwi的建议,即使用脚本筛选出文件,以便仅向OCR软件包提供未受保护的图像文档。

我与Nuance的唯一隶属关系是不是非常满意的客户-我有一批未解决的支持票证以证明这一点:)

@乔:答案很晚,但也许仍然有意义。@SuperUser社区:我希望您觉得这是话题。

**更新**后继包是Nuance PowerPDF Advanced,标价仅为$ 150。我什至可以取得更好的成功,它虽然准确,但更加稳定。

OCR之前/之后的树验证python脚本如下。

'''
Script to validate OCR outputs against inputs.
Both input and output are PDF documents in a directory tree.
For each input document, checks for the corresponding output
document and its page count.

Requires PyPDF2 from https://pypi.python.org/pypi/PyPDF2
'''

from __future__ import print_function
from PyPDF2 import PdfFileReader
import getopt
import os
import stat
import sys

def get_pdf_page_count(filename):
    '''
    Gets number of pages in the named PDF file.
    Fails on an encrypted or invalid file, returns None.
    '''
    with open(filename, "rb") as pdf_file:
        page_count = None
        err = None
        try:
            # slurp the file
            pdf_obj = PdfFileReader(pdf_file)
            # extract properties
            page_count = pdf_obj.getNumPages()
            err = ""
        except Exception:
            # Invalid PDF.
            # Limit exception so we don't catch KeyboardInterrupt etc.
            err = str(sys.exc_info())
            # This should be rare
            print("Warning: failed on file %s: %s" % (filename, err), file=sys.stderr)
            return None

    return page_count

def validate_pdf_pair(verbose, img_file, txt_file):
    '''
    Checks for existence and size of target PDF file;
    number of pages should match source PDF file.
    Returns True on match, else False.
    '''
    #if verbose: 
    #    print("Image PDF is %s" % img_file)
    #    print("Text PDF is %s" % txt_file)

    # Get source and target page counts
    img_pages = get_pdf_page_count(img_file)
    txt_pages = get_pdf_page_count(txt_file)
    if img_pages is None:
        # Bogus PDF, skip.
        print("Warning: failed to get page count for %s" % img_file, file=sys.stderr)
        return None
    if txt_pages is None:
        # Bogus PDF, skip.
        print("Warning: failed to get page count for %s" % txt_file, file=sys.stderr)
        return None

    retval = True
    if img_pages != txt_pages:
        retval = False
        print("Mismatch page count: %d in source %s, %d in target %s" % (img_pages, img_file, txt_pages, txt_file), file=sys.stderr)

    return retval


def validate_ocr_output(verbose, process_count, total_count, img_dir, txt_dir):
    '''
    Walks a tree of files to compare against output tree, calling self recursively.
    Returns a tuple with PDF file counts (matched, non-matched).
    '''
    # Iterate over the this directory
    match = 0
    nonmatch = 0
    for dirent in os.listdir(img_dir):
        src_path = os.path.join(img_dir, dirent)
        tgt_path = os.path.join(txt_dir, dirent)
        if os.path.isdir(src_path):
            if verbose: print("Found source dir %s" % src_path)
            # check target
            if os.path.isdir(tgt_path):
                # Ok to process
                (sub_match, sub_nonmatch) = validate_ocr_output(verbose, process_count + match + nonmatch, total_count, 
                                         src_path, tgt_path)
                match += sub_match
                nonmatch += sub_nonmatch
            else:
                # Target is missing!?
                print("Fatal: target dir not found: %s" % tgt_path, file=sys.stderr)

        elif os.path.isfile(src_path):
            # it's a plain file
            if src_path.lower().endswith(".pdf"):
                # check target
                # HACK: OmniPage changes upper-case PDF suffix to pdf;
                # of course not visible in Windohs with the case-insensitive 
                # file system, but it's a problem on linux.
                if not os.path.isfile(tgt_path):
                    # Flip lower to upper and VV
                    if tgt_path.endswith(".PDF"):
                        # use a slice
                        tgt_path = tgt_path[:-4] + ".pdf"
                    elif tgt_path.endswith(".pdf"):
                        tgt_path = tgt_path[:-4] + ".PDF"
                # hopefully it will be found now!
                if os.path.isfile(tgt_path):
                    # Ok to process
                    sub_match = validate_pdf_pair(verbose, src_path, tgt_path)
                    if sub_match:
                        match += 1
                    else:
                        nonmatch += 1
                    if verbose: print("File %d vs %d matches: %s" % (process_count + match + nonmatch, total_count, sub_match))

                else:
                    # Target is missing!?
                    print("Fatal: target file not found: %s" % tgt_path, file=sys.stderr)
                    nonmatch += 1

        else:
            # This should never happen
            print("Warning: not a directory nor file: %s" % src_path, file=sys.stderr)
    return (match, nonmatch)

def count_pdfs_listdir(verbose, src_dir):
    '''
    Counts PDF files in a tree using os.listdir, os.stat and recursion.
    Not nearly as elegant as os.walk, but hopefully very fast on
    large trees; I don't need the whole list in memory.
    '''
    count = 0
    for dirent in os.listdir(src_dir):
        src_path = os.path.join(src_dir, dirent)
        # stat the entry just once
        mode = os.stat(src_path)[stat.ST_MODE]
        if stat.S_ISDIR(mode):
            # It's a directory, recurse into it
            count += count_pdfs_listdir(verbose, src_path)
        elif stat.S_ISREG(mode):
            # It's a file, count it
            if src_path.lower().endswith('.pdf'):
                count += 1
        else:
            # Unknown entry, print an error
            print("Warning: not a directory nor file: %s" % src_path, file=sys.stderr)
    return count

def main(args):
    '''
    Parses command-line arguments and processes the named dirs.
    '''
    try:
        opts, args = getopt.getopt(args, "vi:o:")
    except getopt.GetoptError:
        usage()
    # default values
    verbose = False
    in_dir = None
    out_dir = None
    for opt, optarg in opts:
        if opt in ("-i"):
            in_dir = optarg
        elif opt in ("-o"):
            out_dir = optarg
        elif opt in ("-v"):
            verbose = True
        else:
            usage()
    # validate args
    if in_dir is None or out_dir is None: usage()
    if not os.path.isdir(in_dir):
        print("Not found or not a directory: %s" % input, file=sys.stderr)
        usage()
    if not os.path.isdir(out_dir):
        print("Not found or not a directory: %s" % out_dir, file=sys.stderr)
        usage()
    if verbose: 
        print("Validating input %s -> output %s" % (in_dir, out_dir))
    # get to work
    print("Counting files in %s" % in_dir)
    count = count_pdfs_listdir(verbose, in_dir)
    print("PDF input file count is %d" % count)
    (match,nomatch) = validate_ocr_output(verbose=verbose, process_count=0, total_count=count, img_dir=in_dir, txt_dir=out_dir) 
    print("Results are: %d matches, %d mismatches" % (match, nomatch))

def usage():
    print('Usage: validate_ocr_output.py [options] -i input-dir -o output-dir')
    print('    Compares pre-OCR and post-OCR directory trees')
    print('    Options: -v = be verbose')
    sys.exit()

# Pass all params after program name to our main
if __name__ == "__main__":
    main(sys.argv[1:])

我刚刚看到了您的更新。我会试试看。我希望它可以安静地执行OCR,并且不会崩溃!(哇!1GB下载文件!)
Erb

0

您可以考虑使用Aquaforest的Autobahn DX:http ://www.aquaforest.com/en/autobahn.asp

它设计用于处理一批PDF,并具有多种选项(例如,跳过或传递OCRed文件),以及用于智能处理PDF的选项,这些选项可能会提供更好的结果(例如,如果PDF具有某些图像页面和某些图像页面,文本页面,它只能对图像页面进行OCR)


如果您是该产品的会员,请通过编辑问题明确声明。
slhck 2012年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.