web-dev-qa-db-ja.com

多くのPDFをバッチOCR

これは1年前にここで議論されました:

多くのバッチOCR PDFファイル(まだOCRedではない)?

まだOCRedになっていないOCR PDFをバッチ処理する方法はありますか?これは、次の2つの問題を扱う現状です。

バッチOCR PDF

ウィンドウズ

  • Acrobat–これは、OCRをバッチ処理する最もまっすぐなocrエンジンです。唯一の問題は、1)すでにOCRedになっているファイルをスキップしないこと、2)PDFの束(古いもの)を投げて、クラッシュすることを確認することです。少しバギーです。エラーが発生するたびに警告が表示されます(ただし、ソフトウェアに通知しないように指示することはできます。ただし、特定の種類のPDFではひどく死ぬため、走行距離が異なる場合があります。

  • ABBYY FineReader(Batch/Scansnap)、Omnipage–これらは人間に知られている最悪のソフトウェアの一部であること。 自動化(プロンプトなし)PDFsのバッチOCRを同じ名前、ここに投稿してください。私が見つけた唯一の解決策はどこかで失敗したようです-名前の変更、完全に自動化されていないなどなど。せいぜい、それを行う方法はありますが、ドキュメントとプログラミングはあまりにもひどいため、決して見つけることはできません。

  • ABBYY FineReader EngineABBYY Recognition Server-これらは実際にはよりエンタープライズソリューションです、評価ファイルをインストールしようとする煩わしさを体験するよりも、アクロバットをフォルダー上で実行して、エラー/プログラムをクラッシュさせる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の結果は、たとえばこれらの一部のアクロバットほど優れていません(上記のリンクを参照)。
    2. どのプログラムもPDFファイルを取り込んでPDFファイルを出力しません。スクリプトを作成してPDFを最初に分解し、それぞれに対してプログラムを実行してから、ファイルをPDFとして再構築する必要があります
    3. 一度実行すると、私が行ったように、(テッセラクト)がシフトされたOCRレイヤーを作成することがわかります。したがって、「the」という単語を検索すると、その隣にある単語の部分のハイライトが表示されます。
  • Batch DjVu→Convert to PDF –まだ調べていませんが、恐ろしい一発解決策のようです。

オンライン

  • PDFcubed.com–さあ、バッチソリューションではありません。
  • ABBYY Cloud OCR-これが本当にバッチソリューションであるかどうかは不明です。どちらにしても、ページごとに支払う必要があり、これは非常に高価になる可能性があります。

OCRed以外のPDFの識別

これは少し簡単な問題で、Linuxでは簡単に解決でき、Windowsではそれほど簡単に解決できません。 pdffontを使用してPerlスクリプトをコーディングし、どのファイルがOCRedではないかを判断するためにフォントが埋め込まれているかどうかを識別できました。


現在の「ソリューション」

  1. スクリプトを使用して非OCRed pdfを識別し(何千ものOCRed PDFを再実行しないようにします)、これらを一時ディレクトリにコピーし(正しいディレクトリツリーを保持)、WindowsでAcrobatを使用してこれらを実行します。バッチはクラッシュしません。

  2. 同じスクリプトを使用しますが、Linux ocrツールの1つを適切に動作させると、ocrの品質が低下します。

私は#1を試すつもりだと思います。LinuxOCRツールの結果について心配しすぎています(比較を行った人はいないと思います)。ファイルを分割して、再度結合すると、 Adobeが窒息することなくディレクトリを実際にOCRでバッチ処理できる場合、不要なコーディングは不要です。

完全に無料のソリューションが必要な場合は、スクリプトを使用してOCRed以外のPDFを識別(またはOCRedのPDFを再実行)し、Linuxツールの1つを使用してそれらを試してOCRする必要があります。 Teseractが最良の結果をもたらすようですが、繰り返しますが、これらのツールの一部は、Ubuntuの最新バージョンではうまくサポートされていませんが、設定して問題を修正できれば、画像レイヤーがテキストマッチングレイヤーと一致しないという問題がありました( tesseractを使用した場合)、かなり実用的なソリューションが得られ、もう一度Linux> Windowsになります。


完全に自動化し、バッチOCR PDFを実行するための実用的なソリューションはありますか?高品質?もしそうなら、私は本当に入力をいただければ幸いです。


OCRed以外のファイルを一時ディレクトリに移動するPerlスクリプト。これが機能することを保証できず、おそらく書き換えが必要ですが、誰かがそれを機能させる(機能しない場合)か、うまく機能するようになったら、私に知らせてください。ここでより良いバージョンを投稿します。


#!/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\'`;
                }
            }

        }

    }
}
20
Joe

私も多くのPDFを自動でバッチOCRする方法を探しましたが、あまりうまくいきませんでした。最終的に、Acrobatとスクリプトを次のように使用して、あなたに似た実行可能なソリューションを思い付きました。

  1. 関連するすべてのPDFを特定のディレクトリにコピーします。

  2. すでにテキストが含まれているPDFを削除します(それらがすでにOCRdまたはすでにテキストであると仮定します-理想的ではありませんが、今のところ十分です)。

  3. AutoHotKey を使用してAcrobatを自動的に実行し、特定のディレクトリを選択し、すべてのドキュメントをOCRして、ファイル名に「-ocr」を追加します。

  4. 「-ocr.pdf」ファイルの存在を使用してOCRd PDFを元の場所に戻し、成功したかどうかを判断します。

それは少し Heath Robinson ですが、実際にはかなりうまくいきます。

4
kiwi

ABBYY FineReaderは高速で正確なOCRを提供するように設計されたエンドユーザーソリューションであることを理解する必要があると思います。

私の経験に基づいて、OCRプロジェクトの詳細は毎回大きく異なります。ユニークなケースごとにすぐに使える魂を作る方法はありませんが、あなたのために仕事をすることができるより専門的なツールを提案できます:

  • ABBYY Recognition Server をご覧ください。これは、OCR自動化のための専門的な製品です。

  • Linuxに関しては、 http://ocr4linux.com をご覧ください。これは、コマンドラインユーティリティでもあり、あなたに合うかもしれません。

  • より複雑なタスクの場合ABBYYは ABBYY FineReader Engine (社内でホストされる)または ABBYY Cloud OCR SDK (ベースのような非常に柔軟なSDKを持っていますMicrosoft Azureクラウド)では、OCR処理を思い通りに処理できます。

私は上記のクラウドサービスのフロントエンド開発チームの一員であり、必要に応じて詳細情報を提供できます。

PDFのテキストレイヤーのルックアップを考えると、このタスクは私の専門であるOCRを少し脇に置いているので、アドバイスはできません。外部スクリプトを使用するアプローチは非常に合理的です。多分あなたはこの議論が役に立つと思うでしょう: http://forum.ocrsdk.com/questions/108/check-if-pdf-is-scanned-image-or-contains-text

3
Nikolay

2015年の初めに、WindowsでNuance OmniPage Ultimateを使用して完全にハンドオフのバッチOCRを行うことに成功しました。無料ではなく、定価$ 500。付属のバッチプログラム「DocuDirect」をご利用ください。これには、「プロンプトなしでジョブを実行する」オプションがあり、元の質問に対する直接の回答のようです。

DocuDirectを使用して1つの検索可能なPDFファイル(つまり、検索不能)ごとにファイルを出力しました)PDFファイル;入力ディレクトリを複製するように指示できます出力フォルダーのツリーと元の入力ファイル名(ほとんど-以下を参照)も使用します。複数のコアも使用します。精度は私が評価したパッケージの中で最高でした。パスワードで保護されたドキュメントはスキップされます(ジョブを停止せず、表示せずに)ダイアログ)。

注意1:ほとんどの元のファイル名-接尾辞 ".PDF"は ".pdf"(つまり、大文字から小文字)になります。これは、Windowsではすべて同じだからです。 (うん)

注意2:ログファイルがないため、認識中に失敗したファイル(間違いなくそうです)を診断する必要があります。 DocuDirectは、ページ全体が欠けているような、文字化けした出力を喜んで生成します。 pythonスクリプトをPyPDF2モジュールを使用して大まかな検証を実装し、出力ページ数が入力ページ数と一致するかどうかをテストしました。以下を参照してください。

警告3:ぼやけた不明瞭な入力画像ファイルは、CPUを使用せずにOmniPageを永久にハングさせます。回復しないだけです。これは本当にバッチ処理を狂わせてしまい、回避策は見つかりませんでした。これもNuanceに報告しましたが、どこにも届きませんでした。

@Joeは、ソフトウェアのプログラミングと文書化が不十分であることについて正しい。 OmniPageのcoreには驚くべき文字認識マジックテクノロジーがありますが、外側のシェル(GUIとバッチ処理)で髪を引き出すことができます。

私は@Joeと@Kiwiの提案を支持し、スクリプトを使用してファイルを除外し、保護されていない画像ドキュメントのみを含むOCRパッケージを提示します。

Nuanceとの私の唯一の提携は、完全に満足していない顧客としてです-私はそれを証明するための未解決のサポートチケットのバッチを持っています:)

@ジョー:遅い答えですが、おそらく関連性があります。 @SuperUserコミュニティ:これが話題になっていると感じていただければ幸いです。

**更新**後継パッケージはNuance PowerPDF Advancedで、定価はたったの$ 150です。私はこれでさらに良い成功を収めました。正確さは同じですが、はるかに安定しています。

Pre/Post-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:])
2
chrisinmtown

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 
2

アクアフォレストのアウトバーンDXを検討できます: http://www.aquaforest.com/en/autobahn.asp

PDFのバッチを処理するように設計されており、さまざまなオプション(たとえば、スキップまたはパススルーOCRedファイル)だけでなく、より良い結果を提供できるPDFをスマートに処理するためのオプション(たとえば、PDFいくつかの画像ページといくつかのテキストページがあり、画像ページだけをOCRできます)

1
Neil Pitman

MacまたはLinuxの場合:

parallel --tag -j 2 ocrmypdf '{}' 'output/{}' ::: *.pdf

ここ から。

1
André Levy