質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.99%

  • Android Studio

    3181questions

    Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

  • canvas

    229questions

    HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

  • Tesseract

    24questions

    Tesseractは、Googleが提供しているオープンソースのOCRエンジンです。機械学習があり60以上の言語に対応でき、日本語の文字認識も可能です。さらに精度を上げることもできます。

Canvasでフリーハンドで描いた文字をtess-twoで文字認識

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 324

MasayaUemura

score 3

前提・実現したいこと

javaを扱い始めて2週間ほどの初心者です。
アプリ内でフリーハンドでかいた文字をtess-twoで認識したいのですが、うまく動きません。

発生している問題・エラーメッセージ

tess-twoで認識をおこなった結果が空欄になってしまいます。

なお、コンパイルでエラーはでません。
UIスレッドでの処理が重すぎてフレームが60くらいスキップされますが、処理は行われてる模様です。

該当のソースコード

メインクラス

package com.example.***.***

import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.googlecode.tesseract.android.TessBaseAPI;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


public class MainActivity extends AppCompatActivity {

    String datapath = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //描画とクリアボタン
        Button button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                PaintView paintView = (PaintView) findViewById(R.id.drew);
                paintView.clear();
                TextView textView = (TextView)findViewById(R.id.textView);
                textView.setText("");
            }
        });

        //認識ボタン
        Button button2 = (Button) findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                View drewview = findViewById(R.id.drew);      //描いた絵をdrewviewとして読み込む
                drewview.setDrawingCacheEnabled(true);        // キャッシュを取得する設定にする
                drewview.destroyDrawingCache();               // 既存のキャッシュをクリアする
                Bitmap bmp = drewview.getDrawingCache();      //drewviewをbitmap bmpに変換
                bmp = bmp.copy(Bitmap.Config.ARGB_8888, true);//ARGB_8888に合わせる


                final String language = "kor";                //tess-two言語の選択
                datapath = getFilesDir() + "/tesseract/";     //directoryの指定
                checkFile(new File(datapath + "tessdata/"));  //学習データの有無チェック

                TessBaseAPI tessOCRAPI = new TessBaseAPI();   //Init OCR 
                tessOCRAPI.init(datapath, language);

                tessOCRAPI.setImage(bmp);                     //bmp型にした描いた絵をセットする

                String APIkekka;
                APIkekka = tessOCRAPI.getUTF8Text();          //OCRの結果をString型のAPIkekkaに
                tessOCRAPI.end();                             // Close OCR API

                TextView kekka = (TextView) findViewById(R.id.textView);
                kekka.setText(APIkekka+ "が結果です");         //APIkekkaをtextviewに表示
                Log.d(APIkekka, "結果表示");                   //Logに結果表示

                ImageView madebmp = (ImageView)findViewById(R.id.imageView);     //bmpが正常に取得できているか画面に出力
                madebmp.setImageBitmap(bmp);
                }
            });


        }



    private void checkFile(File dir) {
        if (!dir.exists()&& dir.mkdirs()){
            copyFiles();
        }
        if(dir.exists()) {
            String datafilepath = datapath+ "/tessdata/kor.traineddata";
            File datafile = new File(datafilepath);

            if (!datafile.exists()) {
                copyFiles();
            }
        }
    }

    private void copyFiles() {
        try {
            String filepath = datapath + "/tessdata/kor.traineddata";
            AssetManager assetManager = getAssets();

            InputStream instream = assetManager.open("tessdata/kor.traineddata");
            OutputStream outstream = new FileOutputStream(filepath);

            byte[] buffer = new byte[1024];
            int read;
            while ((read = instream.read(buffer)) != -1) {
                outstream.write(buffer, 0, read);
            }


            outstream.flush();
            outstream.close();
            instream.close();

            File file = new File(filepath);
            if (!file.exists()) {
                throw new FileNotFoundException();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

PaintView.java

public class PaintView extends View {

    private Paint paint;
    private Path path;

    public PaintView(Context context){
        this(context,null);
    }

    public PaintView(Context context, AttributeSet attrs) {
        super(context, attrs);
        path = new Path();
        paint = new Paint();
        paint.setColor(0xFF000000);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(10);

    }
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(path, paint);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.moveTo(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                path.lineTo(x, y);
                invalidate();
                break;
        }
        return true;
    }
    public void clear(){
        path.reset();
        invalidate();
    }
}

Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.***.***.MainActivity">

    <view
        class="com.example.***.***.PaintView.java
        android:id="@+id/drew"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_width="203dp"
        android:layout_height="204dp"
        app:layout_constraintHorizontal_bias="0.029"
        app:layout_constraintVertical_bias="0.033"/>
        <!--android:background="@drawable/flame_style"-->

    <Button
        android:id="@+id/button1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="clear"
        app:layout_constraintHorizontal_bias="0.179"
        app:layout_constraintVertical_bias="0.74" />

    <Button
        android:id="@+id/button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="認識"
        app:layout_constraintVertical_bias="0.74"
        app:layout_constraintHorizontal_bias="0.81" />

    <TextView
        android:id="@+id/textView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:text=""
        android:textSize="40sp"
        android:gravity="center_horizontal"
        android:background="@drawable/flame_style"
        tools:layout_editor_absoluteX="55dp"
        app:layout_constraintVertical_bias="0.91" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="202dp"
        android:layout_height="185dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/flame_style"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintVertical_bias="0.519" />
</android.support.constraint.ConstraintLayout>

試したこと

Viewにフリーハンドで書いた文字をView全体ごとキャッシュを利用してキャプチャしてそれをBitmap型に格納し、tess-twoに引数として渡しているので、どっかでミスってBitmap型に格納した画像が真っ白になってるのかな?と思い、格納している画像も認識結果と一緒に表示するようにしたところ、画像はしっかりかいたものがそのままでてきましたのでキャッシュに失敗してるということはなさそうです。

適当な画像をdrawableにいれてその画像をtess-twoで認識させた場合はうまく結果が返ってきます。

補足情報(言語/FW/ツール等のバージョンなど)

MacOS X 10.11.6
Android studio ver 2.3.3
最小JDK API23:Android6.0
コンパイル ツール ver26.0.2
実機はAndroid6.0です。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

私もtesseract/tess-two をAndroidで使ってみたいと考えていましたので、ご質問の件、調べました。PaintView.javaの onDrawで、PaintViewの背景色を単色で塗りつぶすことで文字認識ができるようになりました。

public PaintView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setBackgroundColor(Color.WHITE);
        //this.setBackgroundColor(Color.LTGRAY);
        path = new Path();
        paint = new Paint();
        paint.setColor(0xFF000000);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(10);
    }

原因としては、以下のコードでViewのビットマップをアルファチャネル付きで
取得していた為に、手書きのストロークの背景が透過色になってしまい、
tessOCRAPI.setImage(bmp)で認識できなかったようです。

  View drewview = findViewById(R.id.drew);      //描いた絵をdrewviewとして読み込む
                drewview.setDrawingCacheEnabled(true);        // キャッシュを取得する設定にする
                drewview.destroyDrawingCache();               // 既存のキャッシュをクリアする
                Bitmap bmp = drewview.getDrawingCache();      //drewviewをbitmap bmpに変換
                bmp = bmp.copy(Bitmap.Config.ARGB_8888, true);//ARGB_8888に合わせる


MasayaUemura様の提示されたコードをもとに作成したテストプロで認識させた結果が以下になります。
手書き用のキャンバス部分の背景色は分かり易くする為にthis.setBackgroundColor(Color.LTGRAY);
で灰色としています。

イメージ説明
手書きの"ABC 123"が文字認識され、意図したものとは違うものの、
結果が出ているのが分かります。

また、traineddataはkor(韓国語版)では無く、eng(英語版)にしているのが
異なります。korに変えても認識できると思いますが、できるかできないかは
精度の問題だと思いましたので私の方では試していません。

"kor"での確認と、適切な修正について追記

"kor"の韓国語traineddataでハングル文字も認識できることを確認しました。
また、PaintView#onDraw内でsetBackgroundColorによって背景色を
セットする修正をしましたが、そうではなく、レイアウトxmlで背景色をセットする方が
適切かもしれません。

<!--抜粋-->
      class="examples.products.test25.PaintView"
        android:id="@+id/drew"
        android:background="#ffffff"

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/16 13:41

    質問後にいろいろ試していたところ、回答でおっしゃっている通り背景色が問題であることがわかりました。
    xmlで直接背景色を設定したところうまく認識できました。ありがとうございました。

    キャンセル

  • 2017/11/16 14:01 編集

    コメントありがとうございます。その後が気になっていましたが、動くようになって良かったです。私も勉強になりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Android Studio

    3181questions

    Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

  • canvas

    229questions

    HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

  • Tesseract

    24questions

    Tesseractは、Googleが提供しているオープンソースのOCRエンジンです。機械学習があり60以上の言語に対応でき、日本語の文字認識も可能です。さらに精度を上げることもできます。