NDLOCRの使い方【バッチ化してより使いやすくしました】

はじめに
このようなブログを書いていると、史料の文字をテキストファイルに起こして調べることが多いのですが、画像ファイルの文字列を目で見てキーボード叩いて、と、人の手で作業するのはミスの原因であり、そもそも非効率です。
そこで出会ったのが、国立国会図書館が公開しているOCRライブラリでした。これは、国立国会図書館が史料の文字列をテキストデータ化するために開発されたもので、高性能且つ無料で公開されているのが最大の特徴です。
セットアップは、Quiitaにて投稿されていた、yanosen_jp様の記事を見て作業しましたので、そちらを見ていただけたらと思います。
さて、今回この記事で紹介するのは、テキストデータ化処理をワンクリックで動かそうというものです。
上に挙げた記事に掲載されていたタスクフローをコマンドプロンプトでバッチ化し、可能な限りユーザー操作を減らし、フォルダ管理もしやすいようにしました。
また、画像ファイル名を一括で採番するバッチも作ったので、合わせて皆さまの業務効率の改善に貢献できればと思います。
セットアップの注意点
前提として、PCの環境に、NVIDIAのドライバーが必要です。私は、これを見落としていて、セットアップ時のエラー調査に無駄に時間を浪費してしまいました(笑)。グラフィックボード搭載のPCは少々高価ですが、悪しからず。
Dockerコンテナのビルド
yanosen_jp様の記事にて1点補足です。
私用のPCでは、項目「Dockerコンテナのビルド」にて、エラーが発生したので、以下のようにコードを修正しました。
原因は、OS更新やpip仕様変更などの環境差で、ビルドロジックが壊れていました。この現象の再現は、個人差あると思います。一応、現象として掲載しておきます。
\ndlocr_cli\docker\Dockerfile
上記ファイルの21行目

バッチのコード
ワードプレスでは、どうやらバッチファイルは掲載することができないらしいので、コードを生身で配置しました。テキストファイルに以下のコードを貼り付けて、拡張子を「.bat」に書き換えて保存してください。
①SJISで保存してください!UTFでは、文字化けします。
②Dockerアプリケーションは起動させた状態で実行してください。
テキストデータ化.bat
@echo off
setlocal
goto mainREM =======================================================================================================
REM メッセージ一覧(クラス Message 相当)。{0} は :msg の引数で置換
REM =======================================================================================================
:initMessages
set “Message.C0001=”
set “Message.C0002=Docker が起動していません。Docker Desktop を起動してから再実行してください。”
set “Message.C0003=フォルダパスが入力されていません。”
set “Message.C0004=入力されたフォルダは存在しません。フルパスを再入力してください。”
set “Message.C0005=フォルダの存在を確認しました: {0}”
set “Message.C0006=このまま作業しますか? (Y/N)”
set “Message.C0007=作業対象のフォルダパス(フルパス)を入力してください:”
set “Message.C0008=jpg・png以外の拡張子が存在します。確認してください。”
set “Message.C0009=対象外: {0}”
set “Message.C0010=画像ファイル(jpg・png)が見つかりません。確認してください。”
set “Message.C0011=画像ファイル: {0} 枚”
set “Message.C0012=退避コピーに失敗しました。処理を中止します。”
set “Message.C0013=退避_{0} にコピーしました。”
set “Message.C0014=コンテナの起動に失敗しました。処理を中止します。”
set “Message.C0015=OCR実行に失敗しました。処理を中止します。”
set “Message.C0016=結果のコピーに失敗しました。処理を中止します。”
set “Message.C0017=OCR結果のフォルダが見つかりません。処理を中止します。”
set “Message.C0018==== 全処理完了 ===”
set “Message.C0019=終了するには何かキーを押してください…”
exit /b 0REM メッセージ表示
:msg
setlocal enabledelayedexpansion
set “m=!Message.%~1!”
if not “%~2″==”” set “m=!m:{0}=%~2!”
echo(!m!
endlocal
exit /b 0REM Y/N選択 Y=0 / N=1 を返す
:askYN
choice /c YN
if errorlevel 2 exit /b 1
exit /b 0
REM =======================================================================================================
REM 事前確認
REM =======================================================================================================REM Docker Desktop が起動しているか確認
:checkDocker
docker info >nul 2>&1
if errorlevel 1 (
call :msg C0001
call :msg C0002
exit /b 1
)
exit /b 0
REM =======================================================================================================
REM 作業対象フォルダの入力(存在しなければ再入力、Nなら再入力)
REM =======================================================================================================
:inputWorkFolder
call :msg C0001
set “checkFolder=”
set /p “checkFolder=%Message.C0007%”
if not defined checkFolder (
call :msg C0003
goto inputWorkFolder
)REM ダブルクォートを除去し、再度空チェック
set “checkFolder=%checkFolder:”=%”
if not defined checkFolder (
call :msg C0003
goto inputWorkFolder
)REM 末尾の \ を除去
if “%checkFolder:~-1%”==”\” set “checkFolder=%checkFolder:~0,-1%”if not exist “%checkFolder%\” (
call :msg C0001
call :msg C0004
goto inputWorkFolder
)call :msg C0001
call :msg C0005 “%checkFolder%”
call :msg C0006
call :askYN
if errorlevel 1 goto inputWorkFolder
exit /b 0
REM =======================================================================================================
REM 画像ファイルの確認(作業フォルダ直下のみ。jpg/pngのみ許可)
REM =======================================================================================================
:checkImages
set /a imgCount=0
set /a otherCount=0
for %%f in (“%checkFolder%\*”) do call :checkFile “%%~xf” “%%~nxf”if %otherCount% GTR 0 (
call :msg C0001
call :msg C0008
exit /b 1
)
if %imgCount% EQU 0 (
call :msg C0001
call :msg C0010
exit /b 1
)
call :msg C0001
call :msg C0011 %imgCount%
exit /b 0REM 画像ファイル判定(%1=拡張子, %2=ファイル名)
:checkFile
if /i “%~1″==”.jpg” goto checkFile_img
if /i “%~1″==”.png” goto checkFile_img
set /a otherCount+=1
call :msg C0009 “%~2”
exit /b 0
:checkFile_img
set /a imgCount+=1
exit /b 0
REM =======================================================================================================
REM 退避フォルダを作成し、作業フォルダの中身をコピー(元ファイルは残す)
REM =======================================================================================================
:createBackup
set /a seq=1
:createBackup_next
if exist “%checkFolder%\退避_%seq%” (
set /a seq+=1
goto createBackup_next
)
set “backupFolder=%checkFolder%\退避_%seq%”
mkdir “%backupFolder%”REM 退避_* は対象外(退避フォルダ自身を含めない)
robocopy “%checkFolder%” “%backupFolder%” /E /R:2 /W:2 /XD “退避_*” >nul
if %errorlevel% GEQ 8 (
call :msg C0001
call :msg C0012
exit /b 1
)
call :msg C0001
call :msg C0013 %seq%
exit /b 0
REM =======================================================================================================
REM OCR入力用の準備(前回分は退避済みなので作り直す)
REM =======================================================================================================
:prepareWorkDirs
set “imgDir=%checkFolder%\.images”
if exist “%imgDir%” rmdir /S /Q “%imgDir%”
if exist “%checkFolder%\getDocker” rmdir /S /Q “%checkFolder%\getDocker”
if exist “%checkFolder%\getMain” rmdir /S /Q “%checkFolder%\getMain”
mkdir “%imgDir%”
copy /Y “%checkFolder%\*.jpg” “%imgDir%\” >nul 2>&1
copy /Y “%checkFolder%\*.png” “%imgDir%\” >nul 2>&1
exit /b 0
REM =======================================================================================================
REM OCR(Docker)
REM =======================================================================================================REM コンテナ起動
:startContainer
docker stop ocr_cli_runner >nul 2>&1
docker run –shm-size=50g –gpus all -d –rm –name ocr_cli_runner -v “%imgDir%:/root/tmpdir/img” -i ocr-v2-cli-py38:latest
if errorlevel 1 (
call :msg C0001
call :msg C0014
exit /b 1
)
exit /b 0REM OCR実行
:runOcr
docker exec ocr_cli_runner sh -c “rm -rf /root/tmpdir/output && python3 main.py infer /root/tmpdir /root/tmpdir/output -s s”
if errorlevel 1 (
call :msg C0001
call :msg C0015
call :dockerStop
exit /b 1
)
exit /b 0REM OCR結果をホストへコピーし、コンテナを止める
:copyResult
docker cp ocr_cli_runner:/root/tmpdir/output “%checkFolder%\getDocker”
if errorlevel 1 (
call :msg C0001
call :msg C0016
call :dockerStop
exit /b 1
)
call :dockerStop
exit /b 0:dockerStop
docker stop ocr_cli_runner >nul 2>&1
exit /b 0
REM =======================================================================================================
REM 後処理
REM =======================================================================================================REM テキスト抽出&整理
:extractText
cd /d “%checkFolder%\getDocker\tmpdir\txt”
if errorlevel 1 (
call :msg C0001
call :msg C0017
exit /b 1
)
mkdir “%checkFolder%\getMain”
for %%f in (*_main*) do xcopy “%%f” “%checkFolder%\getMain” /Y
exit /b 0REM 内容をひとつのテキストファイルに結合
:mergeText
cd /d “%checkFolder%\getMain”
copy *.txt 結合後_main.txt
call :returnUsers
exit /b 0REM OCR入力用の .images を削除
:cleanup
rmdir /S /Q “%imgDir%”
exit /b 0REM Usersに戻る
:returnUsers
cd /d “%USERPROFILE%”
exit /b 0
REM =======================================================================================================
REM 処理本体
REM call :xxx || goto abort … xxx が失敗(exit /b 1)したら abort へ
REM =======================================================================================================
:maincall :initMessages
REM — 事前確認 —
call :checkDocker || goto abortREM — 作業対象フォルダの入力と画像確認(NGなら入力に戻る) —
:mainInputFolder
call :inputWorkFolder
call :checkImages || goto mainInputFolderREM — 退避・準備 —
call :createBackup || goto abort
call :prepareWorkDirsREM — OCR —
call :startContainer || goto abort
call :runOcr || goto abort
call :copyResult || goto abortREM — 後処理 —
call :extractText || goto abort
call :mergeText
call :cleanupcall :msg C0001
call :msg C0018
call :msg C0019
pause >nul
exit /b 0:abort
pause
exit /b 1
採番.bat
@echo off
setlocal EnableDelayedExpansionREM =======================================================================================================
REM 画像 退避&連番リネーム
REM ・メッセージは :defineMessages にまとめて定義({0}{1}{2} は引数で置換)
REM ・処理は各ラベル(サブルーチン)に分割して call で呼び出す
REM =======================================================================================================call :defineConstants
call :defineMessagesREM =======================================================================================================
REM 処理本体
REM =======================================================================================================
:main
call :inputFolder
call :selectFolder
if “!SEL_RESULT!”==”retry” goto mainecho.
call :echoMsg C0014 “!processFolder!” !imageCount!
call :askYN C0015
if “!YN!”==”N” goto maincall :inputStartNumber
echo.
call :echoMsg C0019
call :runBackup
call :runRenameecho.
call :echoMsg C0024
pause
exit /b 0
REM =======================================================================================================
REM 定数
REM =======================================================================================================
:defineConstants
set “EXTS=bmp gif jpg jpeg png”
set “BK_NAME=退避_”
set “TMP_PREFIX=tmp_”
exit /b 0
REM =======================================================================================================
REM メッセージ定義
REM =======================================================================================================
:defineMessages
set “M_C0001=処理対象のフォルダのフルパスを入力してください: “
set “M_C0002=フォルダパスが入力されていません。”
set “M_C0003=接続成功!フォルダにアクセスできました。”
set “M_C0004=接続できません。パスや権限を確認してください。”
set “M_C0005=フォルダにアクセスできません。オンライン専用か権限不足の可能性があります。”
set “M_C0006=フォルダアクセス確認完了”
set “M_C0007=どのフォルダを処理しますか?画像ファイル数を確認してください。”
set “M_C0008=0: フォルダパスを再入力する”
set “M_C0009={0}: {1} (フォルダなし)”
set “M_C0010={0}: {1} ({2} 枚)”
set “M_C0011=選択してください (0/1/2): “
set “M_C0012=選択できません。”
set “M_C0013=入力が正しくありません。”
set “M_C0014=選択したフォルダ: {0} ({1} 枚)”
set “M_C0015=実行してよろしいですか?”
set “M_C0016=連番の開始番号を入力してください(整数): “
set “M_C0017=0以上の整数を入力してください。”
set “M_C0018={0} から採番を開始します。よろしいですか?”
set “M_C0019=処理を開始します・・・”
set “M_C0020=退避完了({0} 件)”
set “M_C0021=開始番号 {0}”
set “M_C0022=終了番号 {0}”
set “M_C0023=連番付与完了”
set “M_C0024==== 全処理完了 ===”
exit /b 0
REM =======================================================================================================
REM 共通:メッセージ処理
REM =======================================================================================================REM メッセージ取得 call :getMsg ID [引数0] [引数1] [引数2] → MSG
:getMsg
set “MSG=!M_%~1!”
set “MSG=!MSG:{0}=%~2!”
set “MSG=!MSG:{1}=%~3!”
set “MSG=!MSG:{2}=%~4!”
exit /b 0REM メッセージ表示 call :echoMsg ID [引数…]
:echoMsg
call :getMsg %*
echo(!MSG!
exit /b 0REM 入力受付 call :prompt 変数名 ID (何も入力しなかった場合は変数が未定義になる)
:prompt
set “%~1=”
call :getMsg %~2
set /p “%~1=!MSG!”
exit /b 0REM Y/N確認 call :askYN ID [引数…] → YN に Y か N
:askYN
call :getMsg %*
choice /c YN /m “!MSG!”
if errorlevel 2 (set “YN=N”) else (set “YN=Y”)
exit /b 0
REM =======================================================================================================
REM 1. フルパスで対象フォルダを入力(OneDrive対応の接続確認つき)
REM =======================================================================================================
:inputFolder
echo.
call :prompt checkFolder C0001if not defined checkFolder (
call :echoMsg C0002
goto inputFolder
)REM ドラッグ&ドロップ等で付く二重引用符を除去
set “checkFolder=!checkFolder:”=!”if not exist “!checkFolder!\” (
call :echoMsg C0004
goto inputFolder
)
call :echoMsg C0003dir “!checkFolder!\*” >nul 2>&1
if errorlevel 1 (
call :echoMsg C0005
goto inputFolder
)echo.
call :echoMsg C0006
exit /b 0
REM =======================================================================================================
REM 2. 画像カウント call :countImages フォルダ → count(フォルダなしは -1)
REM =======================================================================================================
:countImages
setlocal DisableDelayedExpansion
if not exist “%~1\” (
endlocal & set “count=-1”
exit /b 0
)
set count=0
for %%x in (%EXTS%) do (
for /f %%i in (‘dir /b /a-d “%~1\*.%%x” 2^>nul ^| find /c /v “”‘) do set /a count+=%%i
)
endlocal & set “count=%count%”
exit /b 0
REM =======================================================================================================
REM 3. 処理対象フォルダの選択 → processFolder / imageCount / SEL_RESULT(retry)
REM =======================================================================================================
:selectFolder
set “SEL_RESULT=”
set “processFolder=”
set “CAND_1=!checkFolder!”
set “CAND_2=!checkFolder!\.images”for %%i in (1 2) do (
call :countImages “!CAND_%%i!”
set “CNT_%%i=!count!”
)echo.
call :echoMsg C0007
call :echoMsg C0008
for %%i in (1 2) do (
if !CNT_%%i! EQU -1 (
call :echoMsg C0009 %%i “!CAND_%%i!”
) else (
call :echoMsg C0010 %%i “!CAND_%%i!” !CNT_%%i!
)
):selectLoop
call :prompt sel C0011if “!sel!”==”0” (
set “SEL_RESULT=retry”
exit /b 0
)set “valid=”
for %%i in (1 2) do (
if “!sel!”==”%%i” (
set “valid=1”
call :pickCandidate %%i
)
)
if not defined valid (
call :echoMsg C0013
goto selectLoop
)
if not defined processFolder goto selectLoop
exit /b 0REM 候補の確定 call :pickCandidate 番号
:pickCandidate
if !CNT_%1! LSS 1 (
call :echoMsg C0012
exit /b 0
)
set “processFolder=!CAND_%1!”
set “imageCount=!CNT_%1!”
exit /b 0
REM =======================================================================================================
REM 4. 連番の開始番号入力 → countStart
REM =======================================================================================================
:inputStartNumber
echo.
call :prompt countStart C0016set “notNum=”
if not defined countStart set “notNum=1”
for /f “delims=0123456789” %%A in (“!countStart!”) do set “notNum=1”
if defined notNum (
call :echoMsg C0017
goto inputStartNumber
)REM 先頭の0を除去(08 などが8進数扱いになるのを防ぐ)
:trimZero
if “!countStart:~1,1!”==”” goto trimZeroEnd
if “!countStart:~0,1!”==”0” (
set “countStart=!countStart:~1!”
goto trimZero
)
:trimZeroEndcall :askYN C0018 !countStart!
if “!YN!”==”N” goto inputStartNumber
exit /b 0
REM =======================================================================================================
REM 5. 退避処理
REM =======================================================================================================
:runBackup
set “seq=1”
:findNext
if exist “!processFolder!\!BK_NAME!!seq!” (
set /a seq+=1
goto findNext
)
set “backupFolder=!processFolder!\!BK_NAME!!seq!”
mkdir “!backupFolder!”set “backupCount=0”
call :eachImage actBackup “”
call :echoMsg C0020 !backupCount!
exit /b 0
REM =======================================================================================================
REM 6. リネーム処理(いったん tmp_ を付けて衝突を避けてから連番化)
REM =======================================================================================================
:runRename
call :eachImage actToTmp “”set /a counter=countStart
call :eachImage actNumber !TMP_PREFIX!set /a endNum=counter-1
echo.
call :echoMsg C0021 !countStart!
call :echoMsg C0022 !endNum!
call :echoMsg C0023
exit /b 0
REM =======================================================================================================
REM 共通:画像ファイルのループ
REM call :eachImage 処理ラベル 先頭文字列
REM → 対象フォルダの「先頭文字列*.拡張子」を拡張子ごとに名前順で列挙し、
REM 1ファイルごとに call :処理ラベル “ファイル名” を実行する
REM =======================================================================================================
:eachImage
for %%x in (%EXTS%) do (
for /f “delims=” %%f in (‘dir /b /a-d /on “!processFolder!\%~2*.%%x” 2^>nul’) do (
call :%~1 “%%f”
)
)
exit /b 0REM —- 各ファイルに対する処理 —-
REM 退避フォルダへコピー
:actBackup
copy /Y “!processFolder!\%~1” “!backupFolder!\%~1” >nul
set /a backupCount+=1
exit /b 0REM tmp_ を付けてリネーム
:actToTmp
ren “!processFolder!\%~1” “!TMP_PREFIX!%~1”
exit /b 0REM 連番(3桁ゼロ埋め)でリネーム
:actNumber
set “num=000!counter!”
set “num=!num:~-3!”
ren “!processFolder!\%~1” “!num!%~x1”
set /a counter+=1
exit /b 0
「テキストデータ化.bat」の解説
自動でDockerのコンテナを立ち上げ、画像ファイルの読み込みを行います。画面に従えば、問題ないはずです。
処理が完了したら、「getMain」フォルダを見に行きましょう。このフォルダは、テキストデータ化されたファイルが格納されています。
「結合後_main.txt」で、それらを1つにまとめています。

「採番.bat」の解説
できるだけ直感操作を重視した作りにしています。画面に従って作業してください。
強いて言うと、以下の2つの条件だとエラーになる可能性が高いです。
- GoogleDriveなどのクラウド環境
- 画像がHELIC拡張子
なお、採番対象は、画像ファイル(.imgと.png)のみです。
終わりに
OCRによって、作業効率が数十倍、数百倍になりました。なんせ出力されたテキストの制度が高すぎます。手直しする作業が減るだけでなく、テキスト化作業そのものの作業が短縮されたので、管理しやすくなりました。
作っておいたマニュアルも開く必要なければ、エラー対応であちこちタブ移動する必要もなくなりましたからね。退避タスクを挟んでいることによって、戻り作業も発生しません。
良い仕事しました!
| 前の記事へ << | 次の記事へ >> |











