📑
校異源氏物語に対する類似テキスト検索アプリを作成しました。
概要
校異源氏物語に対する類似テキスト検索アプリを作成しました。以下のURLからお試しいただけます。
本アプリの使用方法などについて紹介します。
データ
以下の校異源氏物語DBで公開されているテキストデータを使用します。
アプリの内容
仕組みは単純で、校異源氏物語の巻毎・ページ毎のテキストを用意しておき、入力された文字列との編集距離を算出し、類似度が高いテキスト(+巻とページ)を返却します。
ソースコードは以下です。
応用例
例えば、以下の「[源氏物語] [4](東京大学総合図書館所蔵)」では、1つのIIIFマニフェスト内に複数の巻が含まれており、何コマ目から何コマ目までが何巻に属するのか、素人には判断が難しい場合があります。
そこで、上記に資料に対してコマ毎のOCRテキストを取得し、今回作成したアプリに問い合わせることで、ページ毎に推定される巻数が提示され、巻の変わり目を知る手助けを行うことができます。
OCR
OCRにあたっては、NDL古典籍OCR-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