tesseract4androidでの通常数字,7seg数字の認識テスト
16 KB page-sizeに対応
2025.9.29Coskx Lab
1 はじめに
tesseract4androidAPIを利用して,通常数字・7seg数字の認識テストアプリをjavaで作成します。
テストアプリは,googleの推奨する16 KB page-sizeに対応します。
tesseractAPIはOCR(文字認識)ライブラリで,
https://github.com/adaptech-cz/Tesseract4Android
で開発が継続,維持されていまして,tesseract4.9.0(202509現在)として公開されています。
(READMEの先頭に[Jitpack 4.9.0]の表記があります。)
また4.8.0以降はgoogleの推奨する16 KB page-sizeに対応しています。
(https://github.com/adaptech-cz/Tesseract4Android/issues/80)
tesseractAPIでは文字を学習させたtraineddataファイルを使います。
ここでは通常の英数文字セットのための"eng.tesseract"と"7seg.tesseract"の2つのファイルを使用しています。
テストアプリでは,tesseractAPIにテスト画像のbitmapを与えて,認識ボタンで文字認識を起動し,認識結果文字列を画面に表示させます。
2 使用環境
- Windows 11 64-bit
- Android Studio Narwhal Feature Drop | 2025.1.2 Patch 2
Build #AI-251.26094.121.2512.13991807, built on August 26, 2025
Runtime version: 21.0.6+-13391695-b895.109 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Toolkit: sun.awt.windows.WToolkit
- java (JavaVersion.VERSION_11)
- Build Target 35 (24以降で動作検証済)
3 AndroidStudioでの準備作業
(1) 新しいprojectを作る
(2) Gradle Scriptsの設定
(3) テスト用画像ファイルの埋め込み
(4) layout定義用ファイルの上書き
(5) traineddataファイルの取得と埋め込み
使用する主要ファイルのダウンロード
3.1 新しいprojectを作る
・(ファイルメニュー File -> New -> )New Project --> Empty Views Activity
・New Project の名前は testTesseract4 とします。(別の名前でもOK)
・AppおよびOPenCVモジュールでjavaを使用するので,
Language → Java
Build configuration language → Groovy DSL(build.gradle)
とします。
・パッケージ名は「jp.gr.java_conf.coskx.testtessaract4」(この文書での)です。デフォルトのパッケージ名のままでOKです。
・他の設定はそのまま -> Finish
3.2 Gradle Scriptsの設定
(1) settings.gradle
dependencyResolutionManagement {}中の
repositories {}中に次の1行を追加します。
maven { url 'https://jitpack.io' }
(このことはgithub tesseract4androidのREADME中に記載があります。)
(2) build.gradle(Module:app)
dependencies {}中に次の1行を追加します。
implementation libs.cz.tesseract4android // standard flavor
(dependenciesへの追加に関しては,github tesseract4androidのREADME中に記載がありますが,README中の記載通りにStandard variantで記述しても,上記のように変更を促されます。)
(3) libs.versions.toml
[versions]中に次の1行を追加します。
tesseract4androidVersion = "4.9.0"
[libraries]中に次の1行を追加します。
cz-tesseract4android = { module = "cz.adaptech.tesseract4android:tesseract4android", version.ref = "tesseract4androidVersion" }
上記"4.9.0"のところは,公開バージョンが更新された場合,変更を促されます。
(ここの記述は追記しなくても,androidstudioが自動生成するかもしれません。)
3.3 テスト用画像ファイルの埋め込み
テスト用画像の提示方法はいろいろありますが,テストするだけなので,リソースとして与えることにしました。
ダウンロードしたtestTesseract4files.zip中の
sample_image.jpg
を app>res>drawableに入れてください。
3.4 layout定義ファイルの上書き
ダウンロードしたtestTesseract4files.zip中の
activity_main.xml
をapp>res>layoutに入れて,上書きしてください。
3.5 traineddataファイルの取得と埋め込み
通常数字,7seg数字の認識を行いたいため,英数用ファイル(eng.trainesdata)と7seg文字用ファイル(7seg.traineddata)をそれぞれダウンロードし,assetsに埋め込みます。
eng.traineddataは次のURLからダウンロードします。
https://github.com/tesseract-ocr/tessdata/tree/4.0.0
7seg.traineddataは次のURLからダウンロードします。
https://github.com/Shreeshrii/tessdata_ssd
★ダウンロードに当たっては,当該xxx.traineddataを開いて,ダウンロードボタン(Download raw file)でダウンロードしてください。それぞれ20MB,10MBほどのファイルです。ファイルサイズを確認してください。
取り出せた2つのファイルはフォルダassetsに保存します。
フォルダassetsは,標準では作られていないため,作成します。
場所はapp直下です。
左のandroid構成一覧で,appのアイコン上で右クリック>new>directoryで作成します。(出てきたフォルダ候補中で src\main\assets を選びます。)

フォルダassets内にフォルダtessdataを作って,その中にeng.traineddataと7seg.traineddataを埋め込みます。
次のように出来たら成功です。

4 MainActivity.java
MainActivity.javaは,ダウンロードしたtestTesseract4files.zip中に入っています。
tesseractAPIの利用では,tesseractAPIを初期化して,tesseractAPIに画像bitmapを与えて,tesseractAPIから認識結果を得るだけですので,面倒はありません。
しかしtraineddataファイルの扱いが面倒です。
androidアプリでは,assets領域に必要なファイル(traineddataファイル)をおいてコンパイルするため,出来たアプリでは,assets領域にファイルが置かれたままになっています。
実行時に,assets領域のファイル(traineddataファイル)をアプリ固有の内部ストレージにコピーしておき,tesseractAPI初期化の際に固有の内部ストレージにあるtraineddataファイルを与えるようになります。
そのため,onCreate()内で
(1)TessBaseAPIのインスタンスtessを生成
tess = new TessBaseAPI();
(2)assets領域のファイル(traineddataファイル)をアプリ固有の内部ストレージにコピー
setReadyTrainedFile();
(3)tesseractAPI初期化の際に固有の内部ストレージにあるtraineddataファイルを与える
initTessWithTrainedFile();
を行っています。
なお,TessBaseAPIのインスタンスtessを生成直後に
TessBaseAPIのversionを取得したら5.5.1となっていました。4.9.0のはずではなかったのかな?
途中で,使用しているパスを確認のため表示しています。
アプリ固有の内部ストレージのパスは
「...../(パッケージ名)/files」
となっています。
traineddataファイルを置くためのパスは
「...../(パッケージ名)/files/tessdata」
となっています。「tessdata」の名前はこの名前で固定です。
TessBaseAPI init()では
アプリ固有の内部ストレージのパス(「...../(パッケージ名)/files」)と,traineddataの接頭語の「eng」と「7seg」を与えます。「eng+7seg」で両方使えという意味になります。
TessBaseAPI setPageSegMode()では,認識モードを与えることができます。
TessBaseAPI setVariable()では,ホワイトリストを与えることができます。ホワイトリストを与えると,数字に限った認識を行う場合にはご認識が少なくなります。
MainActivity.java
package jp.gr.java_conf.coskx.testtessaract4;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.googlecode.tesseract.android.TessBaseAPI;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
TessBaseAPI tess;
final String TAG = "MainActivity";
private Button button1;
private TextView textview1;
private Bitmap bitmap1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
Log.i(TAG,"onCreate()");
//Layout
button1 = findViewById(R.id.button1);
button1.setOnClickListener(this);
textview1 = findViewById(R.id.textview1);
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.sample_image);
ImageView imageview1 = findViewById(R.id.imageview1);
imageview1.setImageBitmap(bitmap1); //テスト用imageを表示
// TessBaseAPIインスタンスの作成
tess = new TessBaseAPI();
Log.i(TAG,"onCreate() new TessBaseAPI done");
String tessversion = tess.getVersion();
Log.i(TAG,"onCreate() TessBaseAPI version = " + tessversion);
//Assetsからアプリ固有の内部storageへのファイルコピー
setReadyTrainedFile();
Log.i(TAG,"onCreate() setReadyTrainedFile() done");
//tesseractAPIに学習ファイル(traineddata)を与えて初期化します
initTessWithTrainedFile();
Log.i(TAG,"onCreate() initTessWithTrainedFile() done");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG,"onResume()");
textview1.setText("Hello, world!");
}
@Override
public void onPause() {
super.onPause();
Log.i(TAG,"onPause()");
}
@Override
public void onDestroy() {
super.onDestroy();
tess.recycle();
}
public void onClick(View view) {
if (view == button1) {
Log.i(TAG,"onClick() view == button1");
String recognizedText= "no data";
//initTessWithTrainedFile();
//Log.i(TAG,"onClick() initTessWithTrainedFile() done");
tess.setImage(bitmap1);
recognizedText = tess.getUTF8Text();
textview1.setText(recognizedText);
Log.i(TAG,"onClick() recognizedText = [" + recognizedText +"]");
}
}
public void setReadyTrainedFile() {
AssetManager assetManager = getAssets();
/* 確認用
String[] fileList = null;
try {
fileList = assetManager.list("tessdata");
Log.i(TAG,"setReadyTrainedFile() fileList = " + Arrays.toString(fileList));
} catch (IOException e) {
e.printStackTrace();
}
*/
String owndatapath = this.getFilesDir() + "/"; //app specific internal storage path
Log.i(TAG,"setReadyTrainedFile() owndatapath = " + owndatapath);
//owndatapath = /data/user/0/jp.gr.java_conf.coskx.testtessaract4/files/
//traineddataファイルをコピーするためのディレクトリを作成
String[] paths = new String[] { owndatapath, owndatapath + "tessdata/" };
for (String path : paths) {
Log.i(TAG,"setReadyTrainedFile() path = " + path);
File dir = new File(path);
if (!dir.exists()) {
if (!dir.mkdirs()) {
Log.v(TAG, "ERROR: on Creation of directory [" + path + "]");
return;
} else {
Log.v(TAG, "Created directory [" + path + "]");
}
}
}
//traineddataファイルをコピー (eng.traineddataと7seg.traineddata)
copyTessTrainedFile("eng", assetManager, owndatapath);
copyTessTrainedFile("7seg", assetManager, owndatapath);
}
public void initTessWithTrainedFile() {
String owndatapath = this.getFilesDir() + "/"; //app specific internal storage path
/* ファイルが思った場所にあるかどうか確認用です
String ldpass1 = owndatapath + "tessdata/eng.traineddata";
if ((new File(ldpass1)).exists()) {
Log.i(TAG,"initTessWithTrainedFile() exist [" + ldpass1 + "]");
} else {
Log.i(TAG,"initTessWithTrainedFile() NOT exist [" + ldpass1 + "]");
}
String ldpass2 = owndatapath + "tessdata/7seg.traineddata";
if ((new File(ldpass2)).exists()) {
Log.i(TAG,"initTessWithTrainedFile() exist [" + ldpass2 + "]");
} else {
Log.i(TAG,"initTessWithTrainedFile() NOT exist [" + ldpass2 + "]");
}
*/
String tessdataPath = owndatapath; //フォルダ名"tessdata"はinit()内で追加される
Log.i(TAG,"initTessWithTrainedFile() tessdataPath = " + tessdataPath);
// 初期化
// "eng+7seg"はeng.traineddataと7seg.traineddataの2つを指示しています
if (!tess.init(tessdataPath, "eng+7seg")) {
Log.i(TAG,"initTessWithTrainedFile() tess.init failed");
tess.recycle();
return;
}
tess.setPageSegMode(TessBaseAPI.PageSegMode.PSM_SINGLE_BLOCK);
//ホワイトリスト指示
tess.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, "-01:23456789.");
}
//assets/tessdataにあるtraineddataファイルをアプリ固有の内部ストレージにコピーする
//ただし,すでにアプリ固有の内部ストレージ内に当該ファイルが存在する場合はコピーしない
private void copyTessTrainedFile(String lang, AssetManager assetManager, String owndatapath) {
if (!(new File(owndatapath + "tessdata/" + lang + ".traineddata")).exists()) {
try {
//AssetManager assetManager = getAssets();
InputStream in = assetManager.open("tessdata/" + lang + ".traineddata");
//GZIPInputStream gin = new GZIPInputStream(in);
OutputStream out = new FileOutputStream(owndatapath
+ "tessdata/" + lang + ".traineddata");
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
//while ((lenf = gin.read(buff)) > 0) {
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
//gin.close();
out.close();
Log.v(TAG, "Copied " + lang + " traineddata");
} catch (IOException e) {
Log.e(TAG, "Was unable to copy " + lang + " traineddata " + e.toString());
}
} else {
Log.i(TAG, " already the file exists, quit copying " + lang + " traineddata ");
}
}
}
5 実行の様子
実機で実行すると左図のように表示され,ボタンを押すと認識結果が右図のように表示されます。
6 補足
このアプリはtesseractAPIの利用方法を調べるために作成しました。tesseractAPIの認識性能を検証したわけではありません。
検証に当たっては,認識しやすい画像を使用しました。
以前tesseract2を使ってみましたが,7seg文字の認識は苦手だと思っておりました。
その点はまだ解決していないようです。
試しに次のような画像で認識を試みましたが,7seg文字は認識できませんでした。
7seg文字の文字素間の隙間が苦手なようです。黒い部分を膨張させるなどの認識前処理が必要と思います。

https://github.com/adaptech-cz/Tesseract4Android のREADMEは参考になりましたが,tesseractAPI init()へ与えるパスに関しては,プログラムsampleそのままではうまくいきませんでした。
init()内で"tessdata"を付け加えているので,initへ与えるパスに"tessdata"を付け加える必要はありませんでした。
7 まとめ
tesseract4androidのテストアプリを作成し,動作を確認しました。
githubからtesseractAPIを直接利用する方法の説明がなかなか見つからない中で,時間はかかりましたが,目的を達成しました。