S3にアップロードされた画像に透かし(Watermark)を付与してみる。

2015.01.05

はじめに

Lambdaのプレビューも通ったので、S3にあげられた画像に透かしを付与するといったサンプルを作ってみました。

実装方針としては以下になります。

  • 付与する透かしは、静的ファイルとして一緒にLambdaに配置する。
  • 実際の処理部分は別ファイルとし、Lambda外からも呼べるようにする。
  • Lambda外から呼ぶテストコードを作成する。

透かし画像を作成

MacのペインティングソフトとしてFireAlpacaを用いて背景が透過な画像をpng形式で作成しました。

waterMark

透かし付与

Lambdaには、imagemagickが含まれているとのことです。

AWS Lambda: How it Works - AWS Lambda

また、node.js用のwrapperも含まれているようなのですが、こちらは画像合成の方法がなさそうでしたので、

node.js用のwrapperは別途 gmというwrapperを用いることにします。

gm自体はgraphicsmagickのwrapperのようなのですが、オプションを付与することで、imagemagickのwrapperとして使うことが可能です。

npm install gm 

addWaterMark.js

'use strict';
var gm = require('gm');
exports.addWaterMark = function(createImageBuffer, callback) {
    gm(createImageBuffer, 'image.png').options({
        imageMagick: true
    }).composite('./watermark.png').toBuffer('PNG', function(err, buffer) {
        callback(err, buffer);
    });
};

呼び出し用のテストコードを作成します。

/test/addWaterMarkTest.js

'use strict';
var addWaterMark = require('../addWaterMark.js');
var targetFile = './testImage.jpg';
var dstFile = './resultFile.png';
var fs = require('fs');
var assert = require('assert');
before(function(done) {
    if (!fs.existsSync(targetFile)) {
        assert.fail();
    }
    if (fs.existsSync(dstFile)) {
        if (!fs.unlinkSync(dstFile)) {
            assert.fail();
        }
    }
    done();
});
describe('Add Watermark test', function() {
    it('Add Watermark test', function(done) {
        fs.readFile(targetFile, function(err, data) {
            if (!err) {
                addWaterMark.addWaterMark(data, function(err, result) {
                    if (!err) {
                        fs.writeFile(dstFile, result, 'binary', function(err) {
                            if (!err) {
                                done();
                            } else {
                                assert.fail();
                            }
                        });
                    } else {
                        assert.fail();
                    }
                });
            } else {
                assert.fail();
            }
        });
    });
});

テストコードを実行し、問題ないことを確認します。

今回はテストフレームワークである、mochaを用いているためインストールしていない場合はインストールを行います。

また、テスト用の画像として、testImage.jpgを配置する必要があります。

reateWaterMark
├── addWaterMark.js
├── node_modules(省略)
├── test
│   └── addWaterMarkTest.js
├── testImage.jpg
└── watermark.png

mochaのインストールから実行

npm install -g mocha
mocha ./test/addWaterMarkTest.js
open ./resultFile.png

上記でWaterMarkが付与された画像が出力されれば、Watermarkを付与する部分の実装は完了です。

Lambdaからの呼び出し

よくあるLambdaを用いたコードになります。

注意点としては、対象のバケットにオブジェクトが配置されたことをハンドリングするため、 WaterMarkを付与した画像を同一のバケットに配置すると、それもハンドリング対象となり無限ループとなります

無限ループを作らない方法として、 今回の例ではオブジェクトのファイル名をチェックすることで無限ループを作らないようにしています。

lambdaHandler.js

var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var addWaterMark = require('./addWaterMark.js');
exports.handler = function(event, context) {
    var bucket = event.Records[0].s3.bucket.name;
    var key = event.Records[0].s3.object.key;
    if (key.indexOf('-watermark.png') > -1) {
        context.done(null, '');
    }
    console.log('s3 get object');
    s3.getObject({
        Bucket: bucket,
        Key: key
    }, function(err, data) {
        if (err) {
            console.log(err);
            context.done('error', 'error getting file' + err);
        } else {
            console.log('add water mark');
            addWaterMark.addWaterMark(data.Body, function(err, result) {
                if (err) {
                    console.log(err);
                    context.done('error', 'error add WaterMark' + err);
                } else {
                    console.log('put s3 object');
                    var watermarkKey = key.split('.')[0] + '-watermark.png';
                    s3.putObject({
                        Bucket: bucket,
                        Key: watermarkKey,
                        Body: new Buffer(result, 'binary')
                    }, function(err) {
                        if (err) {
                            console.log(err);
                            context.done('error', 'error s3 put object' + err);
                        } else {
                            context.done(null, '');
                        }
                    });
                }
            });
        }
    });
}

Lambdaへの登録

<pLambdaへの登録は弊社佐々木の以下の記事を参考に行います。

初めてのJavaScript、初めてのAWS Lambda | Developers.IO

ただしく配置ができれば、S3へ配置した画像に、Watermarkが付与された画像が配置されます

配置した画像ファイル

black

Watermarkを付与された画像ファイル

black-waterMark

最後に

投稿された画像に対して、共通の処理を行うと言ったケースはLambdaを使う教科書的なケースかと思われます。 今回はその中で、透かしをつける処理を実装しました。

Lambdaを使うことでネイティブとアプリ同時に作ると言った際に、共通化しておきたい処理ををAWS側で共通化でき、 かつ粗結合にしておくことができるものと思われます。

今回作成したソースは以下のURLで公開しております。

cm_kajiwara / lambdaSample / ソース / — Bitbucket

参考サイト

AWS - S3に画像をアップロードしたらLambdaでサムネイルを生成する(node-imagemagick) - Qiita