見出し画像

最近ポケモンSVのレンタルパ画像から文字を読み取ろうとしている話

11月30日までに記事を書くと3ヵ月連続更新らしい、3日坊主ならぬ3ヵ月坊主回避の為に最近ハマっていることを書こうと思う。

最近Switchの自動化やそのほかのことなどでPythonというプログラミング言語に触れる機会があり、プログラミング面白そうだなと少し興味が湧いてきていた。
自分でもやってみたいなと思うようになり「Python 初心者」などで調べてみるとPythonの学習に使える資料を京都大学が無料で公開していたりしていて学び始めのハードルは低そう、とりあえず公開されている「プログラミング演習 Python 2021」から学び始めた。

少しずつ演習やほかのサイトなどを参考に学習を進めていたのだが新しいことをやるという事はその分自分の時間が減るという事。
ポケモンに割ける時間が減り、新しい構築を使いたいが育成が追い付かないという問題が発生した。
解決の為にレンタルパ画像を収集することにしたが集めてみると毎回画像を開いてお目当てのパーティを探すのが面倒、出来ればテキスト化してExcelとかで管理したい。
どうにかできないかと思い色々試した結果「ヤドンの井戸」さんの「ポケモンパーティ文字起こし」を使いパーティを文字にしてレンタルIDと合わせて保存しておくことでかなり楽になった。

最近Pythonを学び始めてやりたいことを探していたところに見つけたこのツールはまさにやってみたいことだったので自分にできる範囲で再現してみることにした。

1.Tesseractを使って画像から文字を読み取る

とりあえずやりたいことをある程度まとめていくと

・スマホやウェブアプリは難しそうなのでPCで動くアプリが作りたい
・画像をドラック&ドロップで読み込ませたい
・できればフォルム判別なども自動でやってほしい

以上を行うためには

・GUI(アプリの側)
・OCR(画像から文字を読み取るやつ)
・テンプレートマッチング(画像から同じ画像を読み取るやつ)

が必要そう。
それぞれ調べていくと

・PythonでGUIを作るならtkinterが簡単そう
・ドラック&ドロップ機能を使うにはtiknterのtkinterDnDが必要
・文字読み取りはTesseract
・テンプレートマッチングはOpenCV

で再現できそうなのでとりあえず最初にアプリの側と文字の読み取り機能の実装を目指してやってみた。

試運転

1280*720 
ハバタクカミ ゲンガー
イダイトウ サーフゴー
ミカルゲ ラウドボーン

640*360
ウインディ ヌメルゴン
クレセリア ドラパルト
チオンジェン カイリュー

1024*576
ハバタクカミ オーガポン
ランドロス ヒードラン
クレセリア テツノカイナ

読み取り結果

元画像から読み取りたい場所を切り取ってグレースケールをかけた後に2値化したりそのままだと黒地に白文字になってしまうので白黒反転させたり色々下処理をしてみた、8割くらいは読み取れるがだいぶ怪しい。
特に濁音無声音が怪しく、見た目で何のポケモンかは分かるがフィルター検索などは流石に無理、何とか精度を向上させたい。

2.Levestein距離による置換

ある日突然大体合っているなら名前リストを作って一番似た名前に置換すれば大体合うのでは?と思いついたので「Python 文字列 類似」で検索するといくつかの候補が出てきた、眺める中でLevestein距離による置換がライブラリがあって使うのが簡単そうなので実装してみた。

かなりいい感じに成功したがいくつか問題も見つかった。

・ヤバソチャを読み取って「ヤバリチヤ」が出た場合
「ヤバチャ」(1文字削除)と「ヤバソチャ」(1文字置換)で差が出ない、
結果リストの先にあるヤバチャが選ばれてしまう
・イーユイを読み取って「ィーュィ」が出た場合
「イーユイ」(3文字置換)より「ガーディ」(2文字置換)になってしまう

どちらもTesseractの読み取り精度の問題なのでそちらを改善しなければならないが少し見てみた感じ難しそうだったので一旦保留して別のことをやることにした。

作成したポケモンの名前,特性,持ち物(一部),技のリスト

3.OpenCVを使ってフォルム判別をやってみる

できればフォルムも自動で読み取ってほしいので考えたのがOpenCVのテンプレートマッチングによる判別だった、実機側と完全に同じ画像でなくともどちらが似ているかくらい分かれば行けるだろうと思い試してみた。
ポケモン名,対応する画像,返す名前を記入したcsvファイルを作成
OCRでテキスト読み取り
→Levestein距離による置換
→置換後のテキストでcsvファイル先頭行を検索
→ヒットした行の対応する画像でテンプレートマッチングしてmaxvalを保存
→複数ヒットした場合以前のmaxvalと比較して高い場合結果を保存
→一番高いmaxvalを出した画像の行にある返す名前を出力
で実装した。

拾ってきたアイコン画像をそのまま使うと透過部分が悪さをしてしまうのか精度が低すぎて使い物にならなかった。
色々調べた結果用意したアイコン画像がレンタルパのポケモンアイコンよりかなり小さかったので大体同じ大きさになるようにリサイズ、更にマスクを使ったテンプレートマッチングの方法を見つけてやってみたところうまくいった。

一部の例外の為に第2タイプのテンプレートマッチングを実装してフォルム判別は終了。

一部の例外

レンタルパ画像からテラシンボルとタイプのアイコンを切り取ったもの

4.jTessBoxEditorを使った言語データの訓練

調べる中でjTessBoxEditorを使ってTesseractにポケモンのフォントを学習させるくらいであれば自分でもできそうだと思ったのでやってみることに。

これらのサイトを参考に進めていった。

まずポケモンSVのフォントを調べてみたところ
・ニタラゴルイカ
・タイプラボN
・ロダンニタラゴ
の系統らしいという事がわかったもののすべて有料フォント。(フォントのサブスク滅茶苦茶値段が高くて椅子から転げ落ちた)
ニタラゴルイカについては手が出ないこともないがデザイン関連の仕事しているわけでない自分がフォント買ってもなぁと思ったので似たフォントをいくつか探してみた。

jTessBoxEditorを入手

jTessBoxEditorの入手場所少し分かり辛い

平仮名カタカナ数字のほかにポケモンで使われる単語も入れたほうが良いらしいので2で使ったリストから,を取り除いて各5回ほどコピペして使ってみることにした。
単語を使うには恐らくBox Editor画面で「ハ バ タ ク カ ミ」のように1文字ずつに分かれた状態からボックスをmargeするべきだと思われる、が流石に全部手動でやるのは無理と思って調べてみた結果滅茶苦茶ありがたいプログラムを発見した。

テキストファイル読み込みの際にエンコードエラーがでたので少し弄った以外はそのまま使用した。
(平仮名カタカナ数字記号ポケモン関連の単語を1行ずつ)×5に分けたテキストファイルを用意して読み込ませて終わり、神。

jpnbest

Tesseractデフォルトのjpnbestによる読み取り、とりあえずこれを超えたい。

作成したtrainneddataによる読み取り結果

kosugi bold
kosugi maru bold
meirio bold
notosan bold
bizudpg bold
murecho bold

一番牟礼町が可能性を感じたので当面jpn bestでやりつつmurecho boldで精度を高める方法を探しながら別の作業をやっていくことにした。

5.Concurrent futuresを使った並列処理化

ポケモン6匹に対して名前,特性,持ち物,技4つ加えてレンタルIDで計43個文字の読み取りを行うと流石に遅い、1回で約45秒ほどかかってしまう。
辛かったので並列処理について調べると関数化したOCRをconcurrent futureにぶち込めば良さそうなので実装。

本当は1つに纏めたかったし読み取り言語等も分けたかったが自分には難しかった…。こんな雑な実装でも約45秒→約18秒と半分以下になったのでとりあえず満足した。

6.TesseractのFine Tuning

いよいよ他に出来ることがなくなってしまったので手を出すことにした。
今まで躊躇っていた理由の1つにFine Tuningについて書かれた記事が基本的にUbuntu環境を前提としたものばかりだったというのがあった。他のことをやりつつ調べる中でWindowsでも出来そうな記事をいくつか見つけたので挑戦してみた。
自分が見つけた中では

・wslを利用してWindowsにUbuntuを入れる方法
・Windowでそのまま行う方法

の2つがあった。先にUbuntuを入れて行う方法を見つけて試してみたがUbuntuの導入まではできたもののどこかのタイミングで詰まってしまって諦めた。

Windowsで行う方法は上を参考に行った。(py train.py stage ~に注意)
基本的に書いてあることをそのまま行えば終わりだったが自分の環境だとStage1でエラーが出た。

ERROR - Loaded unicharset of size 3715 from file 
Other caseof Z is not in unicharset
Setting script properties
Warning: properties incomplete for index 3712 = 2
Warning: properties incomplete for index 3713 = Z
Warning: properties incomplete for index 3714 = :
Config file is optional, continuing...
Null char=2
Invalid format in radical table at line 0: 19886 3 23 6 3
Creation of encoded unicharset failed!!
Error writing recoder!!
Reducing Trie to SquishedDawg
Reducing Trie to SquishedDawg
Error during conversion of wordlists to DAWGs!!

unicharsetが悪いっぽいがよく分からない、それっぽいファイルも見当たらないので公式のファイルを覗いてみるとlangdata-lstmフォルダにはそれっぽいファイルがある、落としてきてlangdata_dirをlangdata-lstm指定にしてtextだけ別に指定すると無事に動いてくれた。


完成してないけど現時点ではここまで。
Fine TuningのおかげでLevestein距離による置換前で42/43±1は無事に読み取れるようになった。更に上を目指して学習用テキストの量や内容などを弄ったり、少し変な形で実装してしまったTreeviewを右クリックから要素を消すようにできなくて悪戦苦闘中。

とりあえずPython楽しいのでみんなもPythonしよう!


この記事が気に入ったらサポートをしてみませんか?