データ・サイエンティストは可視化の夢を見るか?

Does Data Scientist Dream of Visualization?

半自動化 OCR

おはようございます。
我が家の『自炊書籍』も 500 冊近くになりました。
ScanSnap SV600 で丹念にスキャンしてくれている母に感謝です。

さて、そのようにして作成した自炊 PDF、そのままだと取り扱いにくいです。
以前述べたとおり、論文など PDF の管理(マーキングやコメント)は "Mendeley" に一元化したのですが、偶にはキーワード検索したくなる。そういうときに『大雑把にでも』OCR しておくことは大変重要です。現在の OCR の認識精度はそれなりものに留まっていますが、それでも「粗方、検索できるように」文字認識しておくことは、あとあとのことを考えるとかなり大切な下準備になっています。
将来的にもっと精度の高い OCR が出てきたら、再認識させれば好いことですからね。

この作業、これまで手でやってきましたが、定型作業であることもあって面倒です。
さらに今回、旧約・新約聖書を分冊して取り扱う必要があったので、一気に数十の PDF を OCR に掛ける必要が出てきました。そうなるとケアレス・ミスの可能性だって出てきます。

そこでこの文字認識作業の工程を自動化できないかどうか挑戦してみました。
いくつか調べて試してみたのですが、結局一番便利で安定しているのは "UI Automation PowerShell Extensions" によるバッチ処理の実現でした。寡聞にして知らなかったのですが、"PowerShell" は近年 Windows OS に実装されたシェル環境です。Unix ユーザーにとっては、これまでの MS-DOS コマンドプロンプトより格段に使いやすいです。

"UI Automation" については、此の辺を参考にしてください。
(前者のページは近々に閉じてしまう模様です。)

uiautomation.codeplex.com
github.com


あれこれと解説していても仕方ないので、今回作成した『半自動 OCRスクリプトを公開します。
工夫せざるを得なかったのは、ID を参照してのマウス・アクションのシミュレーションが意外と難しかったので、キー・アクションで望みの処理フローを実装したところです。あと、自炊 PDF によって数百ページとか数ページとかページ数の多寡が違いすぎるので、OCR 処理各工程の時間が読めず、しかたなく Start-Sleep でキー入力待ちにして、人間が次の処理の実行を判断するようにしました。


まだ多少不便ですが、かなりの改善がなされました。
この PowerShell の活用による作業プロセス自動化は今後も大いに役に立つことは間違いないですね。

# Import-Module "C:\Users\renpoo\UIAutomation\UIAutomation.dll"


Clear-Host;
$Path = "~\Downloads\";
cd $Path;

ForEach ($file in Get-ChildItem $Path -Name)
{ If ($file.Contains(".pdf"))
  {
    $file.fullname

    Start-Process e_Typist -PassThru;

    Start-Sleep -s 20;

    $process = Get-UiaWindow -Class 'eTypistClass';

    Start-Sleep -s 3;

    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_F)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_F)
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU);

    Start-Sleep -s 3;

    $process.Keyboard.TypeText( $file );

    Start-Sleep -s 3;

    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::TAB)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::TAB)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN);

    Start-Sleep -s 5;

    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN);

    Start-Sleep -s 3;

    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_L)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A)
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU);

    Start-Sleep;

    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_R)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_L)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A)
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU);

    Start-Sleep;

    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_R)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_M)
    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::SHIFT)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F6)
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::SHIFT)
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU);
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN)

    Start-Sleep;

    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_L)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A)
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU);

    $newfile = $file.replace( ".pdf", "*.pdf" );

    Start-Sleep -s 3;

    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::TAB)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN);

    Start-Sleep -s 5;

    $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_F)
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_X)
    $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU);
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN);

    $process.Keyboard.TypeText( $newfile );
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN);
    $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN);

    Start-Sleep -s 10;

    #break
  }
}