개발 환경 : windows 10, Adroid Studio 3.0.1, 갤럭시 노트 8(API 28, Android 9)

 

Tesseract는 구글에서 제공하는 문자 인식 관련 오픈소스입니다.

 

오픈소스이므로 직접 언어 데이터를 개선 및 발전에 직접 참여할 수 있습니다.

지속적으로 최신 버전이 출시되고 있습니다.

 

언어 데이터는 아래 링크에서 다운로드 가능합니다.

깃허브에서 배포 중입니다.

https://github.com/tesseract-ocr/tessdata

 

tesseract-ocr/tessdata

Contribute to tesseract-ocr/tessdata development by creating an account on GitHub.

github.com

 

먼저 안드로이드 프로젝트를 생성하고 Modul.app 단에 아래의 코드를 추가합니다.

dependencies {
	implementation 'com.rmtheis:tess-two:6.3.0'
}

만약 tess의 최신버진이 존재할 경우 이 보다 최신 버전의 코드를 작성해도됩니다.

 

이후 위 링크에서 언어 데이터를 다운로드합니다.

 

저는 kor(한글)과 eng(영어)를 다운로드 했습니다.

다운로드 이후 아래처럼 assets 폴더 아래에 tessdata 폴더 생성 후 방금 다운로드한 언어 데이터를 저장합니다.

 

이후 문자를 인식해 추출할 이미지를 res/drawable 폴더에 아래처럼 저장합니다.

저는 test.png에서 문자를 추출할 것입니다.

 

먼저 아래와 같은 전역변수를 선언합니다.

TessBaseAPI tess;
String dataPath = "";

 

이후 MainActivity에서 동작할 코드를 작성합니다.

먼저 onCreate에서 파일 체크 및 객체 초기화를 수행합니다. 이후 문자 인식을 진행합니다.

주석을 참고하면 됩니다.

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

	// 데이터 경로
        dataPath = getFilesDir()+ "/tesseract/";
        
	// 한글 & 영어 데이터 체크
        checkFile(new File(dataPath + "tessdata/"), "kor");
        checkFile(new File(dataPath + "tessdata/"), "eng");

	// 문자 인식을 수행할 tess 객체 생성
        String lang = "kor+eng";
        tess = new TessBaseAPI();
        tess.init(dataPath, lang);

	//문자 인식 진행
        processImage(BitmapFactory.decodeResource(getResources(), R.drawable.test));
    }

 

 

이후 각 함수를 정의합니다.

// 문자 인식 및 결과 출력
public void processImage(Bitmap bitmap){
        Toast.makeText(getApplicationContext(), "이미지가 복잡할 경우 해석 시 많은 시간이 소요될 수도 있습니다.", Toast.LENGTH_LONG).show();
        String OCRresult = null;
        tess.setImage(bitmap);
        OCRresult = tess.getUTF8Text();
        TextView OCRTextView = (TextView) findViewById(R.id.tv_result);

        OCRTextView.setText(OCRresult);
    }

 

// 파일 복제 
private void copyFiles(String lang) {
        try {
            //location we want the file to be at
            String filepath = dataPath + "/tessdata/" + lang + ".traineddata";

            //get access to AssetManager
            AssetManager assetManager = getAssets();

            //open byte streams for reading/writing
            InputStream inStream = assetManager.open("tessdata/" + lang + ".traineddata");
            OutputStream outStream = new FileOutputStream(filepath);

            //copy the file to the location specified by 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();
        }
    }

 

// 파일 존재 확인
private void checkFile(File dir, String lang) {
        //directory does not exist, but we can successfully create it
        if (!dir.exists()&& dir.mkdirs()){
            copyFiles(lang);
        }
        //The directory exists, but there is no data file in it
        if(dir.exists()) {
            String datafilePath = dataPath+ "/tessdata/" + lang + ".traineddata";
            File datafile = new File(datafilePath);
            if (!datafile.exists()) {
                copyFiles(lang);
            }
        }
    }

 

이제 프로그램을 실행하면 tess 객체에 의해 이미지에서 문자가 추출되고 TextView에 결과가 출력됩니다.

test.png

결과

영어의 인식률과 정확도는 굉장히 높은 편인 것을 볼 수 있습니다.

한글의 경우 인식률과 정확도가 영어에 비해 떨어집니다.

 

하지만 오픈소스이고 개선 참여 및 배포가 가능하니 OCR에 관심이 있는 분들은 아래 링크에서 개선에 참여해보세요.

https://github.com/tesseract-ocr/tesseract

 

tesseract-ocr/tesseract

Tesseract Open Source OCR Engine (main repository) - tesseract-ocr/tesseract

github.com

 

'IT > 안드로이드' 카테고리의 다른 글

안드로이드 팝업 알림  (1) 2017.09.22
안드로이드 동영상 배경화면 만들기  (2) 2017.07.17
안드로이드 ListView 클릭 안될 때  (3) 2017.07.14

안드로이드 배경화면에 팝업 Activtiy로 알림 처리하기

 


이 방법을 사용하면 푸시 알람이 올 때 배경화면에 팝업을 띄움으로써 사용자에게 자세한 정보를 보여줄 수 있게 된다.

 

1.    필요한 클래스 및 xml 파일

1)     Push Receiver : 안드로이드 디바이스에 Push message를 보내는 클래스

2)     Popup Activity : Push message 전송 시 배경화면에 출력되는 팝업 화면을 컨트롤하는 클래스

3)     Layout : 팝업 화면 출력 시 사용자에게 보여지는 UI

4)     Manifest : Activity를 추가하고 launchMode를 설정하기 위한 설정 파일


 

2.    구현

1)     Push Receiver에 아래의 코드를 삽입함으로써 특정 시점에 팝업 화면이 출력되도록 함 :

Intent intent = new Intent(context, PopupActivity.class);
Intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);

부가설명 :

FLAG_ACTIVITY_SINGLE_TOP : 호출되는 Activity가 최상위에 존재할 경우에는 해당 Activity를 다시 생성하지 않고, 존재하던 Activity를 다시 사용함.

FLAG_ACTIVITY_NEW_TASK : 새로운 태스크를 생성하여 그 태스크 안에 Activity를 추가함. 이 코드에서는 Activity가 아닌 곳에서 startActivity()를 하기 위해 사용.

 


2)     PopupActivity에서 필요한 설정을 함 :

+@ : 여기서 버튼 클릭 이벤트를 처리하면 된다.

public class PopupActivity extends Activity {
   
@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);

 

               // 타이틀 바 제거
       
requestWindowFeature(Window.FEATURE_NO_TITLE);

 

               // 설정을 위한 준비
       
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();

 

               // 팝업 화면 출력 시 배경이 검게 되지 않도록 함.
       
layoutParams.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;

 

               // 팝업 화면 출력 시 배경화면 투명도 설정

             layoutParams.dimAmount = 0.7f;

 

               // 설정을 적용함
       
getWindow().setAttributes(layoutParams);
       
setContentView(R.layout.activity_sos_popup);
   
}
}

 

 

3)     Layout : 본인이 원하는대로 커스텀해서 사용


 

4)     Manifest : Activity를 추가하고 launchMode를 설정함.

<activity
 
android:name="PopupActivity"
// 다른 Activity를 무시하고 오직 하나의 Activity만 출력되게 함.
 
android:launchMode="singleInstance" 
   android:screenOrientation="portrait"
   android:theme="@android:style/Theme.Translucent" >
</activity>

 

질문은 댓글이나 방명록에 남겨주세요.

쉽다.

아래의 코드를 복사 붙여넣기만 해도 된다.


준비물 :

res 폴더 -> raw 폴더 생성 -> raw 폴더에 동영상 삽입



VedioView로 화면을 꽉 채운다.

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="fill_parent"
android:layout_height="fill_parent">

<VideoView
android:id="@+id/video_view"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/

</RelativeLayout> 



아래 주석을 참고

MainActivity

package kr.waem.www.videotest;

import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.VideoView;

public class MainActivity extends Activity {
private String TAG = "VideoActivity";
private VideoView videoView;

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

mVideoview = (VideoView) findViewById(R.id.video_view);
//play video
mVideoview.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.wild_life));

mVideoview.start();
//loop
mVideoview.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true); // 동영상 무한 반복. 반복을 원치 않을 경우 false
}
});
}

}


생각보다 간단하게 만들었다.

위 코드만 복사해도 충분히 만들 것 같다.

Adapter를 아래 처럼 바꾼다.

가장 중요한 것은 View.setFocusable(false);와

ImageButton -> ImageView, Button -> TextView라는 것이다.

버튼을 뷰로 바꿔도 클릭이 정상적으로 먹힌다.

클릭만 먹히면 되니 이런 식으로 하는 것이 좋다. // 버튼으로 할 경우 OnClickListener()이 안 먹힘.


예시 코드 :


// getView

@Override


public View getView(int position, View convertView, ViewGroup parent) {

View v = convertView;

if (v == null) {
holder = new ViewHolder();
v = mInflater.inflate(R.layout.list_contacts, null);
holder.mainName = (TextView) v.findViewById(R.id.tv_main_name);
holder.mainFavoritesSwitch = (ImageView) v.findViewById(R.id.iv_main_favorits_switch);
holder.mainCall = (ImageView) v.findViewById(R.id.iv_main_call);
holder.mainMan = (ImageView) v.findViewById(R.id.iv_main_man);
holder.mainName.setFocusable(false);
holder.mainFavoritesSwitch.setFocusable(false);
holder.mainCall.setFocusable(false);
holder.mainMan.setFocusable(false);

v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}

//InfoClass를 생성하여 각 뷰의 포지션에 맞는 데이터를 가져옴
InfoClass info = InfoArr.get(position);

//리스트뷰의 아이템에 맞는 String값을 입력
holder.mainName.setText(info.name);

return v;
}



//ViewHolder

private class ViewHolder {

TextView mainName;

ImageView mainFavoritesSwitch;

ImageView mainCall;

ImageView mainMan;

} 


안드로이드 자체 버그로 보인다.

다른 해결책이 있을 수도 있다.

ImageButton이든 ImageView든 클릭만 먹히면 되니 이렇게 해도 무방하다고 본다.

+ Recent posts