文字認識ライブラリ okrabyte を使ってS3にPUTされた画像をAWS Lambdaで文字認識する

このエントリはAWS Lambda Advent Calendar 2014 - Qiitaの(本当は9日目の)記事です。
(ちょっとAWS Lambdaのアカウント取得に手間取ってしまって遅刻してしまいました。。すいません。。)

AWS Lambda とは

AWS LambdaはAmazonが作った新サービスであり、S3の変更やDynamoDBの変更をフックしてNode.jsの関数を実行するという仕組みになっています。これを使うと、例えばデータが変更された後にPush Notificationを出したり、S3にアップロードされた画像のサムネイル画像を作成したりといったことが可能になります。

今回はこのAWS Lambdaを使ってS3でPUTされた画像から文字認識をしてみようという試みです。文字認識した結果をDynamoDBとかに置いて検索に利用すれば、画像内に書かれた文字で検索できるようになったりしてハッピー。

okrabyte

okrabyteAWS Lambdaで文字認識するために僕が書いたライブラリです。

C++で書かれた文字認識ライブラリをemscriptenによってJS化されたocrad.jsを内部的に利用しています。ocrad.jsがcanvasを内部的に使っていたんですが、そこをcanvasじゃなくても文字認識できるようにアダプターを書いてnode.jsっぽく非同期に呼べるように加工したものです。*1

Getting Started okrabyte

こんな感じにインストールして、

$ npm install okrabyte -S

こういう画像を用意して、

https://github.com/yosuke-furukawa/okrabyte/raw/master/test/fixture/hello_world.png

以下のようにAPIを呼び出せばOK!

var okrabyte = require("okrabyte");
okrabyte.decodeFile("test/fixture/hello_world.png", function(error, data){
  console.log(data); // Hello World!
});

okrabyteの詳細はまた今度書きます。

ではこのokrabyteを使ってAWS Lambdaで画像認識をしてみましょう。

S3でPUTされた画像をAWS Lambda から取得して文字認識する

S3から画像をGETして画像認識させるために以下のようなファイルを書きます。

var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var okrabyte = require('okrabyte');

exports.handler = function(event, context) {
   var bucket = event.Records[0].s3.bucket.name;
   var key = event.Records[0].s3.object.key;
   s3.getObject({Bucket:bucket, Key:key}, function(err,data) {
        if (err) {
           console.log('error getting object ' + key + ' from bucket ' + bucket + 
               '. Make sure they exist and your bucket is in the same region as this function.');
           context.done('error','error getting file'+err);
        } else {
           // 今のところ、okrabyteがpng以外をサポートしてないので一旦エラーにする
           if (data.ContentType !== "image/png") context.done('error', 'support png only.');
           // ここで、okrabyteを使って文字認識させる
           okrabyte.decodeBuffer(data.Body, function(err, data){
             if (err) context.done('error', '' + err);
             // 一旦stdoutに出力させる、ここをDynamoDBへのinsertに変えるとかすれば、画像内の文字で画像を検索できるようになって捗りそう。
             console.log(data); 
             context.done(null,'');
           });
        }
      }
   );
};
Execution Role
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:*"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::*"
      ]
    }
  ]
}
Invoke Role
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Resource": [
        "*"
      ],
      "Action": [
        "lambda:InvokeFunction"
      ]
    }
  ]
}

Demo

動かしてみましょう。

画像

f:id:yosuke_furukawa:20141222085912p:plain

この画像をAWS Lambdaに渡して、AWS Lambda amazonという文字列を認識させるようにします。console.logとして、ログに文字列が出力されればゴール。

S3から画像をPUTする。

f:id:yosuke_furukawa:20141222090135p:plain

S3コンソールからUploadボタンを押して、先ほどの画像をUploadします。

CloudWatch Logからログを確認する。

f:id:yosuke_furukawa:20141222090905p:plain

やっふー!AWS Lambda amazonっていう文字列が出力されてますね!
色々試しましたが、独特の表現があるフォントや背景がごちゃごちゃしている画像は認識精度に難があるものの、ある程度はっきり見えているとちゃんと認識されます。

Noderから見るAWS Lambda

AWS LambdaはNode.jsの可能性の一つであり、イベント駆動でサービスを構築できる非常に面白い機能です。これまで、Node.jsでタブーとされてきたイベントループを止めるようなCPUを使った処理が、AWS Lambda経由で実行されることで自分のサービス内のプロセスのイベントループを止める事無く実現できます。つまり、Node.jsが苦手と言われてきたCPUを使ったイベントループを止めるような処理をAWS Lambda側に逃がして非同期で処理させることができるようになります。

AWS Lambdaへの要望

文字認識ライブラリとして代表的なのはtesseractなんですが、こういう任意のコマンドが事前にビルドできると嬉しいなと思いました。
(まぁtesseractがなかったので、okrabyteみたいなpure JSのOCRライブラリができたので、それはそれで副次的な効果を産んで良いとは思いますが。)

まとめ

  • okrabyteっていう文字認識ライブラリを作りました。


  • AWS LambdaでS3にPUTされた画像を使って文字認識をしてみました。

今回使ったコード一式は以下のリポジトリに上げました。




そんなAWS Lambdaですが、今日は初のmeetupが開催されるようです。


AWS Lambda Meetup #0 - connpass

僕は補欠で応募中ですが、既に30人以上の補欠がいるので、ちょっと厳しいかも。またなにか作ったらお知らせします!

*1:ちなみに名前の由来はOCRとオクラからです。Ocraっていう名前にしようかなとも思ったんですけど、既にRubyで同名のGemがあるみたいなんでやめました。