Google Cloud Functions に ファイルをアップロードして Vision API で OCR

Google Cloud Functions へファイルをアップロードしGoogle Cloud Storage へ保存できたので、アップロードしたので、そのファイルを Cloud Vision APIにかけてOCRを行う。

Firebase Admin SDK を利用しているので、Google Cloud Platform 周りの認証が隠蔽されるので非常に便利。

以下の点にはまったが、なんとか夏季休暇2日目に疎通できた。

  • Functions呼び出しURLを間違えていることに気づかず、Vision APIは、Firebase Admin SDK が利用できないと勘違いし空回り。結局利用できた。
  • TypeScript と import を使用したかったのだが、tsc のエラーが解決できなかったので、コーディングでは、import を使用して、型定義の恩恵をえて、デプロイ時にはtscエラーを回避するために、require に切り替えるなど。

1.Functions 実装

functions/index.ts

import * as functions from ‘firebase-functions’;
import * as admin from ‘firebase-admin’;
admin.initializeApp();
import * as path from ‘path’;
import * as os from ‘os’;
import * as fs from ‘fs’;
import * as Busboy from ‘busboy’;

// コーディング時にTypeScriptの型定義を import して利用したいが、
// デプロイではエラーになるので、require を使う
//import { v1p1beta1 as vision } from ‘@google-cloud/vision’;
const vision = require(“@google-cloud/vision”).v1p1beta1;

export const helloVisions = functions.https.onRequest((request, response) => {
if (request.method !== ‘POST’) {
response.status(405).end();
return;
}
const visionClient = new vision.ImageAnnotatorClient();
const busboy = new Busboy({headers: request.headers});
const tmpdir = os.tmpdir();

const uploads: {[key:string]:string} = {};
const fileWrites: Array<Promise<any>> = [];

busboy.on('file', (fieldname, file, filename) => {
    console.log(`Processed file ${filename}`);
    const filepath = path.join(tmpdir, filename);

    console.log(`Filepath ${filepath}`);
    uploads[fieldname] = filepath;

    const writeStream = fs.createWriteStream(filepath);
    file.pipe(writeStream);

    const promise = new Promise((resolve, reject) => {
        file.on('end', () => {
            writeStream.end();
        });
        writeStream.on('finish', resolve);
        writeStream.on('error', reject);
    });
    fileWrites.push(promise);
});

busboy.on('finish', async () => {
    await Promise.all(fileWrites);

    for (const file in uploads) {
        const filepath = uploads[file];
        console.log(`Finish filename:${filepath}`);

        const [textDetections] = await visionClient.textDetection(filepath);
        const [annotation]:any = textDetections.textAnnotations;

        const text = annotation ? annotation.description : '';
        console.log("## OCR ## " + text);
        response.write(text);

        try {
            console.log(`UN LINK FILE : ${filepath}`);
            fs.unlinkSync(filepath);
        } catch(e) {
        }
    }
    response.send();
});

busboy.on('error', async () => {
    console.log("busboy on error");
});
busboy.end(request.rawBody);

});

2.呼び出しページ

<!DOCTYPE html>
<html lang=“ja”>
<head>
<meta charset=“uft–8”>
</head>
<body>
<h1>Cloud Vision</h1>
 <form action=“https://{Firebase Cloud Functions のURL}/helloVisions” method=“POST” enctype=“multipart/form-data”>
 <p><input type=“file” name=“upload_file1”></p>
 <p><input type=“submit” value=“Submit”></p>
 </form>
<h1>Log Console</h1>
 <a href=“https://console.cloud.google.com/logs” target=“_blnak”>GCP Log Console</a>
</body>
</html>

3.実行

OCRにかけるサンプル画像

ソースコードを適当にキャプチャ。

upload_sample_image

アップロードページ

上記アップロードページを起動

スクリーンショット 2020-08-09 12.43.02

 

上記アップロードの、Cloud Vision にサンプルファイルを指定し、submit

結果が返ってきた!OK! ながかった。

スクリーンショット 2020-08-09 12.43.14

GCPのログ

ログにも、想定した結果がはきだされている。

log

4.tscエラー対応

最終的には、以下の対応を行う必要はなかった(冒頭の記述参照)のだが、メモ。

(1) Cannot find name ‘AsyncIterator’

https://github.com/apollographql/graphql-subscriptions/issues/83

tsconfig.json

 “compilerOptions”: {
 “lib”: [
 “esnext.asynciterable”
 ],

(2) require(…) is not a function

https://stackoverflow.com/questions/33007878/nodejs-typeerror-require-is-not-a-function

5.参考

Follow me!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です