FMX: 바코드 스캔 라이브러리, ZXing.Delphi 소개 및 버그 수정

저는 현재 공장 자동화 목적의 안드로이드 앱을 개발중인데, 라인/공정별 각 단계마다 라벨에 찍혀있는 QR code를 스캔하는 기능이 필요합니다. (QR code도 바코드의 한 종류로서 2D barcode에 속합니다)

바코드를 스캔하는 방법에는 바코드 스캐너 전용 기기를 사용하는 경우와 스마트폰/태블릿 등의 범용 기기를 이용하는 방법 두가지가 있는데요, 제가 진행중인 프로젝트에서는 기본적으로는 전용 바코드 스캐너 기기(안드로이드 기반)를 사용합니다. 하지만 비상시 등의 백업 목적으로 스마트폰 카메라를 통한 바코드 스캔 기능도 추가로 필요하게 되었습니다.

스마트폰 등에서 카메라를 이용한 바코드 스캔 기능을 구현하려는 경우, 아이폰 등의 iOS의 경우 ZBar SDK를 사용하는 경우가 대부분이고, 안드로이드의 경우 외부의 ZXing 앱을 호출하는 방식이 일반적입니다. 이 ZXing 앱 방식은 구글플레이에서 “바코드 스캐너”로 검색되는데요. 개발 앱에서 ZXing 앱을 실행시키고, ZXing 앱 화면에서 바코드가 인식되고 나면 종료되면서 클립보드 복사를 통해 바코드 값을 전달받는 방식입니다. 그런데 ZXing 호출 방식은 자체 내장 라이브러리가 아닌 관계로, 원활한 사용을 어렵게 만드는 몇가지 단점들이 있습니다.

  1. 개발 앱과는 별도로 외부 ZXing 앱을 설치해야만 동작합니다. ZXing 앱이 설치가 안된 경우 당연히 스캔 기능 실행이 불가능하고, 또한 화면에 “바코드 스캐너” 라는 별도의 타이틀이 나타나므로 이질감을 느끼게 됩니다.
  2. 호출된 ZXing 앱이 실행되기까지 몇초 정도의 시간이 소요됩니다. 짧은 시간이긴 하지만 반복적으로 바코드 스캔 기능을 실행시키는 앱 사용자의 입장에서는 불편을 느낄 정도의 상당한 시간입니다.
  3. 호출된 ZXing 앱에서 스캔 완료 전에 Back 버튼을 누르면 개발된 앱으로 돌아오는 대신 안드로이드 바탕화면으로 빠집니다. 이때 호출했던 개발 앱이 종료되는 경우가 많고, 따라서 사용자가 메인 앱을 다시 실행시켜야만 하는 불편이 생깁니다.

저도 처음에는 위에서 소개한 ZXing 앱을 이용했습니다만, 실제 테스트 결과 몇가지 문제들이 있었습니다. 무엇보다도 시간의 문제가 가장 컸는데요. 카메라 방식은 안그래도 전용 기기보다 인식 속도가 많이 느린데다(초점 잡는 시간이 소요되는 점이 가장 큽니다) 여기에 ZXing 앱을 기동하는 몇초의 시간까지 더해지니 대규모 스캔 작업이 필요한 경우에는 실제 사용이 어려울 정도로 너무 느려졌습니다.
이런 문제점을 피하기 위해 상용의 안드로이드용 바코드 라이브러리를 구입하는 방법도 있는데, WinSoft 및 Han-Soft 등에서 이런 바코드 스캔 라이브러리를 개발 판매하고 있습니다. 하지만 데모를 테스트해보니 구입까지 해서 사용할 정도로 뛰어나다고 느껴지지는 않더군요. 그래서 이래저래 고민이 이어지던 중이었는데, 우연히 GitHub에 올라와 있는 ZXing.Delphi 라이브러리를 발견했습니다.

https://github.com/Spelt/ZXing.Delphi

ZXing.Delphi이 ZXing.Delphi 라이브러리는 아직 완성 단계의 라이브러리는 아닙니다만, 적어도 ZXing 앱 호출방식보다는 훨씬 빠릅니다. (개발자에 따르면 .NET 버전의 ZXing 바이너리를 델파이.NET으로 디컴파일한 후 다시 네이티브 델파이 코드로 포팅하는 엄청난 노가다를 했다고 합니다 -.-;;;;) 현재까지의 버전은 Code 128, Code93, ITF 등의 1D 바코드와 함께 2D 바코드 중 QR Code만 인식되는데요. 원래의 ZXing은 훨씬 많은 바코드 타입을 인식합니다만, 아직 포팅이 다 끝나지 않아 다른 개발자들의 도움을 요청하고 있네요.

어쨌든, ZXing.Delphi 라이브러리가 제가 필요로 하는 QR Code를 지원하므로 제 프로젝트에서 사용해봤는데요. 구동 및 인식 속도가 빠를 뿐만 아니라 사용도 더 간편합니다.

라이브러리 Floating point 버그 수정

ZXing.Delphi 라이브러리를 이용하여 이미지를 스캔하는 중에 이따금씩 “Invalid floating point operation” 에러가 발생하는 경우가 있습니다. 꽤 한참 걸려 디버깅을 해봤더니, 이 문제는 이미지 스캔 과정에서 두 군데의 소스에 연산 오류 가능성이 있어서 발생하더군요. 다음과 같은 방법으로 두 군데에 검사 코드를 추가해주시면 됩니다.

첫번째 수정

  1. \Lib\Classes\2D Barcodes\Detector\Detector.pas 유닛을 여십시오.
  2. TDetector.calculateModuleSize 함수의 바디를 찾으십시오.
  3. 함수의 마지막 end; 이전에 아래의 코드를 추가하십시오. (108번 라인)

(QR 코드 인식 중에 이미지 인식 오류로 이 함수에서 연산해서 리턴하는 값이 NaN 값이 되는 경우가 있어서, 리턴받은 쪽 함수가 이 값을 사용할 때 floating point 에러가 발생합니다. 리턴 값을 0.으로 설정해주면 리턴받은 함수가 검사 후 에러 없이 안전하게 종료되게 됩니다)

두번째 수정

  1. \Lib\Classes\2D Barcodes\Detector\FinderPatternFinder.pas 유닛을 여십시오.
  2. TFinderPatternFinder.selectBestPatterns 함수의 바디를 찾으십시오.
  3. 중간쯤의 stdDev := Sqrt(… 라인을 찾아 그 위에 아래의 코드를 추가하십시오. (834번 라인)

(Sqrt 함수에 0보다 작은 값이 전달되면서 floating point 에러가 발생하는 경우입니다. 0보다 작은 값이 나올 경우 리턴하면서 nil 값을 돌려줘 안전하게 종료하도록 합니다)

참고로, 델파이/C++빌더에서 디버깅 모드로 실행중이라면 간혹 “version number needs to be between 1 and 40” 예외가 보이는 경우가 있는데, 이것은 에러 상황이 아니라 스캔 실패의 경우로서 개발자가 코드 탈출 목적으로 의도적으로 예외를 낸 것으로, 리턴받는 쪽 함수에서 try..except 예외 처리로 없애줍니다. 즉 개발환경에서 디버깅 모드로 실행시에만 보이는 예외이므로 신경쓰지 않아도 됩니다.

이어서, 다음 포스트에서 이 ZXing.Delphi 라이브러리를 제대로 활용하기 위한 예제 소스에 대해 설명하겠습니다.

 

답글 남기기

이메일 주소는 공개되지 않습니다.