Tesseract OCR / pytesseract メモ

Tesseract OCR / pytesseract メモ

Takahiro Iwasa
(岩佐 孝浩)
Takahiro Iwasa (岩佐 孝浩)
30 min read
Compute Vision Python

Tesseract OCR v4 / pytesseract を使用して日本語 PDF の OCR を行いました。サンプル PDF として、パブリックドメインの太宰治「走れメロス!」を使用しました。

この投稿のサンプルは、 GitHub リポジトリから取得できます。

Python

PDF は以下の手順で処理されます。

  1. pdf2image ライブラリを使用して、 PDF を PNG に変換
  2. Tesseract OCR / pytesseract を使用して変換されたイメージから文字を抽出
  3. 結果をテキストファイルに保存
import re
from datetime import datetime

import pdf2image
import pytesseract


def to_images(pdf_path: str, first_page: int = None, last_page: int = None) -> list:
    """ Convert a PDF to a PNG image.

    Args:
        pdf_path (str): PDF path
        first_page (int): First page starting 1 to be converted
        last_page (int): Last page to be converted

    Returns:
        list: List of image data
    """

    print(f'Convert a PDF ({pdf_path}) to a png...')
    images = pdf2image.convert_from_path(
        pdf_path=pdf_path,
        fmt='png',
        first_page=first_page,
        last_page=last_page,
    )
    print(f'A total of converted png images is {len(images)}.')
    return images


def to_string(image) -> str:
    """ OCR an image data.

    Args:
        image: Image data

    Returns:
        str: OCR processed characters
    """

    print(f'Extract characters from an image...')
    return pytesseract.image_to_string(image, lang='jpn')


def normalize(target: str) -> str:
    """ Normalize result text.

    Applying the following:
    - Remove newlines.
    - Remove spaces between Japanese characters.

    Args:
        target (str): Target text to be normalized

    Returns:
        str: Normalized text
    """

    result = re.sub('\n', '', target)
    result = re.sub('([あ-んア-ン一-鿐])\s+((?=[あ-んア-ン一-鿐]))', r'\1\2', result)
    return result


def save(result: str) -> str:
    """ Save the result text in a text file.

    Args:
        result (str): Result text

    Returns:
        str: Text file path
    """

    path = 'result.txt'
    with open(path, 'w') as f:
        f.write(result)
    return path


def main() -> None:
    start = datetime.now()
    result = ''

    images = to_images('run-melos.pdf', 1, 2)
    for image in images:
        result += to_string(image)
    result = normalize(result)
    path = save(result)

    end = datetime.now()
    duration = end.timestamp() - start.timestamp()

    print('----------------------------------------')
    print(f'Start: {start}')
    print(f'End: {end}')
    print(f'Duration: {int(duration)} seconds')
    print(f'Result file path: {path}')
    print('----------------------------------------')


if __name__ == '__main__':
    main()

結果

原文

メロスは激怒した。必ず、かの邪智暴虐(じやちぼうぎやく)の王を除かなければならぬと決意した。メロスには政治がわからぬ。メロスは、村の牧人である。笛を吹き、羊と遊んで暮して來た。けれども邪惡に對しては、人一倍に敏感であつた。けふ未明メロスは村を出發し、野を越え山越え、十里はなれた此のシラクスの市にやつて來た。メロスには父も、母も無い。女房も無い。十六の、内氣な妹と二人暮しだ。この妹は、村の或る律氣(りちぎ)な一牧人を、近々、花婿として迎へる事になつてゐた。結婚式も間近かなのである。メロスは、それゆゑ、花嫁の衣裳(いしやう)やら祝宴の御馳走(ごちそう)やらを買ひに、はるばる市にやつて來たのだ。先(ま)づ、その品々を買ひ集め、それから都の大路をぶらぶら歩いた。メロスには竹馬(ちくば)の友があつた。セリヌンテイウスである。今は此のシラクスの市で、石工をしてゐる。その友を、これから訪ねてみるつもりなのだ。久しく逢(あ)はなかつたのだから、訪ねて行くのが樂しみである。歩いてゐるうちにメロスは、まちの樣子を怪しく思つた。ひつそりしてゐる。もう既に日も落ちて、まちの暗いのは當りまへだが、けれども、なんだか、夜のせゐばかりでは無く、市全體が、やけに寂しい。のんきなメロスも、だんだん不安になつて來た。路(みち)で逢つた若い衆をつかまへて、何かあつたのか、二年まへに此の市に來たときは、夜でも皆が歌をうたつて、まちは賑(にぎ)やかであつた筈(はず)だが、と質問した。若い衆は、首を振つて答へなかつた。しばらく歩いて老爺(らうや)に逢ひ、こんどはもつと、語勢を強くして質問した。老爺は答へなかつた。メロスは兩手で老爺のからだをゆすぶつて質問を重ねた。老爺は、あたりをはばかる低聲で、わづか答へた。「王樣は、人を殺します。」「なぜ殺すのだ。」「惡心を抱いてゐる、といふのですが、誰もそんな、惡心を持つては居りませぬ。」「たくさんの人を殺したのか。」「はい、はじめは王樣の妹婿さまを。それから、御自身のお世嗣(よつぎ)を。それから、妹さまを。それから、妹さまの御子さまを。それから、皇后さまを。それから、賢臣のアレキス樣を。」「おどろいた。國王は亂心か。」「いいえ、亂心ではございませぬ。人を、信ずる事が出來ぬ、といふのです。このごろは、臣下の心をも、お疑ひになり、少しく派手な暮しをしてゐる者には、人質ひとりづつ差し出すことを命じて居ります。御命令を拒めば十字架にかけられて、殺されます。けふは、六人殺されました。」 聞いて、メロスは激怒した。「呆(あき)れた王だ。生かして置けぬ。」 メロスは、單純な男であつた。買ひ物を、背負つたままで、のそのそ王城にはひつて行つた。たちまち彼は、巡邏(じゆんら)の警吏に捕縛された。調べられて、メロスの懷中からは短劍が出て來たので、騷ぎが大きくなつてしまつた。メロスは、王の前に引き出された。「この短刀で何をするつもりであつたか。言へ!」暴君デイオニスは靜かに、けれども威嚴を以て問ひつめた。その王の顏は蒼白(さうはく)で、眉間(みけん)の皺(しわ)は、刻み込まれたやうに深かつた。「市を暴君の手から救ふのだ。」とメロスは惡びれずに答へた。「おまへがか?」王は、憫笑(びんせう)した。「仕方の無いやつぢや。おまへには、わしの孤獨がわからぬ。」「言ふな!」とメロスは、いきり立つて反駁(はんばく)した。「人の心を疑ふのは、最も恥づべき惡徳だ。王は、民の忠誠をさへ疑つて居られる。」「疑ふのが、正當の心構へなのだと、わしに教へてくれたのは、おまへたちだ。人の心は、あてにならない。人間は、もともと私慾のかたまりさ。信じては、ならぬ。」暴君は落着いて呟(つぶや)き、ほつと溜息をついた。「わしだつて、平和を望んでゐるのだが。」「なんの爲の平和だ。自分の地位を守る爲か。」こんどはメロスが嘲笑(てうせう)した。「罪の無い人を殺して、何が平和だ。」「だまれ、下賤(げせん)の者。」王は、さつと顏を擧げて報いた。「口では、どんな清らかな事でも言へる。わしには、人の腹綿の奧底が見え透いてならぬ。おまへだつて、いまに、磔(はりつけ)になつてから、泣いて詫(わ)びたつて聞かぬぞ。」「ああ、王は悧巧(りかう)だ。自惚(うぬぼ)れてゐるがよい。私は、ちやんと死ぬる覺悟で居るのに。命乞(いのちご)ひなど決してしない。ただ、--」と言ひかけて、メロスは足もとに視線を落し瞬時ためらひ、「ただ、私に情をかけたいつもりなら、處刑までに三日間の日限を與へて下さい。たつた一人の妹に、亭主を持たせてやりたいのです。三日のうちに、私は村で結婚式を擧げさせ、必ず、ここへ歸つて來ます。」「ばかな。」と暴君は、嗄(しはが)れた聲で低く笑つた。「とんでもない嘘(うそ)を言ふわい。逃がした小鳥が歸つて來るといふのか。」「さうです。歸つて來るのです。」メロスは必死で言ひ張つた。「私は約束を守ります。私を、三日間だけ許して下さい。妹が、私の歸りを待つてゐるのだ。そんなに私を信じられないならば、よろしい、この市にセリヌンテイウスといふ石工がゐます。私の無二の友人だ。あれを、人質としてここに置いて行かう。私が逃げてしまつて、三日目の日暮まで、ここに歸つて來なかつたら、あの友人を絞め殺して下さい。たのむ。さうして下さい。」 それを聞いて王は、殘虐な氣持で、そつと北叟笑(ほくそゑ)んだ。生意氣なことを言ふわい。どうせ歸つて來ないにきまつてゐる。この嘘つきに騙(だま)された振りして、放してやるのも面白い。さうして身代りの男を、三日目に殺してやるのも氣味がいい。人は、これだから信じられぬと、わしは悲しい顏して、その身代りの男を磔刑(たくけい)に處してやるのだ。世の中の、正直者とかいふ奴輩(やつばら)にうんと見せつけてやりたいものさ。「願ひを、聞いた。その身代りを呼ぶがよい。三目日には日沒までに歸つて來い。おくれたら、その身代りを、きつと殺すぞ。ちよつとおくれて來るがいい。おまへの罪は、永遠にゆるしてやらうぞ。」「なに、何をおつしやる。」「はは。いのちが大事だつたら、おくれて來い。おまへの心は、わかつてゐるぞ。」 メロスは口惜しく、地團駄踏(ぢだんだふ)んだ。ものも言ひたくなくなつた。 竹馬の友、セリヌンテイウスは、深夜、王城に召された。暴君デイオニスの面前で、佳(よ)き友と佳き友は、二年ぶりで相逢うた。メロスは、友に一切の事情を語つた。セリヌンテイウスは無言で首肯(うなづ)き、メロスをひしと抱きしめた。友と友の間は、それでよかつた。セリヌンテイウスは、繩打たれた。メロスは、すぐに出發した。初夏、滿天の星である。 メロスはその夜、一睡もせず十里の路を急ぎに急いで、村へ到着したのは、翌(あく)る日の午前、陽は既に高く昇つて、村人たちは野に出て仕事をはじめてゐた。メロスの十六の妹も、けふは兄の代りに羊群の番をしてゐた。よろめいて歩いて來る兄の、疲勞困憊(ひらうこんぱい)の姿を見つけて驚いた。さうして、うるさく兄に質問を浴びせた。「なんでも無い。」メロスは無理に笑はうと努めた。「市に用事を殘して來た。またすぐ市に行かなければならぬ。あす、おまへの結婚式を擧げる。早いはうがよからう。」 妹は頬(ほほ)をあからめた。「うれしいか。綺麗(きれい)な衣裳(いしやう)も買つて來た。さあ、これから行つて、村の人たちに知らせて來い。結婚式は、あすだと。」 メロスは、また、よろよろと歩き出し、家へ歸つて神々の祭壇を飾り、祝宴の席を調へ、間もなく床に倒れ伏し、呼吸もせぬくらゐの深い眠りに落ちてしまつた。 眼が覺めたのは夜だつた。メロスは起きてすぐ、花婿の家を訪れた。さうして、少し事情があるから、結婚式を明日にしてくれ、と頼んだ。婿の牧人は驚き、それはいけない、こちらには未だ何の仕度も出來てゐない、葡萄(ぶだう)の季節まで待つてくれ、と答へた。メロスは、待つことは出來ぬ、どうか明日にしてくれ

OCR 処理結果

メロスは激怒した。必ず、かの邪智暴上(じゃちぼうぎやく)の王を除かなければならぬと決意した。メロスには政治がわからぬ。メロスは、村の牧人である。笛を吹き、羊と遊んで暮して來たを。けれども那章に半しては、人一倍に敏感であつた。けょ未明メロスは村を出紙し、野を越え山越え、十里はなれた此のシラクスの市にやつて來た。メロスには父も、母も無い。女房も無い。十六の、内気な妹と二人暮しだ。この妹は、村の或る律舞(りちぎ)な一牧人を、近々、花婚として迎へる事になつてゐた。結婚式も間近かなのである。メロスは、それゆる、和花嫁の衣裳(いしゃう)やら祝宴の御馳走(ごちをそう)やらを買ひに、はるばる市にやつて來たのだ。先(ま)づ、その品々を買ひ集め、それから都の大路をぶらぶら歩いた。メロスには竹馬(ちくば)の友があつた。セリヌンテイウスである。 今は此Mのシラクスの市で、石工をしてゐる。その友を、これから訪ねてみるつもりなのだ。 久しく人逢(ぁ)はなかつたのだから、訪ねて行くのが楽しみである。歩いてゐるうちにメロスは、まちの様子を怪しく思つた。ひつそりしてゐる。 もう既に日も落ちて、まちの暗いのは常りまへだが、けれども、なんだか、夜のせるばかりでは無く、市全骨が、やけに外しい。のんきなメロスも、だんだん不安になつて來た。路(みち)で逢つた若い衆をつかまへて、何かあつたのか、二年まへに此の市に來たときは、夜でも皆が歌をうたつて、まちは賑(にぎ)やかであつた管(はず)だが、と質問した。若い衆は、首を振つて答へなかつた。しばらく歩いて老爺(らうゃ)に逢ひ、こんどはもつと、語勢を強くして質問した。老爺は答へなかつた。メロスは了規手で老爺のからだをゆすぶつて質問を重ねた。老谷は、あたりをはばかる低衣で、わづか答へた。「王様は、人を殺します。」「なぜ殺すのだ。」「悪心を抱いてゐる、といふのですが、誰もそんな、悪心を持つては居りませめぬ。」「たくさんの人を殺したのか。」「はい、はじめは王様の妹婚さまを。それから、御自身のお世識(よっぎ)を。それから、妹さまを。それから、妹さまの御子きまを。それから、和皇后さまを。それから、賢到のアレキス様を。」「おどろいた。國王は負心か。」「いいえ、角心ではございませぬ。人を、信ずる事が出来ぬ、といふのです。このごろは、臣下の心をも、お疑ひになり、少しく派手な暮しをしてゐる者には、人質ひとりづつ差し出すことを命じて居ります。御命令を拒めば十字架にかけられて、殺されます。けふは、六人殺されました。 」聞いて、メロスは激怒した。 「果(ぁき)れた王だ。生かして置けめ。」メロスは、晶純な男であつた。買ひ物を、背負つたままで、のそのるそ王城にはひつて行つた。たちまと彼は、巡漏(じゅんら)の警更に捕縛された。調べられて、メロスの居中からは短剣が出て來たので、騒ぎが大きくなつてしまつた。メロスは、王の前に引き出された。「この短刀で何をするつもりであつたか。言へ ! 」暴君デイオニスは静かに、けれども威滅を以て問ひつめた。その王の顔は蒼白(さうはく)で、眉間(みけん)の鐘(しゎ)は、刻み込まれたやうに深かつた。「市を暴君の手から救ふみのだ。」とメロスは悪びれずに答へた。「おまへがか ? 」王は、翼笑(びんをせうぅう)した。 「仕方の無いやつぢや。おまへには、わしの孤狼がわからぬ。」「言ふな! 」とメロスは、いきり立つて反須(はんばく)した。「人の心を疑ふのは、最も恥づべき悪徳だ。ヨは、民の忠誠をさへ疑つて居られる。」「疑ふのが、正常の心構へなのだと、わしに教へてくれたのは、おまへたちだ。人の心は、あてにならなはい。 人間は、もともと私人徐のかたまりさ。信じては、ならぬ。」暴君は落着いて咳(つぶゃや)き、ほつと潤息をついた。「わしだつて、平和を望んでるるのだが。」「なんの久の平和だ。自分の地位を守る師か。」こんどはメロスが喘笑(てうせう)した。 「罪の無い人を殺して、何が平和だ。」「だまれ、下剛(げせん)の者。」王は、さつと顔を撃げて報いた。「品では、どんな清らかな事でも言へる。わしには、人の腹綿の奥底が見え透いてならぬ。おまへだつて、いまに、礎(はりっけ)になつてから、泣いて諸(わ)びたつて聞かぬぞ。」「ああ、王は便巧(りかうぅ)だ。自惚(うぬぼ)れてゐるがよい。 私は、ちやんと死ぬる算悟で居るのに。命婦(ぃのちご)ひなど決してしない。ただ、一一」 と言ひかけて、メロスは足もとに視線を落し瞬時ためらひ、「ただ、私に情をかけたいつもりなら、雇刑までに三日間の日限を異へて下さい。たつた一人の妹に、亭主を持たせてやりたいのです。三日のうちに、私は村で結婚式を軸げさせ、必ず、ここへ包つて來ます。 」「ばかな。」と暴君は、喝(しはが)れた英で低く笑つた。「とんでもない嘘(うそ)を言ふわい。 逃がした小/夕つて來るといふのか。 」「さうです。旋つて來るのです。」メロスは必死で言ひ張つた。「私は約束を守ります。私を、三日間だけ許して下さい。妹が、私の過りを待つてゐるのだ。そんなに私を信じられないならば、よろしい、この市にセリヌンテイウスといふ石工がゐます。私の無二の友人だ。あれを、人質としてここに置いて行かう。私が逃げてしまつて、三日目の日暮まで、ここに騙つて来なかつたら、あの友人を絞め殺して下さい。たのむ。さうして下さい。 」それを聞いて王は、残虐な気持で、そつと北明笑(ほくそる)んだ。生意気なことを言ふわい。どうせ訪つて挫ないにきまつてゐる。この嘘つきに騙(だま)された振りして、放してやるのも面白い。さうして身代りの男を、三日目に殺してやるのも無味がいい。 人は、これだから信じられぬと、わしは悲しい顔して、その身代りの男を秦刑(たくけぃ)に虎してやるのだ。世の中の、正直者とかいふ奴募(やっぱら)にうんと見せつけてやりたいものきさ。「願ひを、聞いた。その身代りを呼ぶがよい。三目日には日浸までに上包つて來い。おくれたら、その身代りを、きつと殺すぞ。ちょつとおくれて來るがいい。おまへの罪は、永遠にゆるしてやらうぞ。」「なに、何をおつしやる。」「はは。いのちが大事だつたら、おくれて來い。おまへの心は、わかつてゐるそぞ。 」メロスは口惜しく、地圏駄踏(なおだんだぉぁ)んだ。ものも言ひたくなくなつた。竹馬の友、セリヌンテイウスは、深夜、王城に召された。 暴君デイオニスの面前で、佳(ょ)き友と佳き友は、二年ぶりで相逢うた。メロスは、友に一切の事情を語つた。セリヌンテイウスは無言で首衣(うなづ)き、メロスをひしと抱きしめた。友と友の間は、それでよかつた。セリヌンテイウスは、細打たれた。メロスは、すぐに出獲した。 初夏、満天の星である。メロスはその夜、一睡もせず十里の路を急ぎに急いで、村へ到着したのは、翌(ぁく)る日の午前、陽は既に高く、昇つて、村人たちは野に出て仕事をはじめてゐた。メロスの十六の妹も、けふは見の代りに壮群の番をしてゐた。よろめいて歩いて來る兄の、疲替困付(ひらうこんばぱい)の姿を見つけて驚いた。さうして、うるさく見に質問を浴びせた。「なんでも無い。、」メロスは無理に括はうと努めた。 「市に用事を残して來た。またすでぐ市に行かなければならぬ。あす、おまへの結婚式を撃げる。早いはうがよからう。 」妹は類(ほほ)をあからめた。「うれしいか。 綺麗(きれぃ)な衣裳(いしゃう)も買つて來たを。さあ、これから行つて、村の人たちに知らせて來い。結婚式は、あすだと。」メロスは、また、よろよろと歩き出し、家へ由つて神々の祭壇を飾り、祝宴の席を調べへ、間もなく床に倒れ伏し、呼吸もせぬくらゐの深い眠りに落ちてしまつた。眼が算めたのは夜だつた。メロスは起きてすぐ、花婚の家を訪れた。さうして、少し事情があるから、結婚式を明日にしてくれ、と頼んだ。婚の牧人は息き、それはいけない、こちらには未だ何の仕度も出來てゐない、和攻略(ぶだうぅ)の季節まで待つてくれ、と答へた。メロスは、待つことは出來ぬ、どうか明日にしてくれが旬

差分

Python スクリプトが改行を削除しているため、 diff は比較に適していないかもしれません。代わりに、 Araxis Merge, WinMerge などの GUI diff ツールを使用することをお勧めします。

まとめ

特定のケースにおいては有用で、 Tesseract OCR の精度は時間とともに大幅に向上し続けると思います。ただし、 PDF に図や表などの複雑なレイアウトが含まれている場合、精度が低下する可能性があります。

Takahiro Iwasa
(岩佐 孝浩)

Takahiro Iwasa (岩佐 孝浩)

Software Developer at iret, Inc.
主に AWS を利用したクラウドネイティブアプリケーションの設計および開発をしています。 Japan AWS Top Engineers 2020-2023