java 버전은 https://stickode.tistory.com/135 에서 확인해주세요
안녕하세요~
오늘은 OCR(Optical character recognition) 기능을 구현해 보겠습니다. OCR는 인간이 종이 위에 써 놓은 글씨를 인지하여 텍스트 데이터로 바꿔주는 기능을 말해요. 이 포스팅에서는 스틱코드를 사용하여 글자가 들어 있는 이미지에서 글자를 인식하는 간단한 앱을 만들어보겠습니다.
# 환경 세팅
1. 라이브러리 추가
▶ 해당 기능을 사용하기 위해서는 tess-two라는 모듈이 필요합니다. 위 그림처럼 build.gaddle(moudle) 파일을 열어서 맨 아래 한 줄을 추가하고 동기화시켜줍니다. 최신 버전은 여기서 확인하실 수 있습니다.
2. Language Data 추가
▶ 인식할 언어 데이터도 추가해줍니다. 영어와 한글을 인식해서 텍스트로 옮기기 위해 두 가지 언어 데이터를 추가해줬습니다. 언어 데이터는 여기에서 다운로드할 수 있습니다.
assets 폴더가 없으신 분들은 [우클릭] -> [New] -> [Forder] -> [Asset Folder]를 만드시면 됩니다.
바로 붙여넣기가 안되시는 분은 폴더를 찾아가시면 되는데요. 폴더 경로는 아래 사진과 같이 경로를 얻어 폴더를 직접 여셔서 파일을 붙여넣으시면 됩니다.
3. 샘플 이미지 추가
▶ 영어와 한글을 각각 테스트해보기 위해 샘플 이미지를 준비했습니다.
# 레이아웃 그리기
▶ 분석할 이미지를 띄워주는 화면과 OCR을 동작시키는 버튼, 그리고 결과를 출력하는 화면을 만들어 줍니다. 아래는 레이아웃 전체 소스입니다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.stickcodeocr.MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="250dp"
android:background="#ffffff"
android:id="@+id/ImageContainer">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/imageView"
android:src="@drawable/sample_eng"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="42dp"
android:layout_below="@id/ImageContainer"
android:clickable="true"
android:onClick="processImage"
android:background="#166e78"
android:id="@+id/OCRButtonContainer">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RUN OCR"
android:textSize= "18dp"
android:textColor="#ffffff"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:clickable="true"
android:onClick="processImage"/>
</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="15dp"
android:textColor="#169cdf"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:background="#fff"
/>
</RelativeLayout>
</RelativeLayout>
# OCR 기능 구현
MainActivity.java
▶ 필요한 샘플 이미지 파일과 언어 데이터를 OCR 모듈에 세팅해줍니다. 그리고 MainActivity 아래쪽에 스틱코드를 이용해서 OCR 동작 기능을 추가해줍니다.
▶ 이제 불러온 코드에 OCR 결과를 보여줄 뷰에 연결시켜 줍니다.
▶ 전체 코드입니다.
MainActivity.java
import com.googlecode.tesseract.android.TessBaseAPI
import android.graphics.BitmapFactory
import android.os.Bundle
import android.widget.TextView
import android.graphics.Bitmap
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import java.io.*
class MainActivity : AppCompatActivity() {
var image //사용되는 이미지
: Bitmap? = null
private var mTess //Tess API reference
: TessBaseAPI? = null
var datapath = "" //언어데이터가 있는 경로
var OCRTextView // OCR 결과뷰
: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
OCRTextView = findViewById(R.id.OCRTextView)
//이미지 디코딩을 위한 초기화
image = BitmapFactory.decodeResource(resources, R.drawable.sample_kor) //샘플이미지파일
val imageView: ImageView = findViewById(R.id.imageView);
imageView.setImageBitmap(image)
//언어파일 경로
datapath = "$filesDir/tesseract/"
//트레이닝데이터가 카피되어 있는지 체크
checkFile(File(datapath + "tessdata/"))
//Tesseract API 언어 세팅
val lang = "kor"
//OCR 세팅
mTess = TessBaseAPI()
mTess!!.init(datapath, lang)
}
/***
* 이미지에서 텍스트 읽기
*/
fun processImage(view: View?) {
var OCRresult: String? = null
mTess!!.setImage(image)
OCRresult = mTess!!.utF8Text
OCRTextView!!.text = OCRresult
}
/***
* 언어 데이터 파일, 디바이스에 복사
*/
// 언어 파일 이름
private val langFileName = "kor.traineddata"
private fun copyFiles() {
try {
val filepath = datapath + "tessdata/" + langFileName
val assetManager = assets
val instream: InputStream = assetManager.open(langFileName)
val outstream: OutputStream = FileOutputStream(filepath)
val buffer = ByteArray(1024)
var read: Int
while (instream.read(buffer).also { read = it } != -1) {
outstream.write(buffer, 0, read)
}
outstream.flush()
outstream.close()
instream.close()
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
/***
* 디바이스에 언어 데이터 파일 존재 유무 체크
* @param dir
*/
private fun checkFile(dir: File) {
//디렉토리가 없으면 디렉토리를 만들고 그후에 파일을 카피
if (!dir.exists() && dir.mkdirs()) {
copyFiles()
}
//디렉토리가 있지만 파일이 없으면 파일카피 진행
if (dir.exists()) {
val datafilepath = datapath + "tessdata/" + langFileName
val datafile = File(datafilepath)
if (!datafile.exists()) {
copyFiles()
}
}
}
}
# 테스트
1. 영어
2. 한글
▶ 한글의 경우 정확도가 아쉬웠지만, 영어 / 한글 모두 정상적으로 동작하는 걸 확인할 수 있었습니다.
'안드로이드 코틀린' 카테고리의 다른 글
[Kotlin][Android] AAC ViewModel + LiveData+ Databinding 이용 숫자 증감 (0) | 2022.04.11 |
---|---|
[Android][kotlin] EditText Submit처럼 사용하기 (0) | 2022.04.06 |
[Kotlin][Android] 키보드 보이기/숨기기 (0) | 2022.03.20 |
[Kotlin][Android] Bottom Sheet Dialog (0) | 2022.03.16 |
[Kotlin][Android] 글씨가 흐르는 텍스트뷰 만들기 (0) | 2022.03.11 |