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

Google Cloud Platform での光学式文字認識(OCR)の実行方法について学びます。このチュートリアルでは、画像ファイルを Google Cloud Storage へアップロードする方法、Google Cloud Vision API を使って画像ファイルからテキストを抽出する方法、Google Cloud Translate 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 プロジェクトを選択または作成します。

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

  3. プロジェクトに対する課金を有効にします。

    課金を有効にする

  4. Cloud Functions、Cloud Pub/Sub、Cloud Storage、Cloud Translation、Cloud Vision API(複数)を有効にする。

    Enable the APIs

  5. Cloud SDK をインストールし、初期化します
  6. gcloud コンポーネントを更新し、インストールします。
    gcloud components update &&
    gcloud components install ベータ版
  7. Node.js 開発用に環境を準備します。

    セットアップ ガイドに移動

データの流れを可視化

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

  1. いずれかの言語の(画像に表示されている)テキストを含む画像が Cloud Storage にアップロードされます。
  2. Cloud Function がトリガーされます。すると Cloud Function は Vision API を使いテキストを抽出し、設定した言語に翻訳されるようキューに入れます。
  3. キューにある翻訳待ちテキストごとに、Cloud Function がトリガーされます。Cloud Function は Translate API を使ってテキストを翻訳し、翻訳したテキストを、Cloud Storage に保存されるようキューに入れます。
  4. 翻訳されたテキストごとに Cloud Function がトリガーされ、テキストを 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. アプリケーション コード用のディレクトリをローカル システム上に作成します。

    Linux / Mac OS X

    ディレクトリを作成します。

    mkdir ~/gcf_ocr

    作成したディレクトリに移動します。

    cd ~/gcf_ocr

    Windows(CMD)

    ディレクトリを作成します。

    mkdir %HOMEDRIVE%%HOMEPATH%\gcf_ocr

    作成したディレクトリに移動します。

    cd %HOMEDRIVE%%HOMEPATH%\gcf_ocr

  4. GitHub 上に置かれている Cloud Functions サンプル プロジェクトから index.js ファイルと package.json ファイルをダウンロードし、両方のファイルを gcf_ocr ディレクトリに保存します。

  5. gcf_ocr ディレクトリに 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] は、結果を保存するために使うトピック名で置換します。
    • [YOUR_TEXT_BUCKET_NAME] は、結果を保存するために使うバケット名で置換します。
    • [YOUR_TRANSLATE_TOPIC_NAME] は、結果を変換するために使うトピック名で置換します。

コードについて

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

アプリケーションは Google Cloud Platform サービスでやりとりするため、いくつかの依存関係をインポートする必要があります。

Node.js

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

// Get a reference to the Pub/Sub component
const pubsub = require('@google-cloud/pubsub')();
// Get a reference to the Cloud Storage component
const storage = require('@google-cloud/storage')();
// Get a reference to the Cloud Vision API component
const vision = require('@google-cloud/vision')();
// Get a reference to the Translate API component
const translate = require('@google-cloud/translate')();

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

画像を処理する

次の関数は Cloud Storage からアップロードされた画像を読み取り、detectText 関数を呼び出します。

Node.js

/**
 * Cloud Function triggered by Cloud Storage when a file is uploaded.
 *
 * @param {object} event The Cloud Functions event.
 * @param {object} event.data A Google Cloud Storage File object.
 */
exports.processImage = (event) => {
  let file = event.data;

  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.`);
    });
};

新しいファイルがユーザーが画像をアップロードするために作成した Cloud Storage バケットにアップロードされると、processImage 関数はモジュールによりエクスポートされ、実行されます。

次の関数は 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({ source: { imageUri: `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,
          from: detection.language
        };

        return publishResult(topicName, messageData);
      });

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

テキストの翻訳

次の関数は抽出されたテキストを翻訳し、翻訳されたテキストを Cloud Storage に保存するよう、キューに入れます。

Node.js

/**
 * Translates text using the Google Translate API. Triggered from a message on
 * a Pub/Sub topic.
 *
 * @param {object} event The Cloud Functions event.
 * @param {object} event.data The Cloud Pub/Sub Message object.
 * @param {string} event.data.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 pubsubMessage = event.data;
  const jsonStr = Buffer.from(pubsubMessage.data, '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');
      }

      const options = {
        from: payload.from,
        to: payload.lang
      };

      console.log(`Translating text into ${payload.lang}`);
      return translate.translate(payload.text, options);
    })
    .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}`);
    });
};

メッセージが config.json ファイルの TRANSLATE_TOPIC 値で指定された Cloud Pub/Sub トピックに公開されると、translateText 関数はモジュールによりエクスポートされ、実行されます。

変換されたテキストを保存する

最後に、次の関数は翻訳されたテキストを受信し、Cloud Storage に保存します。

Node.js

/**
 * Saves the data packet to a file in GCS. Triggered from a message on a Pub/Sub
 * topic.
 *
 * @param {object} event The Cloud Functions event.
 * @param {object} event.data The Cloud Pub/Sub Message object.
 * @param {string} event.data.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 pubsubMessage = event.data;
  const jsonStr = Buffer.from(pubsubMessage.data, '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.`);
    });
};

メッセージが saveResult ファイルの RESULT_TOPIC 値で指定された Cloud Pub/Sub トピックに公開されると、config.json 関数はモジュールによりエクスポートされ、実行されます。

関数のデプロイ

  1. Cloud Storage トリガーを使用して processImage 関数をデプロイするには、gcf_ocr ディレクトリで以下のコマンドを実行します。

    gcloud beta functions deploy ocr-extract --trigger-bucket [YOUR_IMAGE_BUCKET_NAME] --entry-point processImage
    

    ここで、

    • [YOUR_IMAGE_BUCKET_NAME] は、画像のアップロード先とする Cloud Storage バケットの名前です。
  2. Cloud Pub/Sub トリガーを使用して translateText 関数をデプロイするには、gcf_ocr ディレクトリで以下のコマンドを実行します。

    gcloud beta functions deploy ocr-translate --trigger-topic [YOUR_TRANSLATE_TOPIC_NAME] --entry-point translateText
    

    ここで、

    • [YOUR_TRANSLATE_TOPIC_NAME] は、変換をトリガーするために使用する Cloud Pub/Sub トピックの名前です。
  3. Cloud Pub/Sub トリガーを使用して saveResult 関数をデプロイするには、gcf_ocr ディレクトリで以下のコマンドを実行します。

    gcloud beta functions deploy ocr-save --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 beta functions logs read --limit 100
    

  3. RESULT_BUCKET ファイルの config.json 値で指定された Cloud Storage バケットに保存されている翻訳テキストを確認できます。

クリーンアップ

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

プロジェクトの削除

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

プロジェクトを削除する手順は次のとおりです。

  1. GCP Console で [プロジェクト] ページに移動します。

    プロジェクト ページに移動

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

Cloud Functions の削除

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

Cloud Function を削除するには、次のコマンドを実行します。

gcloud beta functions delete [NAME_OF_FUNCTION]

[NAME_OF_FUNCTION] は、削除する関数の名前です。

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

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

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

Cloud Functions のドキュメント