Delphi에서 gRPC로 Google Cloud Vision 사용하기

· 컴포넌트

Google Cloud Vision은 이미지를 분석하여 그 안에 무엇이 있는지 알려줍니다. 설명 라벨을 반환하고, 인쇄된 텍스트와 손글씨 텍스트를 읽고(OCR), 얼굴을 감지하고, 유명한 랜드마크와 로고를 인식하는 등의 작업을 합니다. sgcWebSockets Enterprise는 TsgcGRPCClient 위에 위치하는 형식화된 Vision gRPC 클라이언트를 함께 제공하므로, 외부 런타임이나 직접 작성한 protobuf 없이 Delphi와 C++Builder에서 이미지를 보내고 그 주석을 다시 읽을 수 있습니다.

작동 방식

Cloud Vision은 ImageAnnotator gRPC 서비스를 제공합니다. 요청은 하나 이상의 이미지로 이루어진 배치이며, 각 이미지는 감지하려는 기능 목록과 짝지어지고, 응답은 그에 대응하는 주석 배치입니다. 전체 교환은 HTTP/2 위에 프레이밍된 Protocol Buffers 메시지입니다.

sgcWebSockets는 이미 완전한 HTTP/2 스택을 함께 제공하므로, 전송은 TLS를 통해 vision.googleapis.com:443을 가리키는 TsgcHTTP2Client입니다. TsgcGRPCClient가 그 위에 위치하여 gRPC 프레이밍, 헤더, 타임아웃, 트레일러 파싱을 수행합니다. sgcGRPC_Google_Vision의 Vision 메시지 클래스는 형식화된 protobuf 헬퍼입니다. 요청 객체를 채우고, ToBytes를 호출하여 직렬화하고, 제네릭 클라이언트로 전송한 다음, LoadFromBytes로 응답 바이트를 형식화된 응답 객체에 다시 로드합니다.

인증은 표준 Google 서비스 계정 흐름입니다. 자체 서명된 JWT를 bearer 토큰으로 교환하여 모든 호출에 gRPC 메타데이터로 전송합니다. JWT는 audience에 바인딩되므로, vision.googleapis.com이 수락하도록 토큰을 Vision 엔드포인트를 대상으로 합니다.

클라이언트 설정하기

HTTP/2 전송을 만들고, gRPC 클라이언트를 거기에 연결한 다음, 채널에 바이너리 protobuf 콘텐츠 타입을 사용하도록 지정합니다. Vision은 application/grpc+proto를 사용합니다.

uses
  sgcHTTP2_Client, sgcGRPC_Types, sgcGRPC_Client,
  sgcGRPC_Google_Vision;

var
  HTTP2: TsgcHTTP2Client;
  GRPC: TsgcGRPCClient;
begin
  HTTP2 := TsgcHTTP2Client.Create(nil);
  HTTP2.Host := 'vision.googleapis.com';
  HTTP2.Port := 443;
  HTTP2.TLS  := True;

  GRPC := TsgcGRPCClient.Create(nil);
  GRPC.Client := HTTP2;
  GRPC.ChannelOptions.ContentType := grpcProto;
  GRPC.ChannelOptions.Compression := grpcNoCompression;
end;

인증

bearer 토큰은 서비스 계정 자격 증명에서 나옵니다. 다운로드한 JSON 키의 값으로 JWT 옵션을 구성하고, audience를 Vision 엔드포인트로 향하게 하고, 결과로 받은 토큰을 DefaultMetadata에 추가하면 모든 gRPC 호출에 따라갑니다.

// configure the service-account JWT (values from the JSON key file)
CloudClient.GoogleCloudOptions.Authentication := gcaJWT;
CloudClient.GoogleCloudOptions.JWT.ClientEmail  := ClientEmail;
CloudClient.GoogleCloudOptions.JWT.PrivateKeyId := PrivateKeyId;
CloudClient.GoogleCloudOptions.JWT.PrivateKey.Text := PrivateKey;
CloudClient.GoogleCloudOptions.JWT.ProjectId := ProjectId;
// the self-signed JWT is audience-bound to the Vision endpoint
CloudClient.GoogleCloudOptions.JWT.API_Endpoint :=
  'https://vision.googleapis.com/';

// once the token is acquired, send it as gRPC metadata
GRPC.DefaultMetadata.Clear;
GRPC.DefaultMetadata.Add('authorization', 'Bearer ' + Token);

이미지 주석 달기

이미지를 분석하려면 TsgcGRPCVisionBatchAnnotateImagesRequest를 만들고, 이미지 요청을 하나 추가하고, 이미지를 가리키게 하고(여기서는 Google Cloud Storage URI이지만 원시 바이트도 보낼 수 있습니다), 하나 이상의 기능을 추가합니다. 각 기능은 FeatureType과 선택적 MaxResults를 가집니다. ToBytes로 직렬화하고 ImageAnnotator 서비스의 BatchAnnotateImages 메서드를 호출합니다.

var
  oRequest: TsgcGRPCVisionBatchAnnotateImagesRequest;
  oImgReq: TsgcGRPCVisionAnnotateImageRequest;
  oFeature: TsgcGRPCVisionFeature;
  oResponse: TsgcGRPCResponse;
begin
  oRequest := TsgcGRPCVisionBatchAnnotateImagesRequest.Create;
  try
    oImgReq := oRequest.AddRequest;
    oImgReq.Image.Source.GcsImageUri :=
      'gs://cloud-samples-data/vision/demo-image.jpg';

    oFeature := oImgReq.AddFeature;
    oFeature.FeatureType := 4;   // LABEL_DETECTION
    oFeature.MaxResults := 10;

    oResponse := GRPC.Call('google.cloud.vision.v1.ImageAnnotator',
      'BatchAnnotateImages', oRequest.ToBytes);
  finally
    oRequest.Free;
  end;
end;

FeatureType 값은 Vision API 열거형을 따릅니다. 1 FACE_DETECTION, 2 LANDMARK_DETECTION, 3 LOGO_DETECTION, 4 LABEL_DETECTION, 5 TEXT_DETECTION, 6 DOCUMENT_TEXT_DETECTION 등입니다. 한 번의 왕복으로 같은 이미지에 여러 감지를 실행하려면 기능을 둘 이상 추가하세요.

주석 읽기

응답 바이트는 형식화된 배치 응답으로 바로 로드됩니다. 배치의 각 이미지는 라벨, 랜드마크, 로고, 텍스트 주석에 대한 별도의 목록을 담고 있습니다. 모든 항목은 Description을 가진 TsgcGRPCVisionEntityAnnotation이며, 라벨의 경우 신뢰도 Score도 가집니다.

var
  oResponse: TsgcGRPCVisionBatchAnnotateImagesResponse;
  oImgResp: TsgcGRPCVisionAnnotateImageResponse;
  i, j: Integer;
begin
  oResponse := TsgcGRPCVisionBatchAnnotateImagesResponse.Create;
  try
    oResponse.LoadFromBytes(aData);
    for i := 0 to oResponse.ResponseCount - 1 do
    begin
      oImgResp := oResponse.Response(i);

      for j := 0 to oImgResp.LabelAnnotationCount - 1 do
        Memo1.Lines.Add('Label: ' + oImgResp.LabelAnnotation(j).Description +
          ' (score: ' + FloatToStr(oImgResp.LabelAnnotation(j).Score) + ')');

      for j := 0 to oImgResp.TextAnnotationCount - 1 do
        Memo1.Lines.Add('Text: ' + oImgResp.TextAnnotation(j).Description);

      for j := 0 to oImgResp.LandmarkAnnotationCount - 1 do
        Memo1.Lines.Add('Landmark: ' + oImgResp.LandmarkAnnotation(j).Description);
    end;
  finally
    oResponse.Free;
  end;
end;

스토리지 또는 바이트에서 온 이미지

이미지 소스는 유연합니다. Google Cloud Storage의 객체에는 Image.Source.GcsImageUri를, 공개 HTTP(S) URL에는 Image.Source.ImageUri를 설정합니다. 로컬 파일에 주석을 달려면 파일을 TBytes로 읽어 Image.Content에 할당하면, 사진이 요청에 인라인으로 따라갑니다. 동일한 요청 및 응답 클래스가 모든 소스를 처리합니다.

동기 또는 비동기

위 예제는 블로킹 Call을 사용하며, StatusCode, 원시 Data 바이트, 트레일러가 담긴 TsgcGRPCResponse를 반환합니다. UI 응답성을 유지하려면 CallAsync를 사용하고 OnGRPCResponse 이벤트에서 응답을 처리하며, 거기서 aResponse.Data를 정확히 같은 방식으로 파싱합니다. OK가 아닌 상태는 OnGRPCError를 통해, 전송 실패는 OnGRPCException을 통해 드러납니다.

제공 범위

형식화된 Vision gRPC 클라이언트는 sgcWebSockets Enterprise 에디션의 일부이며 Windows, macOS, Linux, iOS, Android에서 실행됩니다. 바로 실행 가능한 샘플은 Demos\21.GRPC\13.Vision에 있으며, 전체 레퍼런스는 gRPC Client 제품 페이지에 있습니다.

질문이나 의견이 있으신가요? 문의하기. 코드를 작성한 사람들로부터 답변을 받게 됩니다.