OCR Android application that is using Tesseract

2019-01-19 23:33发布

问题:

I'm trying to build an OCR application on Android using Tesseract, but when I save the picture the application crushes.

I've build the photo capture with the tutorial Simple Android Photo Capture and the OCR function with the guide Making a Simple OCR Android App using Tesseract.

This is the code I'm using:

package com.mmm.pitter;

import java.io.File;
import java.io.IOException;

import com.mmm.pitter.R;
import com.googlecode.tesseract.android.*;
import com.googlecode.leptonica.android.*;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class PitterActivity extends Activity
{
    protected Button _button;
    protected ImageView _image;
    protected TextView _field;
    protected String _path;
    protected boolean _taken;

    protected static final String PHOTO_TAKEN = "photo_taken";

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        _image = ( ImageView ) findViewById( R.id.image );
        _field = ( TextView ) findViewById( R.id.field );
        _button = ( Button ) findViewById( R.id.button );
        _button.setOnClickListener( new ButtonClickHandler() );

        _path = Environment.getExternalStorageDirectory() + "/images/make_machine_example.jpg";
    }

    public class ButtonClickHandler implements View.OnClickListener
    {
        public void onClick( View view ){
            Log.i("MakeMachine", "ButtonClickHandler.onClick()" );
            startCameraActivity();
        }
    }

    protected void startCameraActivity()
    {
        Log.i("MakeMachine", "startCameraActivity()" );
        File file = new File( _path );
        Uri outputFileUri = Uri.fromFile( file );

        Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE );
        intent.putExtra( MediaStore.EXTRA_OUTPUT, outputFileUri );

        startActivityForResult( intent, 0 );
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        Log.i( "MakeMachine", "resultCode: " + resultCode );
        switch( resultCode )
        {
            case 0:
                Log.i( "MakeMachine", "User cancelled" );
                break;

            case -1:
                try {
                    onPhotoTaken();
                }
                catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
        }
    }

    protected void onPhotoTaken() throws IOException
    {
        Log.i( "MakeMachine", "onPhotoTaken" );

        _taken = true;

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 4;

        Bitmap bitmap = BitmapFactory.decodeFile( _path, options );

        _image.setImageBitmap(bitmap);

        _field.setVisibility( View.GONE );

        //_path = path to the image to be OCRed
        ExifInterface exif = new ExifInterface(_path);
        int exifOrientation = exif.getAttributeInt(
                ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);

        int rotate = 0;

        switch (exifOrientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                rotate = 90;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                rotate = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                rotate = 270;
                break;
        }

        if (rotate != 0) {
            int w = bitmap.getWidth();
            int h = bitmap.getHeight();

            // Setting pre rotate
            Matrix mtx = new Matrix();
            mtx.preRotate(rotate);

            // Rotating Bitmap & convert to ARGB_8888, required by tess
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, false);
            bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        }

        TessBaseAPI baseApi = new TessBaseAPI();
        // DATA_PATH = Path to the storage
        // lang for which the language data exists, usually "eng"
        baseApi.init(""sdcard/tesseract/tessdata", "eng");
        baseApi.setImage(bitmap);
        String recognizedText = baseApi.getUTF8Text();
        System.out.println(recognizedText);
        baseApi.end();
    }

    @Override
    protected void onRestoreInstanceState( Bundle savedInstanceState){
        Log.i( "MakeMachine", "onRestoreInstanceState()");
        if( savedInstanceState.getBoolean( PitterActivity.PHOTO_TAKEN ) ) {
            try {
                onPhotoTaken();
            }
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onSaveInstanceState( Bundle outState ) {
        outState.putBoolean( PitterActivity.PHOTO_TAKEN, _taken );
    }
}

And this is the log:

10-13 23:13:51.191: I/MakeMachine(29787): ButtonClickHandler.onClick()
10-13 23:13:51.191: I/MakeMachine(29787): startCameraActivity()
10-13 23:13:51.851: D/CLIPBOARD(29787): Hide Clipboard dialog at Starting input: finished by someone else... !
10-13 23:13:51.866: W/IInputConnectionWrapper(29787): showStatusIcon on inactive InputConnection
10-13 23:14:07.431: I/MakeMachine(29787): onRestoreInstanceState()
10-13 23:14:07.431: I/MakeMachine(29787): resultCode: -1
10-13 23:14:07.431: I/MakeMachine(29787): onPhotoTaken
10-13 23:14:07.431: I/System.out(29787): Not a DRM File, opening notmally
10-13 23:14:07.436: E/JHEAD(29787): can't open
10-13 23:14:07.436: D/dalvikvm(29787): Trying to load lib /data/data/com.mmm.pitter/lib/liblept.so 0x4154e9a0
10-13 23:14:07.436: D/dalvikvm(29787): Added shared lib /data/data/com.mmm.pitter/lib/liblept.so 0x4154e9a0
10-13 23:14:07.446: D/dalvikvm(29787): Trying to load lib /data/data/com.mmm.pitter/lib/libtess.so 0x4154e9a0
10-13 23:14:07.456: D/dalvikvm(29787): Added shared lib /data/data/com.mmm.pitter/lib/libtess.so 0x4154e9a0
10-13 23:14:07.471: D/AndroidRuntime(29787): Shutting down VM
10-13 23:14:07.471: W/dalvikvm(29787): threadid=1: thread exiting with uncaught exception (group=0x40c5b1f8)
10-13 23:14:07.476: E/AndroidRuntime(29787): FATAL EXCEPTION: main
10-13 23:14:07.476: E/AndroidRuntime(29787): java.lang.RuntimeException: Unable to resume activity {com.mmm.pitter/com.mmm.pitter.PitterActivity}: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=0, result=-1, data=null} to activity {com.mmm.pitter/com.mmm.pitter.PitterActivity}: java.lang.IllegalArgumentException: Data path must contain subfolder tessdata!
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2456)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2484)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1998)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3363)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.access$700(ActivityThread.java:127)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1163)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.os.Looper.loop(Looper.java:137)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.main(ActivityThread.java:4507)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at java.lang.reflect.Method.invokeNative(Native Method)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at java.lang.reflect.Method.invoke(Method.java:511)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at dalvik.system.NativeStart.main(Native Method)
10-13 23:14:07.476: E/AndroidRuntime(29787): Caused by: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=0, result=-1, data=null} to activity {com.mmm.pitter/com.mmm.pitter.PitterActivity}: java.lang.IllegalArgumentException: Data path must contain subfolder tessdata!
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.deliverResults(ActivityThread.java:2992)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2443)
10-13 23:14:07.476: E/AndroidRuntime(29787):     ... 13 more
10-13 23:14:07.476: E/AndroidRuntime(29787): Caused by: java.lang.IllegalArgumentException: Data path must contain subfolder tessdata!
10-13 23:14:07.476: E/AndroidRuntime(29787):     at com.googlecode.tesseract.android.TessBaseAPI.init(TessBaseAPI.java:178)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at com.mmm.pitter.PitterActivity.onPhotoTaken(PitterActivity.java:146)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at com.mmm.pitter.PitterActivity.onActivityResult(PitterActivity.java:88)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.Activity.dispatchActivityResult(Activity.java:4649)
10-13 23:14:07.476: E/AndroidRuntime(29787):     at android.app.ActivityThread.deliverResults(ActivityThread.java:2988)
10-13 23:14:07.476: E/AndroidRuntime(29787):     ... 14 more
10-13 23:19:32.376: I/Process(29787): Sending signal. PID: 29787 SIG: 9

回答1:

You need to put the data files in the tessdata directory, and specify the parent directory of tessdata in your init() method:

baseApi.init("/mnt/sdcard/tesseract", "eng"); 


回答2:

baseApi.init(""sdcard/tesseract/tessdata", "eng");

is replaced by

baseApi.init(""sdcard/tesseract/", "eng");

with tessaract folder must include tessdata folder. Because, when you compile, the path will add "tessdat" with the line

File tessdata = new File(datapath + "tessdata");

in the init() function. And why has the slash ("/") at the end of the path, the below comment will help you:

The datapath must be the name of the parent directory of tessdata and must end in / . Any name after the last / will be stripped. The language is (usually) an ISO 639-3 string or null will default to eng. It is entirely safe (and eventually will be efficient too) to call Init multiple times on the same instance to change language, or just to reset the classifier.

You can see at website with the comment for init() function. I hope them will help you.



回答3:

tesseract library is as .zip for windows, as .tar.gz for linux user.

baseApi.init("/mnt/sdcard/tesseract/tessdata/eng.traineddata", "eng");

Please tell me if it runs for you.