株式会社インデペンデンスシステムズ横浜

システム開発エンジニアの西田五郎が運営しております。Raspberry Pi や Arduino その他新規開発案件のご依頼をお待ちしております。

OpenCV Tesseract-OCR

Tesseract-OCRの導入(その3)OpenCVの出力を認識する

投稿日:2014年11月24日 更新日:

2019/07/20追記
この記事を書いてから随分と経過しました。完全に情報が古くなっています。外部のサイトですが必要な場合は以下等を参照して下さい。
PythonとTesseract OCRで文字認識
OpenCVのOCR(tesseract-ocr)をWindows(64bit)、C++で使う
vcpkgとCMakeで簡単にtesseract-ocr
2019/07/20追記ここまで

Tesseract-OCRの導入の3回目です。前回はvc++のコンソールアプリからAPIを使ってみました。今回も同じような使い方ですが、OpenCVでの領域の認識結果をTesseract-OCRのAPIで文字を認識するという流れです。(※OpenCV 3.0系からは文字認識が組み込まれるようです。今回のTesseract-OCRもOpenCVに組み込まれるようです。詳細はページ最後の関連サイト等を参照して下さい。)

今回の処理の流れです。以下のような何となくそれらしい入力用紙をイメージした画像を作成しました。作成したのはExcelでそのスクリーンショットを切り取って保存しました。
InputForm1

まずここからOpenCVで入力領域を切り出します。
0013

0011

これらをそれぞれTesseract-OCRの入力ファイルとして認識します。以下のように結果が表示されました。
0014

0015

ここからプログラムの説明です。ペースになっているプログラムは以下で作成したプログラムです。
OpenCVで輪郭抽出から隣接領域の切り出し(その1)輪郭抽出まで

このプログラムのプロジェクトに前回までに書いた環境でTesseract-OCRのAPIを使います。前回書いたライブラリのインクルードディレクトリやライブラリファイルの設定も今回のプロジェクトに追加します。

ここまでをベースにして以下のようなプログラムを作成しました。

※ソース全体は以下からダウンロード出来ます。Tesseractのインストール、ライブラリの設定が必要です。必要な場合は前回までを参照して下さい。
プロジェクト一式
(※必要な場合は用途に限らずご利用頂いて問題ありませんが、一切無保証です。弊社は一切の責任を負いません。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*------------------------------------------------
 処理1
-------------------------------------------------*/
void func1(HWND hWnd)
{
    ////MessageBox(hWnd, "func1", "debug", MB_OK);
    if (imgMatRead.rows == 0){
        MessageBox(hWnd, "画像ファイルが無効です", "エラー", MB_OK);
        return;
    }
 
    //入力画像、ここでは毎回ファイルから読み込む
    cv::Mat imgIn = cv::imread((const std::string&)szOpenFileName, 1); //3チャンネルカラー画像で読み込む;
 
    //グレースケール
    cv::Mat grayImage, binImage;
    cv::cvtColor(imgIn, grayImage, CV_BGR2GRAY);
     
    //2値化(※反転で結果が変わる、基本は背景が黒で物体が白)
    //ここでは固定のしきい値を使用する
    cv::threshold(grayImage, binImage, 220, 255.0, CV_THRESH_BINARY);
    //cv::imshow("bin", binImage);
 
    //輪郭の座標リスト
    std::vector< std::vector< cv::Point > > contours;
 
    //輪郭取得
    cv::findContours(binImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
 
    //輪郭の数
    int roiCnt = 0;
 
    //輪郭のカウント  
    int i = 0;
 
    for (auto contour = contours.begin(); contour != contours.end(); contour++){
 
        std::vector< cv::Point > approx;
         
        //輪郭を直線近似する
        cv::approxPolyDP(cv::Mat(*contour), approx, 0.01 * cv::arcLength(*contour, true), true);
         
        // 近似の面積が一定以上なら取得
        double area = cv::contourArea(approx);
 
        if (area > 1000.0){             
            //輪郭に隣接する矩形の取得
            cv::Rect brect = cv::boundingRect(cv::Mat(approx).reshape(2));
            roi[roiCnt] = cv::Mat(grayImage, brect);
             
            //表示
            cv::imshow("label" + std::to_string(roiCnt+1), roi[roiCnt]);
 
            if (roiCnt == 0){
                imgMatWrite = roi[roiCnt];
            }
         
            roiCnt++;
 
            //念のため輪郭をカウント
            if (roiCnt == 99)
            {
                break;
            }
        }
        i++;
    }
 
    //ここからTesseractの処理
    const int charSize = 1024;
 
    char szTmpPath[charSize];   //作業用ディレクトリ
 
    char userid[charSize];  //ユーザID
    char name[charSize];    //名前
    char msg[charSize];     //メッセージボックス用
 
    //認識対象画像を一時的に保存するディレクトリを取得する
    GetTempPath(sizeof(szTmpPath) / sizeof(szTmpPath[0]),szTmpPath);
    strcat(szTmpPath, "\\ocrimg.png");
 
    //Tesseract初期化
    tesseract::TessBaseAPI tess;
    tess.Init("", "eng", tesseract::OEM_DEFAULT);
 
    //対象画像ごとの処理
    for (int i = 0; i < roiCnt; i++){
 
        //一時ファイルに保存
        imgMatWrite = roi[i];
        cv::imwrite(szTmpPath, imgMatWrite);
 
        //認識開始
        STRING text;
        tess.ProcessPages(szTmpPath, NULL, 0, &text);
 
        //認識結果の取得
        const char* result = text.string();
         
        //ユーザIDの場合
        if (strncmp(result, "UserID", 6) == 0){
            memset(userid, 0x00, 1024);
            strncpy(userid, result + 6, strlen(result) - 6 - 2);    //2は改行コードの長さ
 
            sprintf(msg, "ユーザIDは、%s です。", userid);
            MessageBox(hWnd, msg, "結果", MB_OK);
        }
 
        //名前の場合
        if (strncmp(result, "Name", 4) == 0){
            memset(name, 0x00, 1024);
            strncpy(name, result + 4, strlen(result) - 4 - 2);      //2は改行コードの長さ
 
            sprintf(msg, "名前は、%s です。", name);
            MessageBox(hWnd, msg, "結果", MB_OK);
        }
    }
 
    //念のため
    tess.Clear();
    tess.End();
}

上記ソースは認識の部分のみです。全体が必要な場合はお手数ですがソース一式をダウンロードして参照して下さい。OpenCVの輪郭の認識のプログラムにTesseract-OCRのAPIでの認識を後付けしたような処理ですが以下のようの流れです。

OpenCVで認識出来た輪郭をグレースケールの画像から輪郭画像の配列に保存

輪郭ごとに一時ファイルに保存

一時ファイルをTesseract-OCRで英数字で認識

あらかじめ決めたユーザIDと名前のキーワードがあればユーザID、名前として認識して表示する

OpenCVからTesseract-OCRへは単純にファイルで渡しています。また文字列操作はC言語の関数で処理しています。

まあこれぐらいなら認識出来ましたが日本語も含めてどこまで認識出来るかということになってくると思います。そういった意味でもいろいろと課題があると思いますがとりあえず今回のシリーズとしてはここまでです。

関連サイト
Advanced API
opencvで文字認識その1 Tesseractラッパ – whoopsidaisies’s diary

関連書籍
(※OpenCV 2 プログラミングブックは私も持っています。)

(※ここからはKindle版で英語ですが最新の情報もあるようです。また価格的にはいいかもしれないです。)


AdSense

AdSense

-OpenCV, Tesseract-OCR

執筆者:

関連記事

OpenCVをWin32ベースで利用する(その5)GUIコントロールの追加

OpenCVをWin32ベースで利用するの5回目です。前回の4回目で一度終了したのですが、画面上の入出力用のコントロール、つまり、ラベル、エディット、コンボ等のコントロールがなかったので追加してみまし …

OpenCVで輪郭抽出から隣接領域の切り出し(その3)凸包の取得

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を切り出すという処理を作ってみました。前回までのプログラムとほぼ同じですが、前回までは輪郭抽出 → 直線近似 → 隣接領域 …

OpenCVで背景差分とテスト的な動体検知

OpenCVで背景差分を試してみました。あとテスト的な動体検知も試してみました。背景差分とはあらかじめ取得した画像を背景画像として、観測時点の画像とその背景画像との差分を取ることによりその差分を前景領 …

OpenCVで輪郭抽出から隣接領域の切り出し(その1)輪郭抽出まで

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を切り出すという処理を作ってみました。以下の画像がその結果の例です。(※実画像サイズは大きめです。) ここでの処理結果画像 …

OpenCVをWin32ベースで利用する(その2)OpenCVの組み込み

OpenCVをWin32ベースで利用するの2回目です。前回はベースになるダイアログベースのアプリケーションを作成しました。今回はこのアプリケーションにOpenCVを組み込んで実際に動かしてみます。 ( …

S