주니어개발자_ฅʕ•̫͡•ʔฅ

Android 카메라 연동 및 텍스트 추출 하기 (Android Studio, JAVA) 본문

AndroidStudio

Android 카메라 연동 및 텍스트 추출 하기 (Android Studio, JAVA)

뚜비뚜밥_98 2021. 9. 15. 12:47

카메라로 텍스트가 포함된 사진을 찍고

그 사진에서 텍스트를 추출하고자 함

마지막에 모든 소스코드 첨부

 

앱 실행 순서

1. 사진찍기 버튼을 눌러 카메라를 킴

2. 켜진 카메라에서 사진을 찍음

3. 사진찍은 즉시 원래 화면으로 돌아옴

4. 찍은 사진을 화면에 표시

5. OCR 버튼 클릭시 사진속의 텍스트 추출

 

먼저 카메라를 이용하여 사진을 찍고 가져오는 부분은

카메라로 사진찍은 후 가져오기

 

[Android/안드로이드] 카메라로 사진 찍어 가져오기 (get photo from camera)

갤러리나 카메라로 사진을 가져오는 기능을 구현해야 할 때가 많다. 상당히 많이 쓰이는 기능이라 자료가 많지만, 각종 블로그들을 따라하다보면 Cursor 부분에서 NullPointerException이 뜬다. 뭘 덜

raon-studio.tistory.com

위의 사이트를 참고하였다.

설명이 순서대로 세세하게 쓰여져 있어서

직접 들어가서 보는 것을 추천한다.

 

이해가 안되는 부분이 있다면 부가적인 설명을 여기에 쓰겠지만

그마저도 필요없을 정도로 친절하게 설명하고 있음

 

위의 과정이 끝났다면

사진을 이용하여 텍스트 추출을 진행하면 된다

 

먼저 밑에 표시한 곳(Gradle → Module)에 들어가서

depenencies 안에 OCR 라이브러리인 밑의 코드를 추가해준다.

compile 'com.rmtheis:tess-two:5.4.1'

 

그리고 밑에와 같은 assets파일에 언어 데이터를 추가해 주는데

 

assets 파일이 없다면 그림과 같이 추가해 주면 된다.

 

그리고 assets 파일에 directory를 만들어

그 안에 언어데이터를 추가해 주면 된다.

assets의 디렉토리 명 : tessdata(어떻게 지정해도 상관 없음)

한글 언어 데이터 다운

영어 언어 데이터 다운

언어 데이터 리스트

 

언어 데이터 리스트 링크로 들어가면

한글과 영어를 포함한 다른 언어들도 몇가지 있으니

비슷한 방식으로 참고하여 사용하면 된다

 

언어 데이터 다운은 해당 페이지에 들어가서 밑에와 같이

다운로드 버튼을 눌러주면 된다.

다운로드 된 파일을 assets / tessdata 안에 넣어주는데

위의 파일을 복사하거나 잘라내기 하여

앱 프로젝트가 저장된 위치로 찾아가서 넣어주면 된다.

 

경로 : 프로젝트(본인의 프로젝트 명 : OCRTest) / app / src / main / assets / tessdata

준비는 여기까지 이며

나머지는 소스코드 부분이다.

 

먼저 레이아웃은

사진 찍는 버튼, 사진 보여주는 이미지뷰, 텍스트 추출 버튼, 추출된 텍스트 표시할 텍스트뷰

이렇게 4개로 구성 했다.

 

 activity_main.xml  (레이아웃)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:background="#ffffff"
        android:id="@+id/imageContainer">

        <!--    사진 찍는 버튼    -->
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TAKE PICTURE"
            android:textSize= "18dp"
            android:id="@+id/takePicture"/>

        <!--    사진 보여주는 이미지뷰    -->
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/imageView"
            />

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="42dp"
        android:layout_below="@id/imageContainer"
        android:clickable="true"
        android:id="@+id/OCRButtonContainer">

        <!--    텍스트 추출 버튼    -->
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="RUN OCR"
            android:textSize= "18dp"
            android:id="@+id/ocrButton"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true"/>

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/OCRButtonContainer"
        android:padding="10dp">

        <!--    텍스트 추출 뷰    -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="이곳에 결과 출력"
            android:id="@+id/OCRTextView"
            android:textSize="20dp"
            android:textColor="#169cdf"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true"
            android:background="#fff"
            />
    </RelativeLayout>

</RelativeLayout>

 

MainActivity.java (class)

import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap;

import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.media.ExifInterface;

import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;

import android.provider.MediaStore;
import android.support.v4.content.FileProvider;
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;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

    Bitmap image; //사용되는 이미지
    private TessBaseAPI mTess; //Tess API reference
    String datapath = "" ; //언어데이터가 있는 경로

    Button btn_picture; //사진 찍는 버튼
    Button btn_ocr; //텍스트 추출 버튼

    private String imageFilePath; //이미지 파일 경로
    private Uri p_Uri;

    static final int REQUEST_IMAGE_CAPTURE = 672;

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

        btn_picture = (Button)findViewById(R.id.takePicture);
        btn_ocr = (Button)findViewById(R.id.ocrButton);

        //언어파일 경로
        datapath = getFilesDir()+ "/tesseract/";

        //트레이닝데이터가 카피되어 있는지 체크
        checkFile(new File(datapath + "tessdata/"), "kor");
        checkFile(new File(datapath + "tessdata/"), "eng");


        /**
       	  * Tesseract API
      	  * 한글 + 영어(함께 추출)
       	  * 한글만 추출하거나 영어만 추출하고 싶다면
       	  * String lang = "eng"와 같이 작성해도 무관 
          **/
        String lang = "kor+eng"; 

        mTess = new TessBaseAPI();
        mTess.init(datapath, lang);


		// 사진 찍는 버튼 클릭시 카메라 킴
        btn_picture.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){
                sendTakePhotoIntent();
            }
        });

		// 텍스트 추출 버튼
        btn_ocr.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){

				// 가져와진 사진을 bitmap으로 추출
                BitmapDrawable d = (BitmapDrawable)((ImageView) findViewById(R.id.imageView)).getDrawable();
                image = d.getBitmap();

                String OCRresult = null;
                mTess.setImage(image);
                
                //텍스트 추출
                OCRresult = mTess.getUTF8Text();
                TextView OCRTextView = (TextView) findViewById(R.id.OCRTextView);
                OCRTextView.setText(OCRresult);
            }
        });
    }

    private int exifOrientationToDegrees(int exifOrientation) {
        if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
            return 90;
        } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
            return 180;
        } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
            return 270;
        }
        return 0;
    }

    private Bitmap rotate(Bitmap bitmap, float degree) {
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    private void sendTakePhotoIntent(){

        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                photoFile = createImageFile();
            } catch (IOException ex) {
                // Error occurred while creating the File
            }

            if (photoFile != null) {
                p_Uri = FileProvider.getUriForFile(this, getPackageName(), photoFile);

                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, p_Uri);
                startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
            ((ImageView) findViewById(R.id.imageView)).setImageURI(p_Uri);
            ExifInterface exif = null;

            Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath);
            try {
                exif = new ExifInterface(imageFilePath);
            } catch (IOException e) {
                e.printStackTrace();
            }

            int exifOrientation;
            int exifDegree;

            if (exif != null) {
                exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
                exifDegree = exifOrientationToDegrees(exifOrientation);
            } else {
                exifDegree = 0;
            }
            ((ImageView)findViewById(R.id.imageView)).setImageBitmap(rotate(bitmap, exifDegree));
        }
    }

    private File createImageFile() throws IOException {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "TEST_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(
                imageFileName,      /* prefix */
                ".jpg",         /* suffix */
                storageDir          /* directory */
        );
        imageFilePath = image.getAbsolutePath();
        return image;
    }

    //장치에 파일 복사
    private void copyFiles(String lang) {
        try{
            //파일이 있을 위치
            String filepath = datapath + "/tessdata/"+lang+".traineddata";

            //AssetManager에 액세스
            AssetManager assetManager = getAssets();

            //읽기/쓰기를 위한 열린 바이트 스트림
            InputStream instream = assetManager.open("tessdata/"+lang+".traineddata");
            OutputStream outstream = new FileOutputStream(filepath);

            //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();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //check file on the device
    private void checkFile(File dir, String lang) {
        //디렉토리가 없으면 디렉토리를 만들고 그후에 파일을 카피
        if(!dir.exists()&& dir.mkdirs()) {
            copyFiles(lang);
        }
        //디렉토리가 있지만 파일이 없으면 파일카피 진행
        if(dir.exists()) {
            String datafilepath = datapath+ "/tessdata/"+lang+".traineddata";
            File datafile = new File(datafilepath);
            if(!datafile.exists()) {
                copyFiles(lang);
            }
        }
    }
}

결과는 이렇게 나왔다

두 번째 사진을 보면 하트와 같이 이모티콘이 있는 부분에서는 오류가 생기지만

첫 번째 사진처럼 이모티콘이 없이 글자만 있는 경우엔 잘 나온다. 

 

 

 

이 외에 사진을 찍을때 흐리거나

글씨가 작으면

인식이 잘 안되기도 하며

여러차례 테스트해 본 결과

한글보다 영어 인식률이 더 좋은 것 같다.

 

OCR 참고 사이트

 

[JAVA][Android] 안드로이드 OCR 기능 만들기

안녕하세요~ 오늘은 OCR(Optical character recognition) 기능을 구현해 보겠습니다. OCR는 인간이 종이 위에 써 놓은 글씨를 인지하여 텍스트 데이터로 바꿔주는 기능을 말해요. 이 포스팅에서는 스틱코드

stickode.tistory.com