RORO

ふつうの日記(移転したい)

届いたFAXを自動でGoogle Driveに保存してOCRで本文を抽出してSlackで通知する

(@前職)

リモートワークの絡みで、タイトルの通りのことをやってみようと思いついて、やってみたらけっこう簡単にできたので、その概要を書いておく。

処理の要点は、メールの自動受信処理、Google Cloud VisionによるOCR、Google Drive (G Suite) へのアップロードなど。

最終的な結果は以下のような感じでSlackに通知される。

処理の流れ

FAX複合機
 ↓
SendGrid Inbound Parse Webhook
 ↓
Google Cloud Functions
    → Google Cloud Vision
    → Google Drive (G Suite)
 ↓
Slack

Cloud Functionsでの処理はPythonで書いた。

1. 複合機からメールを送信する

大抵のFAX複合機には、受信したFAXをPDFにしてメールで送信する機能がついている。これを使って受信データを外部に送信する。

次に、複合機から送信されたメールを何らかの方法で自動で(機械的に)受信する必要がある。今回はそれを SendGrid Inbound Parse Webhook を使って行うことにした。

2. メールを受信する

複合機から送信したメールをSendGrid Inbound Parseに受信させる。受信したメールはparseされて、HTTPリクエストとしてGoogle Cloud Functions (のHTTPトリガ) に送られる。

3. Google Cloud FunctionsでFAX (PDF) を処理する

Google Cloud Functionsで、メールヘッダや添付PDFファイルを受け取って、以下の処理を行う。

3.1. Google Cloud VisionでOCRする

Cloud Visionの DOCUMENT_TEXT_DETECTION を使ってOCRを実行する。手書き文字も認識される。

  • ページはPDFの1〜5ページ目のみを読み取るようにした。
  • languageHintsには[‘ja’]を指定した。

3.2. PDFの向きを直す

FAXはひっくり返った向きで送られてくることが多いし、横長で印刷された文書というのもたまにある。

向きを直しておかないと見づらいので、OCRの結果をもとにページの向きを判定して、PyPDF2を使ってPDFの各ページを回転させる。

3.3. Google共有ドライブ (G Suite) にアップロードする

GoogleのDrive APIを使ってGoogle DriveにPDFをアップロードする。

PythonでGoogleのサービスのAPIを呼ぶには、googleapiclient (google-api-python-client) を使う。

G Suiteの共有ドライブにアップロードする場合は、driveIdや、supportsAllDrives=Trueなどを指定しないとうまく動かない(後者はそのうちに撤廃されるらしいが)。このへんの「共有ドライブ」周りについてはDrive APIの英語ドキュメントもまだちゃんと整備されておらず、ググらないと分からなかった。

3.4. 文書のサマリーを作る

文書の形式によっては、OCR結果にノイズ的な文字列が多く含まれることがある。そのままだと読みにくいので、情報として価値のない文字列や、罫線を文字として誤認識したものや、日本語/英語/中国語(漢字)以外の謎の言語として誤認識されたものは、除去したほうがいい。OCR結果の各要素に含まれる confidence の値なども参考にする。

本気でやるなら、罫線は画像処理系のアルゴリズムを使ってあらかじめ除去したほうがいいかもしれない。

Cloud Visionが返す結果は pages → paragraphs → words → symbols という構造になっている。この構造をトラバースして、書式を整えたり、不要部分を除去したりしながら、結果の文字列を組み立てる。

3.5. 文書のタイトル抽出する

OCRの結果をいろいろとルールベースで処理して、タイトルっぽいものを抽出する。

文字列の長さや、文字列が占める面積、タイトルによくある表現(について、のお知らせ、件名〜、etc.)等々を基準にスコアリングして判定している。

抽出したタイトルを用いて、Google Driveに先ほどアップロードしたファイルをリネームする。

3.6. Slackへ投稿する

Slack APIのchat.postMessageを使って、以下のものをSlackに投稿する。

  • 抽出タイトル
  • Google DriveのプレビューURL (webViewLink)
  • OCR結果のサマリ文字列

送信元FAX番号やOCRで得た内容に基づいたルールセットで投稿先のチャンネルを変えるようにしている。

SlackにはGoogle Driveの公式Appがインストールされているので、Drive上のファイルのURLを投稿するだけで自動的にunfurl(プレビュー展開)される。ただしこれを機能させるには、Botユーザではなく、Google Driveへのアクセス権をもつ一般ユーザとしてURLを投稿する必要がある。

評価

  • 良いときは複合機がFAXを受信してから5〜10秒くらいでSlackに投稿される。しかしCloud FunctionsがColdスタートした場合は受信から投稿まで10〜15秒くらいかかってしまうこともまれにある。
  • しばらく運用しているが、エラーでFAXをとりこぼすということは起きていない。