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/

2017년 4월 5일 수요일

유용한 사이트 모음


- 개발 미관련

http://www.free-powerpoint-templates-design.com/ - PPT 템플릿 모음

https://www.showeet.com/ - PPT 템플릿 모음 2

https://www.presentationgo.com/presentation/category/charts-diagrams/ - PPT 차트 및 다이어그램 모음

https://diagrammer.duarte.com/ - PPT 다이어그램 영문 용어 참고용 (다운로드는 유료)

https://smallpdf.com/ - PDF 확장자 전환

http://www.online-convert.com/ - 다양한 확장자 Converter

https://www.koreafont.com/fonts/list/ - 폰트 비교, 검색 사이트

https://noonnu.cc/ - 폰트 비교, 검색 사이트 2 (무료 상업)

http://uhbeefont.com/ - 무료 폰트 모음

https://draftable.com/compare - Word, PPT, PDF 버전 비교 사이트

https://www.photopea.com/ - 웹 포토샵

https://www.ginifab.com/feeds/pms/color_picker_from_image.php - 이미지 색상 코드 추출기

https://mergevideo.online/ - 비디오 편집기

https://online-video-cutter.com - 비디오 편집기

https://ezgif.com/ - gif 편집기

http://embed.ly/code - 링크 미리보기나 유튜브의 경우 영상을 통채로 퍼올 때 Embed 태그 생성기 (SEO 는 링크 내부 텍스트를 포함해서 검색 유도 역할)

https://iframely.com/embed - 링크 미리보기 생성 (이미지가 꼭 존재해야 가능)



- 개발 관련

http://hilite.me/ - 스타일 적용 소스 태그 생성 (블로그 글은 default)

https://mothereff.in/html-entities - HTML entity 전환

https://www.branah.com/unicode-converter - Unicode 전환

http://www.opendesigns.org/website-templates/ - HTML Template 모음

http://codeincomplete.com/ - Javascript 관련 예제 (Game 많음)

https://codepen.io/pen - HTML, Javascript, CSS 테스트 도구

https://jsfiddle.net/ - HTML, Javascript, CSS 테스트 도구 2

https://www.onlinegdb.com/ - C, C++, Java, Python, PHP 웹 컴파일러

https://rextester.com/l/oracle_online_compiler - Oracle 웹 컴파일러

https://www.draw.io/# - XML 기반 순서도 그리기 툴

https://ko.calcuworld.com/%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4/%EB%B0%94%EC%9D%B4%ED%8A%B8-%EA%B3%84%EC%82%B0%EA%B8%B0/ - 바이트 계산기

http://mwultong.blogspot.com/2008/01/float-random-number-generator.html - 난수 생성기

https://www.timestampconvert.com/?go2=true&offset=-9&timestamp=1535508076&Submit=++++++Convert+to+Date++++++ - Java timestamp 날짜 전환기

http://convertcsv.com/csv-to-sql.htm - Import된 csv 값으로부터 SQL 자료형를 유추해서 CREATE / INSERT 문을 생성해줌

http://jsbeautifier.org/ - JavaScript 소스 정렬 및 압축 해제 등 읽기 좋게 변경


http://jsonviewer.stack.hu/ - JSON 데이터 정렬 및 도식화

https://regexper.com/ - 정규 표현식 도식화



- GIS 개발용

https://mapmakerapp.com/ - 맵 좌표 확인용

https://arthur-e.github.io/Wicket/sandbox-gmaps3.html - Geometry 확인용

https://mapshaper.org/ - shp 파일 뷰어

https://geographiclib.sourceforge.io/cgi-bin/GeoConvert?input=83.95%B0N+120.72%B0W&zone=-3&prec=0&option=Submit - 좌표계 변환기

http://mygps.co.kr/text/GPS_gro.htm - GPS 약어 설명


추가할 게 있으면 계속할 예정.

Blogger jQuery 적용 및 자체 스크롤 만들기


* Blogger 기본 테마 중 '세련' 테마 적용 중에 작성하였다.

항목 중에서 Tap 영역에 overflow: auto; 로 스크롤을 추가하였는데, div태그라 그런지 전체 스크롤과 동시에 움직이는 문제가 발생하였다.

검색해보니 두가지 해결법이 있는데,
mouseenter 이벤트를 이용하여 커서가 div에 진입할때 전체 스크롤를 없애거나(overflow: hidden;)
mousewheel 이벤트를 이용하여 스크립트로 작동하는 자체 스크롤을 만들어주는것.

후자가 더 깔끔해보여서 선택하기로 했다.
좋은지 나쁜지 애매한 점은 스크롤이 눈으로 보이진 않는다.

일단 CSS부분은 overflow: hidden; 으로 바꿔준다.
(이전글 http://bluebead38.blogspot.kr/2017/04/blogger-css.html 참고)


일단 소스는 다음과 같다.


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js">
    <!-- jQuery Connection -->
</script>

<script>
    $(function() {

        // column-left-outer class 지정 개체에서 스크롤 액션이 감지될 때
        $('.column-left-outer').on('mousewheel DOMMouseScroll', function(e) {

            var E = e.originalEvent;

            // 스크롤 액션의 변화값 증감에 따라 화면 스크롤 변화
            if (E.wheelDelta >= 100) {

                this.scrollTop -= 20;

            } else if (E.wheelDelta < -100) {
              
                this.scrollTop += 20;

            }

            return false;

        });

    });
</script>

이걸 그대로 복사하면 기호를 인식하지 못하고 오류가 난다.
Entity Name으로 전환해주는 작업이 필요하다.

https://mothereff.in/html-entities
여기서 간단하게 전환이 가능하다.
allow named character references in output도 체크해준다.


결과적으로는 아래 소스를 추가하면 된다. (주석도 같이 전환되기 때문에  지저분해서 지웠다.)
붙여넣는 위치는 <head> 내부면 된다. 적당히 </head> 위로 했다.



<b:include data='blog' name='google-analytics'/>
&lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js&quot;&gt;
    &lt;!-- jQuery Connection --&gt;
&lt;/script&gt;

&lt;script&gt;
    $(function() {

     
        $(&apos;.column-left-outer&apos;).on(&apos;mousewheel DOMMouseScroll&apos;, function(e) {

            var E = e.originalEvent;
          
            if (E.wheelDelta &gt;= 100) {

                this.scrollTop -= 20;
           
            } else if (E.wheelDelta &lt; -100) {
                this.scrollTop += 20;

            }
            return false;
        });
    });
&lt;/script&gt;

</head>



+ 문제점
Firefox에서는 작동하지 않는다.
좀더 자세히 말하면 DOMMouseScroll 까진 인식이 되나 scrollTop에서 문제가 생기는듯하다.

+다음 소스로 수정하면 Firefox에서도 동작한다.


$( '.column-left-outer' ).on( 'mousewheel DOMMouseScroll', function ( e ) {
    var E = e.originalEvent;
    delta = E.wheelDelta || -E.detail;

    this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;
    e.preventDefault();
});

알아보니 detail이라는 값이 Firefox에서만 사용되는 값인데, +, - 만 구분하기 때문에 값을 더 곱함으로써 스크롤처럼 보이도록 하는 것 같다.


참고 사이트 :
http://alik.info/p/65
http://stackoverflow.com/questions/7600454/how-to-prevent-page-scrolling-when-scrolling-a-div-element
http://recoveryman.tistory.com/120

2017년 4월 4일 화요일

Blogger CSS 수정 및 스크롤 고정하기


* Blogger 테마 중 '세련' 테마 적용 중에 작성하였다.

블로그 디자인을 좀 수정했다.

간단하게 말하면 Navbar, Header 및 Tap 스크롤 고정 작업이다.
[디자인] - [테마] - [HTML 편집] 을 건드리면 된다.

고정은 모두 position: fixed; 를 추가한다.

첫번째로 Navbar.
Blogger 기본 서식에 navbar 라는 class가 적용되어 있어 이것을 추가하고 작성하면 된다.
z-index는 중첩시 우선순위를 결정하는 것으로, 어느정도 높은 수치를 주면 된다.
(그렇지 않을 시 스크롤때마다 다른 항목들에 가려지면서 흔들리는것같은 효과를 가진다.)
기본 서식은 배경이 반투명한 색이나, background-color 속성을 추가하면 불투명한 해당 색상으로 바뀐다.

.navbar {
    width: 100%;
    top: 0;
    position: fixed;
    z-index: 99;
}




Header는 header-outer 라는 class로 지정되어 있다.
기본 서식에 있으니 수정해주면 된다.

.header-outer {
    margin: $(header.margin.top) 0 $(header.margin.bottom) 0;
    background: $(header.background.color) $(header.background.gradient) repeat scroll 0 0;
    position: fixed;
}



Tap 은 왼쪽 기준으로 column-left-outer라는 class로 지정되어 있다.
마찬가지로 기본 서식에 있으니 수정해주면 된다.
위치는 margin-right와 top, 크기는 width와 height를 조정해주면 된다.
overflow-y: auto; 는 스크롤바를 생성한다.

.main-inner .column-left-outer,
.main-inner .column-right-outer {
    margin-top: $(widget.outer.margin.top);
    position: fixed;
    top: 180px;
    margin-right: -300px;
    width: 120px;

    height: 600px;
    overflow-y: auto;

}



덤으로, 게시물 내용이 너무 왼쪽으로 붙어있어서 padding-left를 추가했다.
post-outer 를 수정하면 된다.

.post-outer {
  padding-bottom: 10px;
padding-left: 50px;
}

2017년 4월 3일 월요일

Xamarin(자마린) Android Tesseract OCR 어플 만들기 - 개발 환경설정


Laptop
운영체제Windows 10 Home 64bit
개발프로그램Microsoft Visual Studio Community 2015 한글판
Xamarin 4.3.0.795
Xamarin.Android 7.1.0.43

Mobile
모델LG X5
운영체제Android 6.0.1  Marshmallow

원래 안드로이드 스튜디오로 tess-two 모듈을 사용하고 싶었으나..
열심히 찾아봐도 Google이 올렸던 원본 라이브러리는 사라진데다
남아있는건 버전 차이 때문인건지 오류 투성이인 예제들 뿐이다.

포기하고 다른 툴을 찾다가 익숙한 Microsoft Visual Studio라는 점 때문에 끌린 Xamarin 선택.

Xamarin은 크로스플랫폼 개발용이라는 특징이 있는 툴로, C#을 언어로 사용한다.

설치 링크 : https://www.xamarin.com/download

당연히 Visual Studio가 이미 설치되어 있다면 I already have Visual Studio installed를 No로 하면 된다.



설치가 시작되면 여러 세부 설정들과 함께 위와 유사한 창이 뜬다.
저 하늘색 i 표시는 이미 설치되어 있다는 뜻으로 첫 설치라면 안뜨는게 맞다.
실제 필요 드라이브 용량은 사진에는 적게 나와있지만 기억상 36~38GB쯤 되었던것 같다.
 
네이티브 개발 키트 부분은 각자 버전에 맞춰 체크하고
맨 윗부분의 Windows 개발에 관련된 부분은 일단 모두 스킵하고
C#/.NET를 체크하면 아래, SDK, JDK 등에 모두 자동으로 체크가 된다.
 
하지만 설치 완료 후에 SDK, JDK는 구버전이라며 에러가 나니 따로 설치하도록 한다.
어차피 다운로드 대기 시간이 길기도 하니 함께 설치하면 된다.
 
다만 SDK의 경우 기본 경로를 쓰면 겹칠 위험이 있으므로 C: 등에 폴더를 따로 만들어 쓰도록 하자.
JDK는 버전이 다르기 때문에 폴더가 따로 생성된다.
 
(정확히는 Android Studio 설치. SDK만 설치하는 링크를 찾아봤으나 없었다.)
 
 
 
 
 
설치가 모두 완료되면, Microsoft Visual Studio를 실행하면 된다.
 
 
새 프로젝트 창을 켜고 사진과 같이 [Visual C#] - [Cross-Platform] - [플랫폼 간 앱] 을 선택하고, 이름은 임의로 Xocr로 하겠다.
 
여기서 주의사항은 저장 경로에 한글이나 공백이 들어가지 않는게 좋으며, find 관련 에러가 난다면 이것이 원인일 확률이 높다.
 
 
 
 
다음 나오는 화면에서는 코드 공유 전략에서만 PCL을 선택해주고 OK를 누른다.
 
 
 
 
 
 
 
이것은 Xamarin 설치 과정에서 iOS 체크하는 부분을 모두 빼면 나타나는 오류창이다. 이후 iOS 프로젝트가 아예 생성되지 않기때문에 뜨지 않을 것이고, Android만을 다룰 현재 예제에서는 크게 상관없다. 테스트를 해보지 못했기 때문에 이후에도 iOS를 굳이 언급하지 않겠다.
 
 
 
 
위에서 말했듯이 기본적으로 설정되어 있는 SDK, JDK가 구버전이기 때문에 오류가 뜰 것이다.
메뉴의 [도구] - [옵션] - [Xamarin] - [Android Setting] 탭으로 가면 경로 설정을 할 수 있다.
 
(기본적인 경로는 각각 C:\Program Files (x86)\Android\android-sdk~ 와 C:\Program Files (x86)\Java~ 일 것이다.)
 
 
 
 
이것으로 실행 시 오류가 모두 사라졌다면 마지막으로 라이브러리 설치 도구인 Nuget이라는 확장 프로그램을 설치해주면 된다.
 
설치 링크 : https://www.nuget.org/
 
 
 
설치 파일을 실행하면 이런식으로 현재 컴퓨터에 설치된 Visual Studio 버전 리스트가 뜬다.
사용할 버전을 선택하고 Install을 눌러주면 된다.
Xamarin에서 설치한 버전이라면 Community 일 것이다.
 
 
 
 
프로젝트명에서 우측버튼을 클릭하여 [NuGet 패키지 관리] 라는 메뉴를 선택한다.
프로젝트에 다음 두가지 라이브러리를 검색하여 설치한다.
 
XLabs.Forms
Xamarin.Tesseract
 
Xocr.Android 프로젝트에도 마찬가지로 설치해주고,
여기에는 XLabs.IoC.TinyIoC 를 추가적으로 설치한다.
 
빌드해서 오류가 안뜬다면 세팅은 완료된다.
실행은 AVD가 아닌 USB케이블로 기기에 직접 실행하는 방식으로 했다.
그 때 기기 OS 버전에 맞는 API를 설치하라고 뜰텐데, 절차에 따르면 된다.
USB디버깅이나 개발자모드 등에 대한 설명은 생략.
 
 
 
이 화면을 보면 성공.
 
 
 
+
 
기기 목록이 뜨지 않고 시작 버튼을 눌렀을 때 이런 오류창이 뜬다면 Xocr.Android 프로젝트명에서 우측버튼을 누르고 [시작 프로젝트로 설정]을 선택해준다.