Tutorial sobre reconhecimento óptico de caracteres (OCR)

Saiba como realizar o reconhecimento óptico de caracteres (OCR) no Google Cloud Platform. Neste tutorial, demonstramos como fazer upload de arquivos de imagem para o Google Cloud Storage, extrair texto das imagens usando a Google Cloud Vision API, traduzir o texto usando a Google Cloud Translation API e salvar suas traduções de volta no Cloud Storage. O Google Cloud Pub/Sub é usado para enfileirar várias tarefas e acionar o Cloud Functions correto para executá-lo.

Objetivos

  • Escrever e implantar vários Background Cloud Functions.
  • Fazer upload de imagens para o Cloud Storage.
  • Extrair, traduzir e salvar o texto contido nas imagens enviadas.

Custos

Este tutorial usa componentes do Cloud Platform que podem ser cobrados, incluindo:

  • Google Cloud Functions
  • Google Cloud Pub/Sub
  • Google Cloud Storage
  • API Google Cloud Translation
  • API Google Cloud Vision

Use a calculadora de preços para gerar uma estimativa de custo com base no uso previsto.

Usuários novos do Cloud Platform podem se qualificar para uma avaliação gratuita.

Antes de começar

  1. Faça login na sua Conta do Google.

    Se você ainda não tiver uma, inscreva-se.

  2. No Console do GCP, na página do seletor de projetos, selecione ou crie um projeto do GCP.

    Acesse a página do seletor de projetos

  3. Verifique se o faturamento foi ativado no projeto do Google Cloud Platform. Saiba como confirmar que o faturamento está ativado para seu projeto.

  4. Ative a(s) Cloud Functions, Cloud Pub/Sub, Cloud Storage, Cloud Translation e Cloud Vision APIs necessária(s).

    Ativar a(s) APIs

  5. Atualize os componentes gcloud:
    gcloud components update
  6. Prepare seu ambiente de desenvolvimento.

Como visualizar o fluxo de dados

O fluxo de dados no aplicativo do tutorial do OCR envolve vários passos:

  1. Uma imagem é enviada para o Cloud Storage com texto em qualquer idioma (texto que aparece na própria imagem).
  2. Um Cloud Function é acionado, que usa a Vision API para extrair o texto e coloca o texto na fila para ser traduzido nas linguagens de tradução configuradas (publicando uma mensagem em um tópico Pub/Sub, que por sua vez aciona outra função).
  3. Para cada tradução enfileirada, é acionado um Cloud Function que usa a API Translation API para traduzir o texto e enfileirá-lo para ser salvo no Cloud Storage (novamente, publicando um mensagem em um tópico Pub/Sub).
  4. Para cada texto traduzido, é acionado um Cloud Function que salva o texto traduzido no Cloud Storage.

Isso pode ajudar a visualizar os passos:

Como preparar o aplicativo

  1. Crie um intervalo do Cloud Storage para fazer upload das imagens, onde YOUR_IMAGE_BUCKET_NAME é um nome de intervalo globalmente exclusivo:

    gsutil mb gs://YOUR_IMAGE_BUCKET_NAME
    
  2. Crie um intervalo do Cloud Storage para salvar as traduções, onde YOUR_TEXT_BUCKET_NAME é um nome de intervalo globalmente exclusivo:

    gsutil mb gs://YOUR_TEXT_BUCKET_NAME
    
  3. Clone o repositório do app de amostra na máquina local:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

    Go

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

  4. Altere para o diretório que contém o código de amostra do Cloud Functions:

    Node.js

    cd nodejs-docs-samples/functions/ocr/app/

    Python

    cd python-docs-samples/functions/ocr/app/

    Go

    cd golang-samples/functions/ocr/app/

  5. Configure o aplicativo:

    Node.js

    Usando o arquivo config.default.json como um modelo, crie um arquivo config.json no diretório app com o seguinte conteúdo:
    {
    "RESULT_TOPIC": "YOUR_RESULT_TOPIC_NAME",
    "RESULT_BUCKET": "YOUR_TEXT_BUCKET_NAME",
    "TRANSLATE_TOPIC": "YOUR_TRANSLATE_TOPIC_NAME",
    "TRANSLATE": true,
    "TO_LANG": ["en", "fr", "es", "ja", "ru"]
    }
    • Substitua YOUR_RESULT_TOPIC_NAME por um nome de tópico Pub/Sub a ser usado para salvar os resultados quando a tradução está completa.
    • Substitua YOUR_TEXT_BUCKET_NAME por um nome de intervalo usado para salvar os resultados da tradução.
    • Substitua YOUR_TRANSLATE_TOPIC_NAME por um nome de tópico Pub/Sub a ser usado para traduzir o texto extraído na etapa de OCR.

    Python

    Edite o arquivo config.json no diretório app para ter o seguinte conteúdo:
    {
    "RESULT_TOPIC": "YOUR_RESULT_TOPIC_NAME",
    "RESULT_BUCKET": "YOUR_TEXT_BUCKET_NAME",
    "TRANSLATE_TOPIC": "YOUR_TRANSLATE_TOPIC_NAME",
    "TRANSLATE": true,
    "TO_LANG": ["en", "fr", "es", "ja", "ru"]
    }
    • Substitua YOUR_RESULT_TOPIC_NAME por um nome de tópico Pub/Sub a ser usado para salvar os resultados quando a etapa de tradução está completa.
    • Substitua YOUR_TEXT_BUCKET_NAME por um nome de intervalo usado para salvar os resultados da tradução.
    • Substitua YOUR_TRANSLATE_TOPIC_NAME por um nome de tópico Pub/Sub para acionar a tradução do texto extraído.

    Go

    Edite o arquivo config.json no diretório app para ter o seguinte conteúdo:
    {
    "RESULT_TOPIC": "YOUR_RESULT_TOPIC_NAME",
    "RESULT_BUCKET": "YOUR_TEXT_BUCKET_NAME",
    "TRANSLATE_TOPIC": "YOUR_TRANSLATE_TOPIC_NAME",
    "TO_LANG": ["en", "fr", "es", "ja", "ru"]
    }
    • Substitua YOUR_RESULT_TOPIC_NAME por um nome de tópico Pub/Sub a ser usado para salvar os resultados quando a tradução está completa.
    • Substitua YOUR_TEXT_BUCKET_NAME por um nome de intervalo usado para salvar os resultados.
    • Substitua YOUR_TRANSLATE_TOPIC_NAME por um nome de tópico Pub/Sub a ser usado para resultados de tradução.

Como entender o código

Como importar dependências

O aplicativo importa várias dependências para se comunicar com os serviços do Google Cloud Platform:

Node.js

const config = require('./config.json');

// Get a reference to the Pub/Sub component
const {PubSub} = require('@google-cloud/pubsub');
const pubsub = new PubSub();
// Get a reference to the Cloud Storage component
const {Storage} = require('@google-cloud/storage');
const storage = new Storage();

// Get a reference to the Cloud Vision API component
const Vision = require('@google-cloud/vision');
const vision = new Vision.ImageAnnotatorClient();

// Get a reference to the Translate API component
const {Translate} = require('@google-cloud/translate');
const translate = new Translate();

const Buffer = require('safe-buffer').Buffer;

Python

import base64
import json
import os

from google.cloud import pubsub_v1
from google.cloud import storage
from google.cloud import translate
from google.cloud import vision

vision_client = vision.ImageAnnotatorClient()
translate_client = translate.Client()
publisher = pubsub_v1.PublisherClient()
storage_client = storage.Client()

project_id = os.environ['GCP_PROJECT']

with open('config.json') as f:
    data = f.read()
config = json.loads(data)

Go


// Package ocr contains Go samples for creating OCR
// (Optical Character Recognition) Cloud functions.
package ocr

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"cloud.google.com/go/pubsub"
	"cloud.google.com/go/storage"
	"cloud.google.com/go/translate"
	vision "cloud.google.com/go/vision/apiv1"
	"golang.org/x/text/language"
)

type configuration struct {
	ProjectID      string   `json:"PROJECT_ID"`
	ResultTopic    string   `json:"RESULT_TOPIC"`
	ResultBucket   string   `json:"RESULT_BUCKET"`
	TranslateTopic string   `json:"TRANSLATE_TOPIC"`
	ToLang         []string `json:"TO_LANG"`
}

type ocrMessage struct {
	Text     string       `json:"text"`
	FileName string       `json:"fileName"`
	Lang     language.Tag `json:"lang"`
	SrcLang  language.Tag `json:"srcLang"`
}

// GCSEvent is the payload of a GCS event.
type GCSEvent struct {
	Bucket         string    `json:"bucket"`
	Name           string    `json:"name"`
	Metageneration string    `json:"metageneration"`
	ResourceState  string    `json:"resourceState"`
	TimeCreated    time.Time `json:"timeCreated"`
	Updated        time.Time `json:"updated"`
}

// PubSubMessage is the payload of a Pub/Sub event.
type PubSubMessage struct {
	Data []byte `json:"data"`
}

var (
	visionClient    *vision.ImageAnnotatorClient
	translateClient *translate.Client
	pubsubClient    *pubsub.Client
	storageClient   *storage.Client
	config          *configuration
)

func setup(ctx context.Context) error {
	if config == nil {
		cfgFile, err := os.Open("config.json")
		if err != nil {
			return fmt.Errorf("os.Open: %v", err)
		}

		d := json.NewDecoder(cfgFile)
		config = &configuration{}
		if err = d.Decode(config); err != nil {
			return fmt.Errorf("Decode: %v", err)
		}
	}

	var err error // Prevent shadowing clients with :=.

	if visionClient == nil {
		visionClient, err = vision.NewImageAnnotatorClient(ctx)
		if err != nil {
			return fmt.Errorf("vision.NewImageAnnotatorClient: %v", err)
		}
	}

	if translateClient == nil {
		translateClient, err = translate.NewClient(ctx)
		if err != nil {
			return fmt.Errorf("translate.NewClient: %v", err)
		}
	}

	if pubsubClient == nil {
		pubsubClient, err = pubsub.NewClient(ctx, config.ProjectID)
		if err != nil {
			return fmt.Errorf("translate.NewClient: %v", err)
		}
	}

	if storageClient == nil {
		storageClient, err = storage.NewClient(ctx)
		if err != nil {
			return fmt.Errorf("storage.NewClient: %v", err)
		}
	}
	return nil
}

Como processar imagens

A função a seguir lê um arquivo de imagem enviado do Cloud Storage e chama uma função para detectar se a imagem contém texto:

Node.js

/**
 * This function is exported by index.js, and is executed when
 * a file is uploaded to the Cloud Storage bucket you created
 * for uploading images.
 *
 * @param {object} event.data (Node 6) A Google Cloud Storage File object.
 * @param {object} event (Node 8+) A Google Cloud Storage File object.
 */
exports.processImage = event => {
  const file = event.data || event;

  return Promise.resolve()
    .then(() => {
      if (file.resourceState === 'not_exists') {
        // This was a deletion event, we don't want to process this
        return;
      }

      if (!file.bucket) {
        throw new Error(
          'Bucket not provided. Make sure you have a "bucket" property in your request'
        );
      }
      if (!file.name) {
        throw new Error(
          'Filename not provided. Make sure you have a "name" property in your request'
        );
      }

      return detectText(file.bucket, file.name);
    })
    .then(() => {
      console.log(`File ${file.name} processed.`);
    });
};

Python

def process_image(file, context):
    """Cloud Function triggered by Cloud Storage when a file is changed.
    Args:
        file (dict): Metadata of the changed file, provided by the triggering
                                 Cloud Storage event.
        context (google.cloud.functions.Context): Metadata of triggering event.
    Returns:
        None; the output is written to stdout and Stackdriver Logging
    """
    bucket = validate_message(file, 'bucket')
    name = validate_message(file, 'name')

    detect_text(bucket, name)

    print('File {} processed.'.format(file['name']))

Go


package ocr

import (
	"context"
	"fmt"
	"log"
)

// ProcessImage is executed when a file is uploaded to the Cloud Storage bucket you
// created for uploading images. It runs detectText, which processes the image for text.
func ProcessImage(ctx context.Context, event GCSEvent) error {
	if err := setup(ctx); err != nil {
		return fmt.Errorf("ProcessImage: %v", err)
	}
	if event.Bucket == "" {
		return fmt.Errorf("empty file.Bucket")
	}
	if event.Name == "" {
		return fmt.Errorf("empty file.Name")
	}
	if err := detectText(ctx, event.Bucket, event.Name); err != nil {
		return fmt.Errorf("detectText: %v", err)
	}
	log.Printf("File %s processed.", event.Name)
	return nil
}

A função a seguir extrai o texto da imagem usando a API Cloud Vision e enfileira o texto para tradução:

Node.js

/**
 * Detects the text in an image using the Google Vision API.
 *
 * @param {string} bucketName Cloud Storage bucket name.
 * @param {string} filename Cloud Storage file name.
 * @returns {Promise}
 */
function detectText(bucketName, filename) {
  let text;

  console.log(`Looking for text in image ${filename}`);
  return vision
    .textDetection(`gs://${bucketName}/${filename}`)
    .then(([detections]) => {
      const annotation = detections.textAnnotations[0];
      text = annotation ? annotation.description : '';
      console.log(`Extracted text from image (${text.length} chars)`);
      return translate.detect(text);
    })
    .then(([detection]) => {
      if (Array.isArray(detection)) {
        detection = detection[0];
      }
      console.log(`Detected language "${detection.language}" for ${filename}`);

      // Submit a message to the bus for each language we're going to translate to
      const tasks = config.TO_LANG.map(lang => {
        let topicName = config.TRANSLATE_TOPIC;
        if (detection.language === lang) {
          topicName = config.RESULT_TOPIC;
        }
        const messageData = {
          text: text,
          filename: filename,
          lang: lang,
        };

        return publishResult(topicName, messageData);
      });

      return Promise.all(tasks);
    });
}

Python

def detect_text(bucket, filename):
    print('Looking for text in image {}'.format(filename))

    futures = []

    text_detection_response = vision_client.text_detection({
        'source': {'image_uri': 'gs://{}/{}'.format(bucket, filename)}
    })
    annotations = text_detection_response.text_annotations
    if len(annotations) > 0:
        text = annotations[0].description
    else:
        text = ''
    print('Extracted text {} from image ({} chars).'.format(text, len(text)))

    detect_language_response = translate_client.detect_language(text)
    src_lang = detect_language_response['language']
    print('Detected language {} for text {}.'.format(src_lang, text))

    # Submit a message to the bus for each target language
    for target_lang in config.get('TO_LANG', []):
        topic_name = config['TRANSLATE_TOPIC']
        if src_lang == target_lang or src_lang == 'und':
            topic_name = config['RESULT_TOPIC']
        message = {
            'text': text,
            'filename': filename,
            'lang': target_lang,
            'src_lang': src_lang
        }
        message_data = json.dumps(message).encode('utf-8')
        topic_path = publisher.topic_path(project_id, topic_name)
        future = publisher.publish(topic_path, data=message_data)
        futures.append(future)
    for future in futures:
        future.result()

Go


package ocr

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"cloud.google.com/go/pubsub"
	"golang.org/x/text/language"
	visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1"
)

// detectText detects the text in an image using the Google Vision API.
func detectText(ctx context.Context, bucketName, fileName string) error {
	log.Printf("Looking for text in image %v", fileName)
	maxResults := 1
	image := &visionpb.Image{
		Source: &visionpb.ImageSource{
			GcsImageUri: fmt.Sprintf("gs://%s/%s", bucketName, fileName),
		},
	}
	annotations, err := visionClient.DetectTexts(ctx, image, &visionpb.ImageContext{}, maxResults)
	if err != nil {
		return fmt.Errorf("DetectTexts: %v", err)
	}
	text := ""
	if len(annotations) > 0 {
		text = annotations[0].Description
	}
	if len(annotations) == 0 || len(text) == 0 {
		log.Printf("No text detected in image %q. Returning early.", fileName)
		return nil
	}
	log.Printf("Extracted text %q from image (%d chars).", text, len(text))

	detectResponse, err := translateClient.DetectLanguage(ctx, []string{text})
	if err != nil {
		return fmt.Errorf("DetectLanguage: %v", err)
	}
	if len(detectResponse) == 0 || len(detectResponse[0]) == 0 {
		return fmt.Errorf("DetectLanguage gave empty response")
	}
	srcLang := detectResponse[0][0].Language.String()
	log.Printf("Detected language %q for text %q.", srcLang, text)

	// Submit a message to the bus for each target language
	for _, targetLang := range config.ToLang {
		topicName := config.TranslateTopic
		if srcLang == targetLang || srcLang == "und" { // detection returns "und" for undefined language
			topicName = config.ResultTopic
		}
		targetTag, err := language.Parse(targetLang)
		if err != nil {
			return fmt.Errorf("language.Parse: %v", err)
		}
		srcTag, err := language.Parse(srcLang)
		if err != nil {
			return fmt.Errorf("language.Parse: %v", err)
		}
		message, err := json.Marshal(ocrMessage{
			Text:     text,
			FileName: fileName,
			Lang:     targetTag,
			SrcLang:  srcTag,
		})
		if err != nil {
			return fmt.Errorf("json.Marshal: %v", err)
		}
		topic := pubsubClient.Topic(topicName)
		ok, err := topic.Exists(ctx)
		if err != nil {
			return fmt.Errorf("Exists: %v", err)
		}
		if !ok {
			topic, err = pubsubClient.CreateTopic(ctx, topicName)
			if err != nil {
				return fmt.Errorf("CreateTopic: %v", err)
			}
		}
		msg := &pubsub.Message{
			Data: []byte(message),
		}
		if _, err = topic.Publish(ctx, msg).Get(ctx); err != nil {
			return fmt.Errorf("Get: %v", err)
		}
	}
	return nil
}

Como traduzir textos

A função a seguir traduz o texto extraído e o enfileira para ser salvo no Cloud Storage:

Node.js

/**
 * This function is exported by index.js, and is executed when
 * a message is published to the Cloud Pub/Sub topic specified
 * by the TRANSLATE_TOPIC value in the config.json file. The
 * function translates text using the Google Translate API.
 *
 * @param {object} event.data (Node 6) The Cloud Pub/Sub Message object.
 * @param {object} event (Node 8+) The Cloud Pub/Sub Message object.
 * @param {string} {messageObject}.data The "data" property of the Cloud Pub/Sub
 * Message. This property will be a base64-encoded string that you must decode.
 */
exports.translateText = event => {
  const pubsubData = event.data.data || event.data;
  const jsonStr = Buffer.from(pubsubData, 'base64').toString();
  const payload = JSON.parse(jsonStr);

  return Promise.resolve()
    .then(() => {
      if (!payload.text) {
        throw new Error(
          'Text not provided. Make sure you have a "text" property in your request'
        );
      }
      if (!payload.filename) {
        throw new Error(
          'Filename not provided. Make sure you have a "filename" property in your request'
        );
      }
      if (!payload.lang) {
        throw new Error(
          'Language not provided. Make sure you have a "lang" property in your request'
        );
      }

      console.log(`Translating text into ${payload.lang}`);
      return translate.translate(payload.text, payload.lang);
    })
    .then(([translation]) => {
      const messageData = {
        text: translation,
        filename: payload.filename,
        lang: payload.lang,
      };

      return publishResult(config.RESULT_TOPIC, messageData);
    })
    .then(() => {
      console.log(`Text translated to ${payload.lang}`);
    });
};

Python

def translate_text(event, context):
    if event.get('data'):
        message_data = base64.b64decode(event['data']).decode('utf-8')
        message = json.loads(message_data)
    else:
        raise ValueError('Data sector is missing in the Pub/Sub message.')

    text = validate_message(message, 'text')
    filename = validate_message(message, 'filename')
    target_lang = validate_message(message, 'lang')
    src_lang = validate_message(message, 'src_lang')

    print('Translating text into {}.'.format(target_lang))
    translated_text = translate_client.translate(text,
                                                 target_language=target_lang,
                                                 source_language=src_lang)
    topic_name = config['RESULT_TOPIC']
    message = {
        'text': translated_text['translatedText'],
        'filename': filename,
        'lang': target_lang,
    }
    message_data = json.dumps(message).encode('utf-8')
    topic_path = publisher.topic_path(project_id, topic_name)
    future = publisher.publish(topic_path, data=message_data)
    future.result()

Go


package ocr

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"cloud.google.com/go/pubsub"
	"cloud.google.com/go/translate"
)

// TranslateText is executed when a message is published to the Cloud Pub/Sub topic specified
// by TRANSLATE_TOPIC in config.json, and translates the text using the Google Translate API.
func TranslateText(ctx context.Context, event PubSubMessage) error {
	if err := setup(ctx); err != nil {
		return fmt.Errorf("setup: %v", err)
	}
	if event.Data == nil {
		return fmt.Errorf("empty data")
	}
	var message ocrMessage
	if err := json.Unmarshal(event.Data, &message); err != nil {
		return fmt.Errorf("json.Unmarshal: %v", err)
	}

	log.Printf("Translating text into %s.", message.Lang.String())
	opts := translate.Options{
		Source: message.SrcLang,
	}
	translateResponse, err := translateClient.Translate(ctx, []string{message.Text}, message.Lang, &opts)
	if err != nil {
		return fmt.Errorf("Translate: %v", err)
	}
	if len(translateResponse) == 0 {
		return fmt.Errorf("Empty Translate response")
	}
	translatedText := translateResponse[0]

	messageData, err := json.Marshal(ocrMessage{
		Text:     translatedText.Text,
		FileName: message.FileName,
		Lang:     message.Lang,
		SrcLang:  message.SrcLang,
	})
	if err != nil {
		return fmt.Errorf("json.Marshal: %v", err)
	}

	topic := pubsubClient.Topic(config.ResultTopic)
	ok, err := topic.Exists(ctx)
	if err != nil {
		return fmt.Errorf("Exists: %v", err)
	}
	if !ok {
		topic, err = pubsubClient.CreateTopic(ctx, config.ResultTopic)
		if err != nil {
			return fmt.Errorf("CreateTopic: %v", err)
		}
	}
	msg := &pubsub.Message{
		Data: messageData,
	}
	if _, err = topic.Publish(ctx, msg).Get(ctx); err != nil {
		return fmt.Errorf("Get: %v", err)
	}
	log.Printf("Sent translation: %q", translatedText.Text)
	return nil
}

Como salvar as traduções

Por fim, a função a seguir recebe o texto traduzido e o salva no Cloud Storage:

Node.js

/**
 * This function is exported by index.js, and is executed when
 * a message is published to the Cloud Pub/Sub topic specified
 * by the RESULT_TOPIC value in the config.json file. The
 * function saves the data packet to a file in GCS.
 *
 * @param {object} event.data (Node 6) The Cloud Pub/Sub Message object.
 * @param {object} event (Node 8+) The Cloud Pub/Sub Message object.
 * @param {string} {messageObject}.data The "data" property of the Cloud Pub/Sub
 * Message. This property will be a base64-encoded string that you must decode.
 */
exports.saveResult = event => {
  const pubsubData = event.data.data || event.data;
  const jsonStr = Buffer.from(pubsubData, 'base64').toString();
  const payload = JSON.parse(jsonStr);

  return Promise.resolve()
    .then(() => {
      if (!payload.text) {
        throw new Error(
          'Text not provided. Make sure you have a "text" property in your request'
        );
      }
      if (!payload.filename) {
        throw new Error(
          'Filename not provided. Make sure you have a "filename" property in your request'
        );
      }
      if (!payload.lang) {
        throw new Error(
          'Language not provided. Make sure you have a "lang" property in your request'
        );
      }

      console.log(`Received request to save file ${payload.filename}`);

      const bucketName = config.RESULT_BUCKET;
      const filename = renameImageForSave(payload.filename, payload.lang);
      const file = storage.bucket(bucketName).file(filename);

      console.log(`Saving result to ${filename} in bucket ${bucketName}`);

      return file.save(payload.text);
    })
    .then(() => {
      console.log(`File saved.`);
    });
};

Python

def save_result(event, context):
    if event.get('data'):
        message_data = base64.b64decode(event['data']).decode('utf-8')
        message = json.loads(message_data)
    else:
        raise ValueError('Data sector is missing in the Pub/Sub message.')

    text = validate_message(message, 'text')
    filename = validate_message(message, 'filename')
    lang = validate_message(message, 'lang')

    print('Received request to save file {}.'.format(filename))

    bucket_name = config['RESULT_BUCKET']
    result_filename = '{}_{}.txt'.format(filename, lang)
    bucket = storage_client.get_bucket(bucket_name)
    blob = bucket.blob(result_filename)

    print('Saving result to {} in bucket {}.'.format(result_filename,
                                                     bucket_name))

    blob.upload_from_string(text)

    print('File saved.')

Go


package ocr

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
)

// SaveResult is executed when a message is published to the Cloud Pub/Sub topic specified by
// RESULT_TOPIC in config.json file, and saves the data packet to a file in GCS.
func SaveResult(ctx context.Context, event PubSubMessage) error {
	if err := setup(ctx); err != nil {
		return fmt.Errorf("ProcessImage: %v", err)
	}
	var message ocrMessage
	if event.Data == nil {
		return fmt.Errorf("Empty data")
	}
	if err := json.Unmarshal(event.Data, &message); err != nil {
		return fmt.Errorf("json.Unmarshal: %v", err)
	}
	log.Printf("Received request to save file %q.", message.FileName)

	bucketName := config.ResultBucket
	resultFilename := fmt.Sprintf("%s_%s.txt", message.FileName, message.Lang)
	bucket := storageClient.Bucket(bucketName)

	log.Printf("Saving result to %q in bucket %q.", resultFilename, bucketName)

	w := bucket.Object(resultFilename).NewWriter(ctx)
	defer w.Close()
	fmt.Fprint(w, message.Text)

	log.Printf("File saved.")
	return nil
}

Como implantar funções

Nesta seção, descrevemos como implantar funções.

  1. Para implantar a função de processamento de imagem com um gatilho do Cloud Storage, execute o seguinte comando no diretório app:

    Node.js

    gcloud functions deploy ocr-extract --runtime nodejs8 --trigger-bucket YOUR_IMAGE_BUCKET_NAME --entry-point processImage
    É possível usar os seguintes valores para a sinalização --runtime para usar versões diferentes do Node.js:
    • nodejs6 (uso suspenso)
    • nodejs8
    • nodejs10 (Beta)

    Python

    gcloud functions deploy ocr-extract --runtime python37 --trigger-bucket YOUR_IMAGE_BUCKET_NAME --entry-point process_image

    Go

    gcloud functions deploy ocr-extract --runtime go111 --trigger-bucket YOUR_IMAGE_BUCKET_NAME --entry-point ProcessImage

    em que YOUR_IMAGE_BUCKET_NAME é o nome do seu intervalo do Cloud Storage no qual você fará upload de imagens.

  2. Para implantar a função de tradução de texto com um gatilho Cloud Pub/Sub, execute o seguinte comando no diretório app:

    Node.js

    gcloud functions deploy ocr-translate --runtime nodejs8 --trigger-topic YOUR_TRANSLATE_TOPIC_NAME --entry-point translateText
    É possível usar os seguintes valores para a sinalização --runtime para usar versões diferentes do Node.js:
    • nodejs6 (uso suspenso)
    • nodejs8
    • nodejs10 (Beta)

    Python

    gcloud functions deploy ocr-translate --runtime python37 --trigger-topic YOUR_TRANSLATE_TOPIC_NAME --entry-point translate_text

    Go

    gcloud functions deploy ocr-translate --runtime go111 --trigger-topic YOUR_TRANSLATE_TOPIC_NAME --entry-point TranslateText

    em que YOUR_TRANSLATE_TOPIC_NAME é o nome do tópico do Cloud Pub/Sub que acionará as traduções.

  3. Para implantar a função que salva os resultados no Cloud Storage com um gatilho Cloud Pub/Sub, execute o seguinte comando no diretório app:

    Node.js

    gcloud functions deploy ocr-save --runtime nodejs8 --trigger-topic YOUR_RESULT_TOPIC_NAME --entry-point saveResult
    É possível usar os seguintes valores para a sinalização --runtime para usar versões diferentes do Node.js:
    • nodejs6 (uso suspenso)
    • nodejs8
    • nodejs10 (Beta)

    Python

    gcloud functions deploy ocr-save --runtime python37 --trigger-topic YOUR_RESULT_TOPIC_NAME --entry-point save_result

    Go

    gcloud functions deploy ocr-save --runtime go111 --trigger-topic YOUR_RESULT_TOPIC_NAME --entry-point SaveResult

    em que YOUR_RESULT_TOPIC_NAME é o nome do tópico do Cloud Pub/Sub que acionará a gravação dos resultados.

Como fazer upload de uma imagem

  1. Faça upload de uma imagem para o intervalo do Cloud Storage:

    gsutil cp PATH_TO_IMAGE gs://YOUR_IMAGE_BUCKET_NAME
    

    em que

    • PATH_TO_IMAGE é um caminho para um arquivo de imagem que contém texto em seu sistema local.
    • YOUR_IMAGE_BUCKET_NAME é o nome do intervalo onde está sendo feito upload das imagens.

    Você pode fazer o download de uma das imagens do projeto de amostra.

  2. Verifique os registros para ter certeza de que as execuções foram concluídas:

    gcloud functions logs read --limit 100
    
  3. Você pode ver as traduções salvas no intervalo do Cloud Storage especificado pelo valor RESULT_BUCKET no seu arquivo de configuração.

Limpeza

Para evitar que os recursos usados neste tutorial sejam cobrados na conta do Google Cloud Platform:

Como excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  1. No Console do GCP, acesse a página Gerenciar recursos.

    Acessar a página Gerenciar recursos

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

Como excluir o Cloud Functions

A exclusão de Cloud Functions não remove nenhum recurso armazenado no Cloud Storage.

Para excluir o Cloud Functions criado neste tutorial, execute os seguintes comandos:

gcloud functions delete ocr-extract
gcloud functions delete ocr-translate
gcloud functions delete ocr-save

Também é possível excluir o Cloud Functions do Console do Google Cloud.