光学式文字認識(OCR)のチュートリアル

Google Cloud Platform での光学式文字認識(OCR)の実行方法について学びます。このチュートリアルでは、画像ファイルを Google Cloud Storage へアップロードする方法、Google Cloud Vision API を使って画像ファイルからテキストを抽出する方法、Google Cloud Translation API を使ってテキストを翻訳する方法、翻訳したテキストを Cloud Storage に保存する方法について説明します。Google Cloud Pub/Sub を使って、さまざまなタスクをキューに入れ、適切な Cloud Functions を使いこれらを実行していきます。

目標

  • 複数のバックグラウンド Cloud Functions を書き込み、デプロイする。
  • Cloud Storage に画像をアップロードする。
  • アップロードした画像に含まれるテキストを抽出、変換、保存する。

費用

このチュートリアルでは、以下を含む Cloud Platform の有料コンポーネントを使用します。

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

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを作成できます。

新しい Cloud Platform ユーザーは無料トライアルをご利用いただけます。

始める前に

  1. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  2. GCP Console のプロジェクト セレクタのページで、GCP プロジェクトを選択または作成します。

    プロジェクト セレクタのページに移動

  3. Google Cloud Platform プロジェクトに対して課金が有効になっていることを確認します。 プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  4. Cloud Functions、Cloud Pub/Sub、Cloud Storage、Cloud Translation および Cloud Vision必要な API を有効にします。

    API を有効にする

  5. gcloud コンポーネントを更新します。
    gcloud components update
  6. 開発環境を準備します。

データの流れ

OCR チュートリアル アプリケーションのデータの流れでは、次の手順が行われます。

  1. 言語にかかわらず(画像に表示されている)テキストとともに画像が Cloud Storage にアップロードされます。
  2. Visual API を使用してテキストを抽出し、構成済みの翻訳言語に変換するテキストをキューに格納する Cloud ファンクションがトリガーされます(メッセージを Pub/Sub トピックにパブリッシュして別の関数をトリガーします)。
  3. キューに格納された翻訳ごとに Cloud ファンクションがトリガーされます。この関数は、Translation API を使用してテキストを変換し、Cloud Storage に保存するために結果をキューに格納します(再度、メッセージを Pub/Sub トピックにパブリッシュします)。
  4. 変換されたテキストごとに Cloud ファンクションがトリガーされ、テキストを Cloud Storage に保存します。

次はこの手順を可視化した図です。

アプリケーションの準備

  1. Cloud Storage バケットを作成し、画像をアップロードします。ここで、YOUR_IMAGE_BUCKET_NAME はグローバルに固有のバケット名となります。

    gsutil mb gs://YOUR_IMAGE_BUCKET_NAME
    
  2. 変換文を保存するために Cloud Storage バケットを作成します。ここで、YOUR_TEXT_BUCKET_NAME はグローバルに固有なバケット名です。

    gsutil mb gs://YOUR_TEXT_BUCKET_NAME
    
  3. ローカルマシンにサンプルアプリのリポジトリのクローンを作成します。

    Node.js

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

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

    Python

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

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

    Go

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

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

  4. 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. アプリを構成します。

    Node.js

    config.default.json ファイルをテンプレートとして使用し、app ディレクトリに次のコンテンツを含む config.json ファイルを作成します。
    {
    "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"]
    }
    • YOUR_RESULT_TOPIC_NAME は、翻訳完了後に結果を使用する Pub/Sub トピック名に置き換えます。
    • YOUR_TEXT_BUCKET_NAME は、変換結果を保存するバケット名に置き換えます。
    • YOUR_TRANSLATE_TOPIC_NAME は、OCR ステップで抽出されたテキストの翻訳に使用する Pub/Sub トピック名に置き換えます。

    Python

    app ディレクトリの config.json ファイルを次の内容に変更します。
    {
    "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"]
    }
    • YOUR_RESULT_TOPIC_NAME は、変換ステップの完了後に結果を保存する Pub/Sub トピック名に置き換えます。
    • YOUR_TEXT_BUCKET_NAME は、変換結果を保存するバケット名に置き換えます。
    • YOUR_TRANSLATE_TOPIC_NAME は、抽出されたテキストの翻訳をトリガーする Pub/Sub トピック名に置き換えます。

    Go

    app ディレクトリの config.json ファイルを次の内容に変更します。
    {
    "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"]
    }
    • YOUR_RESULT_TOPIC_NAME は、翻訳完了後に結果を使用する Pub/Sub トピック名に置き換えます。
    • YOUR_TEXT_BUCKET_NAME は、結果の保存に使うバケット名で置換します。
    • YOUR_TRANSLATE_TOPIC_NAME は、結果の翻訳に使用する Pub/Sub トピック名に置き換えます。

コードの説明

依存関係をインポートする

アプリケーションが 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
}

画像を処理する

次の関数は Cloud Storage からアップロードされた画像を読み取り、画像にテキストが含まれているかどうかを検出する関数を呼び出します。

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
}

次の関数は Cloud Vision API を使い画像からテキストを抽出し、翻訳されるようにテキストをキューに入れます。

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
}

テキストの翻訳

次の関数は抽出されたテキストを翻訳し、翻訳されたテキストを 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
}

翻訳されたテキストを保存する

最後に、次の関数は翻訳されたテキストを受信し、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
}

関数のデプロイ

このセクションでは、関数をデプロイする方法について説明します。

  1. Cloud Storage のトリガーで画像処理関数をデプロイするには、app ディレクトリで次のコマンドを実行します。

    Node.js

    gcloud functions deploy ocr-extract --runtime nodejs8 --trigger-bucket YOUR_IMAGE_BUCKET_NAME --entry-point processImage
    異なるバージョンの Node.js を使用するには、--runtime フラグに次の値を使用します。
    • nodejs6(非推奨)
    • nodejs8
    • nodejs10(ベータ版)

    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

    YOUR_IMAGE_BUCKET_NAME は、画像をアップロードする Cloud Storage バケットの名前です。

  2. Cloud Pub/Sub トリガーでテキスト変換関数をデプロイするには、app ディレクトリで次のコマンドを実行します。

    Node.js

    gcloud functions deploy ocr-translate --runtime nodejs8 --trigger-topic YOUR_TRANSLATE_TOPIC_NAME --entry-point translateText
    異なるバージョンの Node.js を使用するには、--runtime フラグに次の値を使用します。
    • nodejs6(非推奨)
    • nodejs8
    • nodejs10(ベータ版)

    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

    YOUR_TRANSLATE_TOPIC_NAME は、翻訳をトリガーする Cloud Pub/Sub トピックの名前です。

  3. Cloud Pub/Sub トリガーで、Cloud Storage に結果を保存する関数をデプロイするには、app ディレクトリで次のコマンドを実行します。

    Node.js

    gcloud functions deploy ocr-save --runtime nodejs8 --trigger-topic YOUR_RESULT_TOPIC_NAME --entry-point saveResult
    異なるバージョンの Node.js を使用するには、--runtime フラグに次の値を使用します。
    • nodejs6(非推奨)
    • nodejs8
    • nodejs10(ベータ版)

    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

    YOUR_RESULT_TOPIC_NAME は、結果の保存をトリガーする Cloud Pub/Sub トピックの名前です。

画像のアップロード

  1. 画像を Cloud Storage バケットにアップロードします。

    gsutil cp PATH_TO_IMAGE gs://YOUR_IMAGE_BUCKET_NAME
    

    ここで

    • PATH_TO_IMAGE はお使いのローカル システムにある画像ファイル(テキストを含む)へのパスです。
    • YOUR_IMAGE_BUCKET_NAME は画像のアップロード先となるバケットの名前です。

    サンプル プロジェクトから画像をダウンロードできます。

  2. 実行した内容が完了していることをログで確認します。

    gcloud functions logs read --limit 100
    
  3. 保存された翻訳は、構成ファイルの RESULT_BUCKET 値に指定された Cloud Storage バケットで確認できます。

クリーンアップ

このチュートリアルで使用するリソースについて、Google Cloud Platform アカウントへの課金が発生しないようにする手順は次のとおりです。

プロジェクトの削除

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除するには:

  1. GCP Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

Cloud Functions の削除

Cloud Functions を削除しても、Cloud Storage に保存されたリソースは削除されません。

このチュートリアルで作成した Cloud Functions を削除するには、次のコマンドを実行します。

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

Google Cloud Console から Cloud Functions を削除することもできます。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud Functions のドキュメント