📑

校異源氏物語に対する類似テキスト検索アプリを作成しました。

2025/01/29に公開

概要

校異源氏物語に対する類似テキスト検索アプリを作成しました。以下のURLからお試しいただけます。

https://huggingface.co/spaces/nakamura196/genji_predict

本アプリの使用方法などについて紹介します。

データ

以下の校異源氏物語DBで公開されているテキストデータを使用します。

https://kouigenjimonogatari.github.io/

アプリの内容

仕組みは単純で、校異源氏物語の巻毎・ページ毎のテキストを用意しておき、入力された文字列との編集距離を算出し、類似度が高いテキスト(+巻とページ)を返却します。

ソースコードは以下です。

https://huggingface.co/spaces/nakamura196/genji_predict/tree/main

応用例

例えば、以下の「[源氏物語] [4](東京大学総合図書館所蔵)」では、1つのIIIFマニフェスト内に複数の巻が含まれており、何コマ目から何コマ目までが何巻に属するのか、素人には判断が難しい場合があります。

https://da.dl.itc.u-tokyo.ac.jp/portal/assets/b90bbddc-509d-7c12-0fb9-af409a90a487

そこで、上記に資料に対してコマ毎のOCRテキストを取得し、今回作成したアプリに問い合わせることで、ページ毎に推定される巻数が提示され、巻の変わり目を知る手助けを行うことができます。

OCR

OCRにあたっては、NDL古典籍OCR-Liteを使用します。

https://github.com/ndl-lab/ndlkotenocr-lite

OCR結果を修正して、以下のようなTEI/XMLを作成しました。

output.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0">
  <teiHeader>
    <fileDesc>
      <titleStmt>
        <title>OCR結果: https://iiif.dl.itc.u-tokyo.ac.jp/repo/iiif/b90bbddc-509d-7c12-0fb9-af409a90a487/manifest</title>
      </titleStmt>
      <publicationStmt>
        <publisher>NDL古典籍OCR-Lite</publisher>
        <date>2025-01-29</date>
      </publicationStmt>
      <sourceDesc>
        <bibl>
          <ptr target="https://iiif.dl.itc.u-tokyo.ac.jp/repo/iiif/b90bbddc-509d-7c12-0fb9-af409a90a487/manifest"/>
        </bibl>
      </sourceDesc>
    </fileDesc>
  </teiHeader>
  <text>
    <body>
      <ab n="1" type="page" facs="https://iiif.dl.itc.u-tokyo.ac.jp/iiif/soto_ogai_202310/A05_4/004/A05_4_004_0001.tif/full/full/0/default.jpg">
        <lb/>
        <seg type="本文" n="1" corresp="#zone-1-1">国外</seg>
        <lb/>
        <seg type="本文" n="3" corresp="#zone-1-3">紅葉のか</seg>
        <lb/>
        <seg type="本文" n="4" corresp="#zone-1-4">はなのえん</seg>
        <lb/>
        <seg type="本文" n="5" corresp="#zone-1-5">あふひ</seg>
      </ab>
      <ab n="2" type="page" facs="https://iiif.dl.itc.u-tokyo.ac.jp/iiif/soto_ogai_202310/A05_4/004/A05_4_004_0002.tif/full/full/0/default.jpg">
        <lb/>
        <seg type="本文" n="1" corresp="#zone-2-1">OOO</seg>
        <lb/>
        <seg type="本文" n="2" corresp="#zone-2-2">□□□□□□□□</seg>
        <lb/>
        <seg type="本文" n="3" corresp="#zone-2-3">源氏十七才ノ十月より四年の十月迄有</seg>
        <lb/>
        <seg type="本文" n="4" corresp="#zone-2-4">/朱雀院の行幸ば。神無月の十日</seg>
        <lb/>
        <seg type="本文" n="5" corresp="#zone-2-5">あまりなり。よのつねならず。おもし</seg>
        <lb/>
        <seg type="本文" n="6" corresp="#zone-2-6">ろかるべきたびのこと成ければ。御</seg>
        <lb/>
        <seg type="本文" n="7" corresp="#zone-2-7">かた〴〵物見給はぬことを口おし</seg>
        <lb/>
        <seg type="本文" n="8" corresp="#zone-2-8">がり給。うへもなつぼの。み給はざらん</seg>
        <lb/>
        <seg type="本文" n="9" corresp="#zone-2-9">をあかずおぼさるれば。誠楽を御</seg>
        <lb/>
        <seg type="本文" n="10" corresp="#zone-2-10">前にてせさせ給ふ。源氏の中将は。</seg>
        <lb/>
        <seg type="本文" n="11" corresp="#zone-2-11">青海波をぞまひ給ける。かたてには。</seg>
        <lb/>
        <seg type="本文" n="12" corresp="#zone-2-12">火とのゝ頭中将がたちようい人にこ</seg>
        <lb/>
        <seg type="本文" n="13" corresp="#zone-2-13">となるを。立ならびては。花のかたはらの</seg>
        <lb/>
        <seg type="本文" n="14" corresp="#zone-2-14">みやまばなり。入がたの日かげさやか</seg>
        <lb/>
        <seg type="本文" n="15" corresp="#zone-2-15">にさしたるに。がくのこゑまさり。物の</seg>
        <lb/>
        <seg type="本文" n="16" corresp="#zone-2-16">おもしろきほどに。おなじまひのあ</seg>
        <lb/>
        <seg type="本文" n="17" corresp="#zone-2-17">しぶみおもゝち。よに見えぬさまなり。</seg>
        <lb/>
        <seg type="本文" n="18" corresp="#zone-2-18">詠などし給へるはこれや仏の御迦</seg>
        <lb/>
        <seg type="本文" n="19" corresp="#zone-2-19">一後順伽のこゑならんと聞ゆ。おもし</seg>
      </ab>
      <ab n="3" type="page" facs="https://iiif.dl.itc.u-tokyo.ac.jp/iiif/soto_ogai_202310/A05_4/004/A05_4_004_0003.tif/full/full/0/default.jpg">
        <lb/>
        <seg type="本文" n="1" corresp="#zone-3-1">ろく哀なるに。みかと泪おとし給。</seg>
        <lb/>
        <seg type="本文" n="2" corresp="#zone-3-2">上達部みこたちもみななき給ぬ</seg>
        <lb/>
        <seg type="本文" n="3" corresp="#zone-3-3">詠はてゝ初うちなをし給へるにま</seg>
        <lb/>
        <seg type="本文" n="4" corresp="#zone-3-4">ちとりたるがくのにぎはゝしきに</seg>
        <lb/>
        <seg type="本文" n="5" corresp="#zone-3-5">かほの色あひまさりてつねよりも</seg>
        <lb/>
        <seg type="本文" n="6" corresp="#zone-3-6">ひかると見え給。春宮の女御。かく</seg>

推定

上記のXMLファイルを入力として、先に紹介したGradioアプリのAPIを利用します。

from gradio_client import Client
import json
import xml.etree.ElementTree as ET
from tqdm import tqdm

client = Client("nakamura196/genji_predict")

def extract_text_from_lines(element):
    """本文タイプの要素からテキストを抽出する"""
    lines = element.findall(".//*[@type='本文']")
    return ''.join(line.text for line in lines)

def format_prediction_result(result):
    """予測結果を 'vol-page' 形式にフォーマットする"""
    first_result = result[0][0]
    return f'{first_result["vol"]}-{first_result["page"]}'

def predict(tei_xml_path, limit=-1):
    # XMLの解析
    root = ET.parse(tei_xml_path).getroot()
    
    # ページ要素の取得
    elements = root.findall(".//*[@type='page']")
    if limit > 0:
        elements = elements[:limit]  # スライス表記を簡潔に

    # 予測実行
    predict_results = {}
    for i, element in tqdm(enumerate(elements, 1)):  # enumerate(elements, 1)で1から開始
        text = extract_text_from_lines(element)
        
        result = client.predict(
            query=text,
            api_name="/predict"
        )
        
        predict_results[i] = format_prediction_result(result)

    # 結果の出力
    print(json.dumps(predict_results, ensure_ascii=False, indent=4))

tei_xml_path = "./output.xml"
predict(tei_xml_path)

結果、以下のような結果が得られます。キーはコマ数、値は{巻数}-{校異源氏物語のページ数}です。以下の結果から、2コマ目から第7巻が始まり、29または30コマ目(また40または41コマ目)で巻が変わっているように思われます。

{
    "1": "33-1019",
    "2": "7-237",
    "3": "7-238",
    "4": "7-239",
    "5": "7-239",
    "6": "7-240",
    "7": "7-241",
    "8": "7-242",
    "9": "7-243",
    "10": "7-244",
    "11": "7-245",
    "12": "7-246",
    "13": "7-247",
    "14": "7-248",
    "15": "7-249",
    "16": "7-250",
    "17": "7-251",
    "18": "7-252",
    "19": "7-253",
    "20": "7-254",
    "21": "7-254",
    "22": "7-255",
    "23": "7-256",
    "24": "7-257",
    "25": "7-258",
    "26": "7-259",
    "27": "7-260",
    "28": "7-261",
    "29": "7-262",
    "30": "8-269",
    "31": "8-269",
    "32": "8-271",
    "33": "8-271",
    "34": "8-272",
    "35": "8-273",
    "36": "8-274",
    "37": "8-275",
    "38": "8-276",
    "39": "8-277",
    "40": "8-278",
    "41": "9-283",
    "42": "9-283",
    "43": "9-284",
    "44": "9-285",
    "45": "9-286",
    "46": "9-287",
    "47": "9-288",
    "48": "9-289",
    "49": "9-290",
    "50": "9-290",
    "51": "9-291",
    "52": "9-292",
    "53": "9-293",
    "54": "9-294",
    "55": "9-295",
    "56": "9-296",
    "57": "9-297",
    "58": "9-297",
    "59": "9-298",
    "60": "9-299",
    "61": "9-300",
    "62": "9-301",
    "63": "9-302",
    "64": "9-303",
    "65": "9-304",
    "66": "9-305",
    "67": "9-305",
    "68": "9-306",
    "69": "9-307",
    "70": "9-308",
    "71": "9-309",
    "72": "9-310",
    "73": "9-311",
    "74": "9-312",
    "75": "9-312",
    "76": "9-313",
    "77": "9-314",
    "78": "9-315",
    "79": "9-317",
    "80": "9-318",
    "81": "9-319",
    "82": "9-320",
    "83": "9-321",
    "84": "9-322",
    "85": "9-323",
    "86": "9-323",
    "87": "9-324",
    "88": "9-325",
    "89": "9-326",
    "90": "13-478",
    "91": "1-5"
}

実際に30コマ目を確認すると、巻が変わっていることが確認できます。

また41コマ目は以下です。

まとめ

くずし字に対するOCR精度の向上により、様々な応用が可能になっているように思います。画像の公開および提供機関、OCRプログラムの開発に携わっている方々に感謝いたします。

Discussion