📸

LINEBotとGASでOCR

2022/01/13に公開約4,200字

この記事の内容

  • LINEBotに画像を送信すると、日本語のOCRをかけて認識されたテキストが返ってくる

きっかけ

https://zenn.dev/harachan/articles/d910ef8b89720b

こちらの記事を見つけて、GASで簡単にOCRができることを知ったので試してみました。

構成

  • GAS(Google Apps Script)だけで実装

LINEBot & GASの入門

https://qiita.com/inoue2002/items/0ccda626442921ec4eba

以下の行程で若干省略しているところは、↑の記事を参考にしてみてください

LINEBotの作成

  • LINEBotの作り方はいろんなところに記事があるので適宜探してください。
  • WebhookURLは後ほど取得するので今は放置でいいです
  • チャンネルアクセストークンを取得しておいてください

GASの開発

  • Google DriveからGASのプロジェクトを作成
  • 左メニューの「サービス」タブからDriveAPIを追加
  • 以下のコードをコピペ
  • 先ほど取得したトークンを記入
  • 自分のGoogleDriveに画像を保存するフォルダーを作成&IDを取得
  • 保存するフォルダーのIDをソースコードに記入
  • デプロイしてURLを取得
  • LINEBotのWebhookURLに記入

ソースコード

//LINEのコンソールで取得したチャンネルアクセストークン
const ACCESS_TOKEN = "xxxxxxxx";
//画像を置くフォルダーIDを指定
const FOLDER_ID = "xxxxxx"

const doPost = async (e) => {
  for (let i = 0; i < JSON.parse(e.postData.contents).events.length; i++) {
    const event = JSON.parse(e.postData.contents).events[i];
    const message = await eventHandle(event);
    //応答するメッセージがあった場合
    if (message !== undefined) {
      const replyToken = event.replyToken;
      const replyUrl = "https://api.line.me/v2/bot/message/reply";
      UrlFetchApp.fetch(replyUrl, {
        headers: {
          "Content-Type": "application/json; charset=UTF-8",
          Authorization: "Bearer " + ACCESS_TOKEN,
        },
        method: "post",
        payload: JSON.stringify({
          replyToken: replyToken,
          messages: [message],
        }),
      });
    }
  }
  return ContentService.createTextOutput(
    JSON.stringify({ content: "post ok" })
  ).setMimeType(ContentService.MimeType.JSON);
}


const eventHandle = async (event) => {
  console.log(event)
  let message;
  switch (event.type) {
    case "message":
      message = await messageFunc(event);
      break;
    case "follow":
      message = followFunc(event);
      break;
  }
  return message;
}
//メッセージイベントの処理
const messageFunc = async (event) => {
  let message;
  if (event.message.type === 'image') {
    message = await ocrImageFunc(event)
    //message = {type:'text',text:event.message.id}
  } else {
    message = { type: "text", text: '文字が入った画像を送信すると、OCRするよ!' }
  }
  return message
}

//友達登録時の処理
const followFunc = () => {
  return { type: "text", text: "文字が入った画像を送信すると、OCRするよ!" };
}

const ocrImageFunc = async (event) => {
  //送られてきた画像をダウンロードする
  const img = await getImageFunc(event.message.id)
  //送信された画像をDriveにアップロードする
  const imageId = await saveImageFunc(img)
  console.log(imageId)
  // 設定事項
  const resource = {
    title: "test" // 途中で生成されるコピーのファイル名の指定
  };
  const option = {
    "ocr": true,// OCRを行う
    "ocrLanguage": "ja",// OCRを行う言語
  }

  // 指定したfileIdのファイルをコピー
  const copyImage = Drive.Files.copy(resource, imageId, option)
  // コピー先ファイルにはOCRのデータが含まれているのでテキストを取得
  const ocrText = DocumentApp.openById(copyImage.id).getBody().getText();
  // コピー先ファイルはもう不要なので削除
  Drive.Files.remove(copyImage.id)
  // OCRした内容を返却
  return {type:'text',text:ocrText}
}

//ユーザーから送られてきた画像をダウンロード
const getImageFunc = async(id) => {
  const url = 'https://api-data.line.me/v2/bot/message/' + id + '/content';
  const data = UrlFetchApp.fetch(url, {
    'headers': {
      'Authorization': 'Bearer ' + ACCESS_TOKEN,
    },
    'method': 'get'
  });
  const img = data.getBlob().getAs('image/png').setName(Number(new Date()) + '.png');
  return img;
}

//画像をDriveに保存する関数
const saveImageFunc = async (img) => {
  const folder = DriveApp.getFolderById(FOLDER_ID);
  const file = folder.createFile(img)
  return file.getId()
}

動作確認

https://twitter.com/inoue2002/status/1481597810172575749

コアになる部分の解説

  • 冒頭に示した記事で詳しく説明があるのでそちらをぜひ参考にしてください。

  • LINEから送信された画像をいったんDriveにアップロード、文字認識
    参考にした記事:
    https://qiita.com/777_happ/items/93508d9c44d0d8b941f7

  • 認識した文字列を返却

【動かない人】

レスポンス コード: 404。メッセージ: Not Found。

画像をDriveにアップロードするところで上のエラーが出て詰まりました。
これはGCPプロジェクトとGASを紐づけた僕が原因だったのですが、GCP側でもDriveAPIを有効化することで解決しました。

デバックをしたい人

https://qiita.com/inoue2002/items/ad1003dfc168d1a201ba

こちらの記事を参考にしてみてください。(GCPにプロジェクトを作成し、GASと紐付けを行います。その際に上で紹介した【動かない人】の対象になるので、作成したGCPプロジェクトでもDriveAPIを有効化してください。)

おわりに

LINE自体に簡単にOCRする機能があったりするのでこれだけでは対して面白くないですが、裏側で記録したり、使いようによってはかなり便利になりそうです!
何よりデフォルトでこの機能が使えるのは便利〜

https://qiita.com/n0bisuke/items/477329e80fc85cb8088d
こちらの記事、発展的な内容&ほとんどローコードで実装されている記事でオススメです!
GitHubで編集を提案

Discussion

ログインするとコメントできます