酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

自動エアコン調整システムを考える(2) - C#でtesseract-ocrを使って数値を取得する

我が家にはみそあんというキンクマハムスター(長毛種)という天使がいることは前回「自動エアコン調整システムを考える(1) - 部屋の間取りとシステムの基本構成 - 酢ろぐ!」の記事で書きました。今回は室温を取得するためにどうしたら良いのかを検討していきましょう。

f:id:ch3cooh393:20140712224356j:plain

上の画像はみそあんゲージに設置されたネットワークカメラから出力される画像で、温度センサーがついておりその値が画像右上に出力されています。ネットワークカメラは「カメラ一発!」を設置しています。

PLANEX カメラ一発! Wi-Fi/有線対応 100万画素 ネットワークカメラ CS-W60HD

PLANEX カメラ一発! Wi-Fi/有線対応 100万画素 ネットワークカメラ CS-W60HD

てっとり早やく済ませたいので、OCRを使って文字を認識させようと思います。

C#から使えるOCRライブラリで、NuGetからインストール可能なもののひとつに「tesseract-ocr」があります。

NuGetから「tesseract-ocr」をインストールする

NuGetで「tesseract-ocr」をインストールします。

f:id:ch3cooh393:20140712235723p:plain

これでC#からtesseract-ocrを使う準備が整いました。dllアセンブリも参照済みです。WindowsストアアプリやWindows Phoneアプリを作るのでもNuGetは重宝しますのでVisual Studioを使って開発されている方は是非ご利用ください。

C#からtesseract-ocrを使ってOCRする

tesseract-ocrを使うためには、'.traineddata拡張子'の学習データが必要になります。学習データは自分で作ることもできますが、手間を惜しまない方向で下記のページからダウンロードすると良いかもしれません。

英語と日本語の学習データをダウンロードします。.tar.gzなので任意のフォルダに解凍しておきましょう。

  • jpn
  • eng

まずは、tesseract-ocrを使って文字列を認識させるためのコードの基本形は、以下のコードの通りです。

// 解析したい画像のファイルパス
var imgPath = @"C:\Users\ch3cooh\Desktop\unnamed.jpg";

// jpn.traineddata等の学習データが格納されたディレクトリパス
var dataDirPath = @"C:\Users\ch3cooh\Desktop\tessdata";

// 使用する学習データの言語(ディレクトリパスに格納されている言語に限る)
var lang = "jpn";

using (var image = new System.Drawing.Bitmap(imgPath))
{
    using (var engine = new Tesseract.TesseractEngine(dataDirPath, lang))
    using (var page = engine.Process(image))
    {
        var resultText = page.GetText();

        Console.Write(resultText);
    }
}

結果はこんな感じ。

\n\n

改行コードが2つ。何故なのか……

解析する領域を指定することで精度を上げる

先ほど記したコードは、OCRするための最小限のコードでしたので、画像解析する対象が画像全体を指定していました。あまりにも雑だったようで、ダメダメですので解析するのを最低限の領域を指定します。

解析したいのは、下図の日付と温度のところだけです。

f:id:ch3cooh393:20140713012119j:plain

ターゲット画像を切り貼りするのも良いのですが、teaser act-ocrには指定された領域だけの解析をする方法が用意されています。

Tesseract.Rectクラスのインスタンスを生成して

    ~省略~

    // 処理する領域を指定します
    var rect = new Tesseract.Rect(0, 0, 286, 20);

    // 解析エンジンを生成して、指定した言語の学習データを使って解析
    using (var engine = new Tesseract.TesseractEngine(dataDirPath, lang))
    using (var page = engine.Process(image, rect))
    {
        var resultText = page.GetText();

        Console.Write(resultText);
    }
}

結果はこんな感じ。

20ー4/野"7/ ー2 ーg=ー6=37 26〝C

うーん、微妙……

検出する文字をあらかじめ指定することで精度を上げる

使用する文字を限定してOCR精度を上げます。

    ~省略~

    // 解析エンジンを生成して、指定した言語の学習データを使って解析
    using (var engine = new Tesseract.TesseractEngine(dataDirPath, lang))
    {
        // 解析結果に数字だけが表示されるようにする
        engine.SetVariable("tessedit_char_whitelist", "0123456789/:C");

        using (var page = engine.Process(image, rect))
        {
            var resultText = page.GetText();

            Console.Write(resultText);
        }
    }
}

結果はこんな感じ。

20ー4/ 7/ ー2 9 ー6 37 26 C

より学習している英語の.traineddataを使って精度を上げる

取得したいのは記号+数字部分だけなので、使用言語にengを指定します。

// 使用する学習データの言語(ディレクトリパスに格納されている言語に限る)
var lang = "eng";

結果はこんな感じ。

2014/67/12 19:16:37 26 C

監視カメラの画像をOCRにかけた結果、「2014/07/12 19:16:37 26℃」を「2014/67/12 19:16:37 26 C」と解析するところまではできるようになった。