【C++】OpenCVとTesseractで実現する画像文字認識プログラムの作り方
C++とOpenCV、Tesseractを組み合わせれば、画像から文字情報を効率的に抽出することが実現できます。
OpenCVで画像の前処理(グレースケール化や二値化など)を施し、Tesseractで文字認識を行う方式です。
シンプルな処理のため実装の敷居も低く、柔軟なOCRソリューションとして利用できる点が魅力です。
プログラムの基本構造
画像前処理
前処理は画像の質を高めるために行う工程です。
解像度や画像の状態によって認識率が左右されるため、簡単な操作で画像が扱いやすくなります。
グレースケール変換
カラー画像をグレースケールに変換することで、不要な色情報を取り除き処理速度を向上させます。
以下のサンプルコードは、cv::imread
で画像を読み込み、cv::cvtColor
を利用してグレースケールに変換する工程を示しています。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 入力画像の読み込み
cv::Mat colorImage = cv::imread("sample.jpg");
if (colorImage.empty()) {
std::cout << "画像読み込みエラー" << std::endl;
return -1;
}
// グレースケール変換処理
cv::Mat grayImage;
cv::cvtColor(colorImage, grayImage, cv::COLOR_BGR2GRAY);
std::cout << "グレースケール変換完了" << std::endl;
return 0;
}


ノイズ除去
画像には様々なノイズが含まれていることが多く、OCRの精度に影響を与えることがあります。
cv::GaussianBlur
を利用して画像のぼかしや平滑化を図ることで、ノイズを軽減できます。
以下のコードでは、グレースケール画像からノイズを除去する処理を実装しています。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// グレースケール画像の読み込み
cv::Mat grayImage = cv::imread("gray_sample.jpg", cv::IMREAD_GRAYSCALE);
if (grayImage.empty()) {
std::cout << "画像読み込みエラー" << std::endl;
return -1;
}
// ノイズ除去処理 (Gaussian Blur)
cv::Mat denoisedImage;
cv::GaussianBlur(grayImage, denoisedImage, cv::Size(5, 5), 0);
std::cout << "ノイズ除去完了" << std::endl;
return 0;
}
ノイズ除去完了
二値化処理
二値化処理は、画像内の輝度値をしきい値で分割し、白黒の画像に変換する工程です。
これにより、文字と背景のコントラストが強調され、OCRの認識精度が向上します。
以下のサンプルコードは、cv::threshold
を使って二値化処理を実現しています。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// ノイズ除去済みの画像の読み込み(ここでは既にグレースケール&ノイズ除去が済んだ画像を想定)
cv::Mat denoisedImage = cv::imread("denoised_sample.jpg", cv::IMREAD_GRAYSCALE);
if (denoisedImage.empty()) {
std::cout << "画像読み込みエラー" << std::endl;
return -1;
}
// 二値化処理
cv::Mat binaryImage;
cv::threshold(denoisedImage, binaryImage, 128, 255, cv::THRESH_BINARY);
std::cout << "二値化処理完了" << std::endl;
cv::imshow("二値化画像", binaryImage);
cv::waitKey(0);
return 0;
}
二値化処理完了

OCR処理の流れ
画像前処理を終えた後は、OCRエンジンを利用して画像から文字を抽出します。
この流れで重要なステップはTesseractの初期化、文字認識の実行、結果の取得と解析です。
Tesseractの初期化
OCRエンジンであるTesseractを利用するためには、まず初期化が必要です。
以下のコードでは、TesseractのAPIを用いてエンジンのセットアップを行い、言語データの指定をしています。
エラーが発生した場合は、適切なメッセージが出力される仕組みになっています。
Tesseractは別途インストールが必要です。MSYS2(64bit)であれば、pacman -S mingw-w64-x86_64-tesseract-ocr
でインストールできます。
- mingw-w64-ucrt-x86_64-tesseract-ocr
- mingw-w64-clang-x86_64-tesseract-ocr
- mingw-w64-clang-aarch64-tesseract-ocr
- mingw-w64-x86_64-tesseract-ocr
-ltesseract
#include <tesseract/baseapi.h>
#include <iostream>
int main() {
// Tesseractエンジンのインスタンスを生成
tesseract::TessBaseAPI ocr;
// 英語の言語データを利用して初期化
if (ocr.Init(NULL, "eng")) {
std::cerr << "Tesseract初期化エラー" << std::endl;
return -1;
}
std::cout << "Tesseract初期化完了" << std::endl;
return 0;
}
Tesseract初期化エラー
実際に使用する場合は言語データを渡す必要があります。
リポジトリから使用する言語データをダウンロードし、カレントディレクトリに配置してください。
カレントディレクトリ以外に配置する場合は、traineddataファイルが有るフォルダのパスを、NULLの代わりに渡してください。
文字認識の実行
前処理済みの画像をTesseractに渡して文字認識を実行します。
ここではocr.SetImage
で画像データをセットし、ocr.GetUTF8Text
で抽出されたテキストを取得します。
画像のサイズやチャンネル数にも注意してパラメータを指定することで、正確な認識につながります。
結果取得と解析
OCR処理が完了すると、画像中の文字情報がテキストとして返されます。
返ってきたテキストは、必要に応じてさらに加工や解析を行うことが可能です。
メモリ管理にも気を付けて、取得した文字列のメモリは適切に解放してください。
以下は、前処理からOCR実行までを統合したサンプルコードになります。
#include <opencv2/opencv.hpp>
#include <tesseract/baseapi.h>
#include <iostream>
int main() {
// 画像読み込み処理
cv::Mat colorImage = cv::imread("input.jpg");
if (colorImage.empty()) {
std::cout << "画像読み込みエラー" << std::endl;
return -1;
}
// グレースケール変換
cv::Mat grayImage;
cv::cvtColor(colorImage, grayImage, cv::COLOR_BGR2GRAY);
// ノイズ除去
cv::Mat denoisedImage;
cv::GaussianBlur(grayImage, denoisedImage, cv::Size(5, 5), 0);
// 二値化処理
cv::Mat binaryImage;
cv::threshold(denoisedImage, binaryImage, 128, 255, cv::THRESH_BINARY);
// Tesseract初期化
tesseract::TessBaseAPI ocr;
if (ocr.Init(NULL, "eng")) {
std::cout << "Tesseract初期化エラー" << std::endl;
return -1;
}
// 前処理済みの画像をOCRエンジンにセット
ocr.SetImage(binaryImage.data, binaryImage.cols, binaryImage.rows, 1, binaryImage.step);
// OCR実行および文字認識結果の取得
char* outText = ocr.GetUTF8Text();
std::cout << "抽出されたテキスト:" << std::endl;
std::cout << outText << std::endl;
// メモリ解放
delete[] outText;
return 0;
}

抽出されたテキスト:
Please confirm
your appointment
at 3:00 PM on
March 5, 2025.
モジュール設計と連携
大規模なプロジェクトでは、各機能ごとに分割したモジュールを用意することで、コードの管理や再利用性が向上します。
画像処理とOCR処理を分けることで、保守性もよくなります。
画像処理モジュール
画像処理に関する機能は、グレースケール変換、ノイズ除去、二値化処理などをひとまとめにして実装すると、複数のプロジェクトで再利用しやすくなります。
例えば、画像処理専用のクラスや名前空間を作成して処理フローを整理すると、他の部分との連携がスムーズになります。
- 画像の読み込み、前処理の各工程を関数化
- グローバルなパラメータ設定を一元管理
OCRモジュール
OCR機能は、Tesseractの初期化、文字認識の実行、結果取得などに分けられます。
専用のクラスを用意して、Tesseractの設定や動作をラップすることで、エラーハンドリングや将来的な拡張も行いやすくなる仕組みになります。
- Tesseract設定や状態を管理するクラスの作成
- 言語データやカスタム設定の柔軟な変更が可能な構成
統合処理の実装
画像処理モジュールとOCRモジュールを統合し、シンプルなインターフェースからOCR処理を呼び出せるように設計することで、全体の処理がスムーズに連携します。
各モジュールは独立性が保たれながらも、連携部分では共通のデータ形式を利用するなど、インターフェースの整備が重要です。
- 各モジュール間のデータ受け渡し方法を統一する
- エラーが発生した場合のリカバリ手順も明確に設定
エラー管理とデバッグ
画像認識処理では、様々な要因でエラーが発生する可能性があるため、エラー管理とデバッグ手法を工夫することが重要です。
処理の途中でデータの検証や例外処理を適切に実装することで、プログラムの安定性を向上させます。
入力画像の検証
画像の読み込み時に、画像ファイルが存在しているか、正しく読み込めたかを確認することが大切です。
存在しないファイルや壊れた画像を扱った場合の対処法として、エラーメッセージの出力や処理の中断などを実装します。
- ファイルパスのチェックを行う
- 画像サイズやチャンネル数の検査
OCR処理における例外処理
Tesseractの初期化や画像セット時に、例外が発生することがあるため、例外処理を適切に行う仕組みを取り入れておくと安心です。
エラーが発生した際も、ログを出力するなどして原因究明ができる状態を維持する工夫が必要です。
- 初期化失敗時に適切なエラーメッセージを出力
- OCR結果が不正な場合のデバッグ手法の検討
パフォーマンス向上と拡張性
OCRシステムのパフォーマンス向上や、将来的な機能拡張のポイントとして、前処理の最適化や多言語対応の仕組みが考えられます。
柔軟な設計により、ユーザの要求に応じた拡張が可能になります。
前処理最適化の工夫
画像前処理の工程では、演算量が多くなることもあるため、処理速度の向上は重要なポイントです。
並列処理の導入や効率的なアルゴリズムの採用によって、処理を高速化できます。
並列処理の導入検討
マルチスレッドやGPUを利用した並列処理を検討することで、画像前処理の負荷を分散させることができます。
たとえば、OpenCVのcv::parallel_for_
やCUDAとの連携を利用することで、高速な処理が期待できます。
- マルチスレッドによる画像分割処理
- GPUを活用したフィルタ処理の実装
多言語認識への応用
OCRの活用範囲を広げるために、多言語に対応する仕組みも用意すると便利です。
Tesseractは様々な言語に対応しているため、環境設定を変更するだけで他言語のテキスト認識が可能になります。
カスタム辞書の設定
特定の専門用語や固有名詞を正しく認識するために、カスタム辞書を利用する方法があります。
辞書をカスタマイズすることで、誤認識を減らし精度向上に効果が期待できます。
- ユーザ定義辞書の作成と設定
- 対象言語ごとの固有文字の調整
認識精度改善の対策
認識精度を上げるために、画像前処理のパラメータ調整やTesseractの設定変更、学習済みモデルの更新などが考えられます。
エラーのパターンを解析し、都度最適な処理手法を見つける柔軟な仕組みが役立ちます。
- 二値化のしきい値の最適化
- 学習データの追加や再学習の実施
- OCR前処理のフィードバックループの構築
まとめ
今回紹介した各工程を通して、C++でOpenCVとTesseractを組み合わせたOCRプログラムの基礎構造から細かな実装方法まで柔らかい文体で説明してきました。
画像前処理やOCR実行、モジュール設計、エラー管理、パフォーマンス向上の工夫など、それぞれの部分が相互に連携しながら動作する仕組みを確認することができました。
今後の開発時に参考になれば嬉しく思います。