[Delphi] 델파이 모바일 앱 카메라 바코드 인식

    728x90

    안녕하세요

    로로봉입니다 : )

    오늘은 델파이 모바일 앱 개발 중 카메라를 이용해 바코드를 인식하는 법을 포스팅하겠습니다.

    카메라에서 바코드를 제어하기 위해선 ZXing 라이브러리를 사용해야 합니다.

    ZXing 라이브러리는 아래 사이트에서 다운받을 수 있습니다.

     

    Spelt/ZXing.Delphi

    ZXing Barcode Scanning object Pascal Library for Delphi VCL and Delphi Firemonkey - Spelt/ZXing.Delphi

    github.com

     

     라이브러리는 설치하는 것이 아닌 Pas만 이용하면 되기 때문에 Tools – Options – Language – Delphi – Library 메뉴에 Library path만 등록해주면 됩니다.

    [ 그림 1 : Library Path ]

    ZXing 라이브러리 사용을 위해 interface uses에 3가지 pas를 추가해줍니다.

    ZXing.BarcodeFormat, ZXing.ReadResult, ZXing.ScanManager

    다음으로는 카메라를 사용해야 하기 때문에 폼 위에 TCameraComponent를 올려줍니다. 

    [ 그림 2 : 카메라 컴포넌트 ]

    private에 아래 변수를 선언합니다. 

    FusBarcode : String;
    FbScan  : Boolean;
    FScanManager : TScanManager;
    FScanInProgress : Boolean;

    FormCreate 함수에 아래와 같이 초기화를 해줍니다.


    if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
      AppEventSvc.SetApplicationEventHandler(AppEvent);
    
    FScanManager := TScanManager.Create(TBarcodeFormat.Auto, nil);

     바코드 포맷은 Auto가 아닌 Code39나 EAN13 등 다른 포맷을 지정해 줄 수도 있으며, Auto를 지정해 주면 자동으로 포맷을 인식 하게 됩니다.

     최신 버전의 Android API는 권한을 요청하는 매커티즘을 변경했다고 합니다. 카메라를 사용하기 전에 아래와 같이 권한을 요청하는 프로세스를 추가해 주어야 해당 앱에 카메라 접근 권한이 수락되는 경우에만 카메라를 사용할 수 있습니다.

    DisplayRationale 함수와 ActivateCameraPermissionRequestResult 함수를 정의해줍니다.

    procedure TfrmMain.DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc);
    begin
      // 카메라 권한 허용/거절 창 실행
      APostRationaleProc;
    end;
    
    procedure TfrmMain.ActivateCameraPermissionRequestResult(Sender: TObject; const APermissions: TArray<string; const AGrantResults: TArray<TPermissionStatus>);
    begin
      if (Length(AGrantResults) = 1) and (AGrantResult[0] = TPermissionStatus.Granted) then
      begin
        // 카메라 권한 허용 시 카메라 실행 처리
        CameraComponent.Active := False;    
        CameraComponent.Kind := FMX.Media.TCameraKind.BackCamera;
        CameraComponent.FocusMode := TFocusMode.ContinuousAutoFocus;
        CameraComponent.CaptureSettingPriority := TVideoCaptureSettingPriority.Resolution;
        CameraComponent.Quality := TVideoCaptureQuality.LowQuality;
        CameraComponent.Quality := TVideoCaptureQuality.MediumQuality;
        
        FusBarcode := '';
    
        KeyboardHide;
        CameraComponent.Active := True;
      end
      else
        ToastMessage('카메라 접근 권한이 없습니다.');
    end;

    위 함수를 정의해주시고 아래와 같이 호출하면 DisplayRaionale 함수가 호출됩니다.

    PermissionsService.RequestPermissions([PermissionCamera], ActivateCameraPermissionRequestResult, DisplayRationale);

    DisplayRationale 함수가 실행되면 카메라 권한을 사용하시겠냐는 안내창이 표시되고, ActivateCameraPermissionRequestResult 함수로 권한 허용 유무 결과가 리턴되는 것을 볼 수 있습니다.


    다음으로는 카메라로부터 들어오는 데이터를 처리하는 함수를 선언해 주어야 합니다.

    우선 화면상에는 TImage 컴포넌트를 올려주어 카메라 이미지가 TImage에 표시되도록 합니다.

    추가한 TImage 컴포넌트를 imgCamera로 이름을 변경하고 GetImage 함수를 아래와 같이 선언해주었습니다.

    procedure TfrmMain.GetImage;
    var
      scanBitmap, tempBitmap: TBitmap;
      ReadResult : TReadResult;
      iNewHeight : Integer;
      ARect : TRect;
    
      Task  : ITask;
    begin
      tempBitmap := TBitmap.Create;
      try
        CameraComponent.SampleBufferToBitmap(tempBitmap, True);
        iNewHeight := Trunc(tempBitmap.Width * imgCamera.Size.Height / imgCamera.Size.Width);
        ARect := Rect(0, (tempBitmap.Height-iNewHeight) div 2, tempBitmap.Width, tempBitmap.Height - (tempBitmap.Height-iNewHeight) div 2);
        imgCamera.Bitmap.SetSize(ARect.Width, ARect.Height);
        imgCamera.Bitmap.CopyFromBitmap(tempBitmap, ARect, 0, 0);
      finally
        tempBitmap.Free;
      end;
    
      if (FScanInProgress) then exit;
    
      scanBitmap := TBitmap.Create;
      scanBitmap.Assign(imgCamera.Bitmap);
      ReadResult := nil;
    
      Task := TTask.Create(
      procedure
      begin
        try
          try
              FScanInProgress := True;
              ReadResult := FScanManager.Scan(scanBitmap);
              FScanInProgress := False;
          except
            exit;
          end;
    
          if ReadResult <> nil then
          begin
            FScanInProgress := True;
    
            TThread.Synchronize(TThread.CurrentThread, procedure()
            begin
              // 바코드 값 저장
              FusBarcode := ReadResult.text;
            end);
          end;
        finally
          FreeAndNil(ReadResult);
          FreeAndNil(scanBitmap);
          FScanInProgress := False;
        end;
    
      end);
      Task.Start;
    end;

    카메라 컴포넌트의 이벤트인 OnSampleBufferReady 함수를 추가해주고 아래와 같이 GetImage 함수가 호출되도록 합니다.

    procedure TfrmMain.CameraComponentSampleBufferReady(Sender: TObject; const ATime: TMediaTime);
    begin
      TThread.Synchronize(TThread.CurrentThread, GetImage);
    end;

    이렇게 되면 카메라로 들어오는 Image가 GetImage 함수로 넘어가게 되고, GetImage 함수에서는 ScanManager를 통해 바코드 데이터를 추출할 수 있게 됩니다.

     

    좋아요 ♥ + 구독 부탁드립니다 : )

    728x90
    반응형

    댓글