Translate

2017년 4월 13일 목요일

Xamarin(자마린) Android Tesseract OCR 어플 만들기 - 소스코드 및 결과


*  Xamarin(자마린) Android Tesseract OCR 어플 만들기 - 개발 환경설정 에서 이어짐.

Android Studio 개발을 진행중이라 포스팅이 늦었다. 정리가 되면 올릴 예정.



글자 인식 라이브러리인 Tesseract를 사용하기 위해서는 확장자 .traineddata 형식의 언어데이터가 필요하다.

여기서 다운받을 수 있다.
https://github.com/tesseract-ocr/tessdata

테스트용으로 kor, eng를 다운받았다.

다운받은 파일들은 Android 프로젝트 안에 있는 Assets 폴더에 tessdata라는 폴더를 생성하고 넣어준다.






Android 프로젝트에 있는 MainActivity.cs 를 작성한다.

using System;

using Android.App;
using Android.Content.PM;
using Android.OS;
using TinyIoC;
using Tesseract;
using Tesseract.Droid;
using XLabs.Ioc;
using XLabs.Ioc.TinyIOC;
using XLabs.Platform.Device;


namespace Xocr.Droid
{
    [Activity(Label = "Xocr", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            var container = TinyIoCContainer.Current;
            container.Register<IDevice>(AndroidDevice.CurrentDevice);
            container.Register<ITesseractApi>((cont, parameters) =>
            {
                return new TesseractApi(ApplicationContext, Tesseract.Droid.AssetsDeployment.OncePerInitialization);
            });
            Resolver.SetResolver(new TinyResolver(container));

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }
    }
}

앞에서 다운받았던 라이브러리들이 사용되는데, Ioc는 디자인 틀과 동시에 Register 함수를 통해 인스턴트를 등록시키는 역할을 한다. 이를 통해 tessdata를 불러와 TesseractAPI를 등록하는 작업을 수행한다.


+
OncePerInitialization이 무엇인가 하고 소스를 확인해보니, Init할때마다 tessdata를 업데이트하기 때문에 속도가 느리므로 디버그 모드에서 사용하라고 한다.
그 반대인 OncePerVersion는 tessdata를 업데이트하지 않지만 속도가 빠르므로 실제 프로그램에서 사용하라는 것 같다.
테스트해보니 첫 수행에서는 별 차이 없는 속도고, 두번째 이후부터 차이가 생기는 것 같다.


이제 PCL 프로젝트의 MainPage.xaml.cs를 작성한다.

using System;
using System.Threading.Tasks;
using Tesseract;
using Xamarin.Forms;
using XLabs.Ioc;
using XLabs.Platform.Device;
using XLabs.Platform.Services.Media;

namespace Xocr
{
    public partial class MainPage : ContentPage
    {

        private Button _takePictureButton;
        private Label _recognizedTextLabel;
        private Image _takenImage;

        private readonly ITesseractApi _tesseractApi;
        private readonly IDevice _device;

        public MainPage()
        {
            InitializeComponent();

            //TesseractAPI
            _tesseractApi = Resolver.Resolve<ITesseractApi>();
            _device = Resolver.Resolve<IDevice>();

            //UI 생성
            BuildUi();

            //(버튼)이벤트 생성
            WireEvents();

        }


        private void BuildUi()
        {
            // 버튼 생성
            _takePictureButton = new Button
            {
                Text = "New scan"
            };

            _recognizedTextLabel = new Label();


            _takenImage = new Image() { HeightRequest = 200 };

            //버튼, 이미지, 인식결과텍스트 순서대로 배치
            Content = new ScrollView
            {
                Content = new StackLayout
                {
                    Children =
                        {
                            _takePictureButton,
                            _takenImage,
                            _recognizedTextLabel
                        }
                }
            };

        }

        private void WireEvents()
        {
            _takePictureButton.Clicked += TakePictureButton_Clicked;
        }

        async void TakePictureButton_Clicked(object sender, EventArgs e)
        {
            //버튼이 클릭되었을 때, Text 및 Enable 상태 변경
            _takePictureButton.Text = "Working...";
            _takePictureButton.IsEnabled = false;

            //TesseractAPI 초기화 (언어 선정)
            if (!_tesseractApi.Initialized)
                await _tesseractApi.Init("eng");

            //카메라 촬영 후 byte[] 형식 변수로 받아오기
            var photo = await TakePic();
            if (photo != null)
            {
                var imageBytes = new byte[photo.Source.Length];
                photo.Source.Position = 0;
                photo.Source.Read(imageBytes, 0, (int)photo.Source.Length);
                photo.Source.Position = 0;

                //문자 인식 수행
                var tessResult = await _tesseractApi.SetImage(imageBytes);

                //촬영이미지, 인식 결과 출력
                if (tessResult)
                {
                    _takenImage.Source = ImageSource.FromStream(() => photo.Source);
                    _recognizedTextLabel.Text = _tesseractApi.Text;
                }
            }

            //버튼 상태 복구
            _takePictureButton.Text = "New scan";
            _takePictureButton.IsEnabled = true;
        }

        private async Task<MediaFile> TakePic()
        {
            var mediaStorageOptions = new CameraMediaStorageOptions
            {
                DefaultCamera = CameraDevice.Rear
            };
            var mediaFile = await _device.MediaPicker.TakePhotoAsync(mediaStorageOptions);

            return mediaFile;
        }
    }
}

Init("eng"); 를 Init("kor"); 로 수정하면 인식 언어를 바꿀 수 있다.



다음은 결과 화면이다.
뉴스기사를 출력해서 인식해 보았다.





초기화면




English 결과


Korean 결과

카메라 촬영은 가로모드만 지원하고 세로로찍어도 가로로 돌아간 상태로 출력된다.
인식 성능은 속도가 상당히 나쁜편이다.
특히 사진이 부정확해서 인식이 어려울 때는 유난히 더 느리다.

개선할 방법을 공부해봐야겠다.


참고 사이트 : http://thatcsharpguy.com/post/tesseract-ocr-xamarin/

0 개의 댓글:

댓글 쓰기