GAS에서 OCR 해봤어요.

23005 단어 GAStech

개시하다


젠에게 기고한 것은 이번이 처음이다.
GAS에서 OCR을 시도해 보기 위해 아래 글(거의 복제)을 참고해 영수증의 합계 금액 부분만 반납하는 라인 봇을 제작했다.
LINE Bot과 GAS에서 OCR
const ACCESS_TOKEN = '<LINEのコンソールで取得したチャンネルアクセストークン>';
const FOLDER_ID = '<画像を置くフォルダーID>'

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) reply(message, event.replyToken);
  }
  return ContentService.createTextOutput(
    JSON.stringify({ content: 'post ok' })
  ).setMimeType(ContentService.MimeType.JSON);
}

const eventHandle = async (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);
  } 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);
  // 設定事項
  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に保存されたファイルとコピーファイルは不要なので削除
  Drive.Files.remove(copyImage.id);
  Drive.Files.remove(imageId);
  // OCRした内容を返却
  return { type: 'text', text: getTotalPayment(ocrText) }
}

// OCRから抜き出したテキストに含まれる支払い金額を取得
const getTotalPayment = str => {
  const regex = /合計\n¥[0-9,]+/g;
  const found = str.match(regex);
  return found.length <= 0 ? 0 : found[0].match(/[0-9,]+/g)[0];
}

// ユーザーから送られてきた画像をダウンロード
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();
}

// botの返信を行う
const reply = (message, 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],
    }),
  });
}

참고문과 다른 점


합계 금액 부분만 추첨


물론 이번 주제이기도 하지만 OCR 결과만 답장하는 것이 아니라 봇에서 합산금액을 추첨해 답장하는 것이다.

드라이브에 업로드된 이미지 삭제


무료로 업로드할 수 있는 용량을 줄이지 않기 위해 최종적으로 이미지가 삭제된다.올 때보다 더 예뻐요.

다음에 하고 싶은 거.


가계 수지 관리 Bot으로 일하고 있습니다.


이전에는 포워드 홈 관리 앱을 사용했지만 분류가 귀찮아 포기했다.하지만 한 달 지출을 파악하고 싶어 이번 봇은 제를 배경으로 한다.지금은 합산금액만 반납하지만, 결과를 DB에 저장해 1개월 지출을 가시화하고 싶다.

서버로 이동


어쨌든 GAS에서 집행하는 데는 시간이 너무 걸려요.개별 서버로 옮겨서 실행 시간을 단축하고 싶습니다.
송이경(신지현): 돈이랑...
해본 적이 없어서 람다에서 해보려고요.

유지보수성과 확장성을 의식한 코드


현재의 코드는 개인이 개발한 코드다.팀 개발에서 중시하는 보수성과 확장성은 전혀 없다.나는 개인 개발도 그 시점들을 의식적인 코드로 바꿔야 한다고 생각한다.

좋은 웹페이지 즐겨찾기