Contents
とりあえず、こちらでMNISTの画像を学習させた辞書をつくりました。
eng_best辞書にMNISTを再学習させてもみたけど、今回はひとまずMNISTだけの話。
解析させて思ったこと
- 1~2文字だとEmptyとみなしてくる
画像が小さいことが原因ではない(拡大しても同じ)
eng辞書でも同じ。
『1 23 』ならかろうじて『23』部分だけ拾う - 3文字以上なら認識出来ている(画像を拡大しても認識する)
- 縦に空白が多いと、認識しないか『8』とみなしがち
はい、というわけで、解析前に余白をなるべくカットすることは必要そう。
あと、単一文字だと認識しないのは、PSM値が関係ある?
こちらによると初期値は13って書いてあるのに、詳細が書いてない。なんてこった。
こちらやこちらを信じれば、8(single word)よりは10(single character)でしょうかね。
ただ、こちらは学習時ではなく使うときに指定する値だね?
学習時に指定することと、使うときに指定することの関係がよくわからん。
PSMを指定
使う側から指定
Ubuntu上のtesseractコマンドには、PSMを指定するオプションがありませんでした。
C#製ライブラリから使いたくて辞書を作ったので、Tesseract.TesseractEngineクラスを確認。
ありました! DefaultPageSegMode!
簡単なツールを作って実験してみる。
using System;
using System.Windows.Forms;
using System.IO;
using Tesseract;
namespace TestTesseractTraineddata
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void bunRun_Click(object sender, EventArgs e)
{
ResultBox.ResetText();
try
{
// 言語をリストアップ
string traineddataDir = TraineddataBox.Text;
string[] traineddataLangName = Directory.GetFiles(TraineddataBox.Text);
for (int i = 0; i < traineddataLangName.Length; i++)
{
traineddataLangName[i] = Path.GetFileNameWithoutExtension(traineddataLangName[i]);
}
// ファイルごとに解析開始
string[] Imagefiles = Directory.GetFiles(ImageBox.Text);
for (int imgIdx = 0; imgIdx < Imagefiles.Length; imgIdx++)
{
ResultBox.Text += "===========" + Imagefiles[imgIdx] + "解析結果===========\r\n";
for (int lngIdx = 0; lngIdx < traineddataLangName.Length; lngIdx++)
{
using (var tesseract = new TesseractEngine(traineddataDir, traineddataLangName[lngIdx]))
{
tesseract.DefaultPageSegMode = (PageSegMode)(PSMBox.Value);
using (var img = Pix.LoadFromFile(Imagefiles[imgIdx]))
{
// OCRの実行
using (Page page = tesseract.Process(img))
{
ResultBox.Text += (lngIdx + 1) + " : " + traineddataLangName[lngIdx] + "\r\n" + page.GetText() + "\r\n";
ResultBox.Refresh();
}
}
}
}
}
}
catch (Exception ex)
{
ResultBox.Text = ex.Message;
}
}
/// <summary>
/// Form自体のりサイズ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Resize(object sender, EventArgs e)
{
// 結果表示エリアを追随させる
ResultBox.Width = this.Width - (ResultBox.Left * 2);
ResultBox.Height = this.Height - ResultBox.Top - 50;
}
private void PSMHelpBtn_Click(object sender, EventArgs e)
{
MessageBox.Show("OsdOnly = 0\r\nAutoOsd = 1\r\nAutoOnly = 2\r\nAuto = 3\r\nSingleColumn = 4\r\nSingleBlockVertText = 5\r\nSingleBlock = 6\r\nSingleLine = 7\r\nSingleWord = 8\r\nCircleWord = 9\r\nSingleChar = 10\r\nSparseText = 11\r\nSparseTextOsd = 12\r\nRawLine = 13\r\n");
}
}
}
DefaultPageSegModeをコロコロ変えてみると、解析結果がずいぶん変わりました!
やっぱり10を指定すると、Emptyになることはなく、解析してくれます。いいね。
学習時の指定
上で実験したときは、なにも考えずに学習させた辞書を使っていました。
つまり、13(1行の文章)かな?
そして、1文字ずつ切り取って学習させた辞書と、40文字ずつ切り取って学習させた辞書でも微妙に結果が違う。
気持ち、40文字辞書の方が良い結果を返してくれてる、気がしなくもない。
PSM=13がいい感じに効いているのでしょうか?
40文字ずつでeng_bestに再学習させた辞書の方がさらに精度は高そう。
1文字ずつ切り取ったデータで、PSMに10を指定して、辞書を再作成してみましょう。
nohup time -f "Run time = %E\n" make training MODEL_NAME=mnist PSM=10 >> train.log 2>&1 &
ちょっと認識率良くなりました。
でも体感として、1文字 < 1文字PSM=10 < 40文字辞書 < eng_bestに40文字で再学習って感じ。
MNISTの学習画像を解析してみた
いろんな辞書を作ったので、PSM=10を指定してMNISTの画像(1画像につき1文字)を解析してみました。
「補正あり」は、ゼロの画像にアルファベットのオー返しても正解、みたいな感じで補正して計測。
辞書 | 正解率 | 補正あり |
---|---|---|
eng | 51% | 63% |
eng_best | 38% | 52% |
eng_bestにMNISTを再学習 | 95% | 95% |
eng_bestにMNISTを再学習して警告対策 | 95% | 96% |
MNISTを1文字ずつPSM=10で学習 | 8% | – |
MNISTを1文字ずつPSM=13で学習 | 8% | – |
MNISTを40文字ずつ学習 | 76% | – |
EMNISTを40文字ずつ学習 | 40% | 53% |
eng_bestにEMNISTを再学習 | 71% | 86% |
少なくとも、1文字ずつより、ある程度の塊で学習させる方が良さそう。
数字や限られた記号だけを認識するためなら、下手に他の文字を学習させすぎるのも考えもの、って印象。
てか、そもそも人間の目から見ても「それ、どっちなん??」って文字、結構あるな???
他にもニッチなIT関連要素をまとめていますので、よければ一覧記事もご覧ください。