秋の天皇賞の外枠不利を同僚に話したところ、
競馬を知らない方には新鮮な情報だったようで、とても驚かれました。
どうでもいいですね。アジャストの渕上です。

相変わらずブログの持ち回りが早くて疲労困憊です。
終始競馬の話を書いててよければどうにでもなるのですが、
そういうわけにもいかなそうですので、
今回も先週に引き続きnode.jsで簡単に画像の加工ができる
モジュールをご紹介していきたいと思います!

今回は、gm(GrahicsMagick for node)を使って、
2枚の画像の重ね合わせを行ってみます。

GraphicsMagickのインストール

nodeのmoduleをインストールする前に、GraphicsMagick本体をインストールしておきます。

GraphicsMagickはImageMagickの派生ライブラリで、
処理速度に定評があるそうです。

(既に方々で比較されてるとは思いますが)ImageMagickとの処理速度比較を行ってみても面白いかもしれませんね。

Ubuntuの仮想マシンで作業を行ったので、aptでインストールしました。
CentOSでやる場合は、たぶんyumでインストールできると思います。
(今回はyumにパッケージがあるか調べていません。あしからず。)

$ sudo add-apt-repository ppa:dhor/myway
$ sudo apt-get update
$ sudo apt-get install graphicsmagick

特に事件も起こらず、するっとインストールできました。

gmのインストール

npmコマンドでインストールしましょう。

$ npm install gm

gmで実装してみる

前回とほぼ同じですが、サーバ側のコードを下記のように書き換えました。

  1 var express  = require('express');
  2 var app      = express();
  3 var multer   = require('multer');
  4 var fs       = require('fs');
  5 var gm       = require('gm');
  6
  7
  8 var upload   = multer({dest: 'uploads'});
  9
 10 app.set('view engine', 'jade');
 11 app.set('views', __dirname + '/views');
 12
 13
 14 app.get('/', function(req, res) {
 15   res.render('index', {title: 'test'});
 16 });
 17
 18 app.post('/merge', upload.fields([ {name: 'base_img', maxCount: 1}, {name: 'over_img', maxCount: 1} ]), function(req, res) {
 19
 20   /*
 21    * この処理は下記のコマンドと同じ、、のはず。
 22    * gm composite -quality 100 -geometry +10+10 {元の画像} {重ねる画像}
 23    */
 24   gm(req.files.base_img[0].path)           // 元の画像
 25     .composite(req.files.over_img[0].path) // 重ねる画像
 26     .geometry('+10+10')                    // 重ねる画像の位置(標準は左上起点)
 27     .quality(100)                          // クオリティはとりあえず最大
 28     .stream()
 29     .pipe(res);
 30     /*
 31      * 直接画像をレスポンスするのでなく、テンプレートに当てはめる場合は
 32      * .stream().pipe(res)
 33      * をやめて下記のようにできる。(dataURL化してimgタグに埋め込むようにする)
 34     .toBuffer('PNG', function(error, buffer){
 35       res.render('merge', {resultImg: 'data:image/png;base64,'+buffer.toString('base64')});
 36     });
 37     */
 38 });
 39
 40 app.locals.pretty = true;
 41 app.listen(8080);

上記のうち、GraphicsMagickでの操作を行っているのは↓の部分です。
GraphicsMagickのgmコマンドに渡すオプションや引数をメソッドチェーンで渡していくことができます。

 20   /*
 21    * この処理は下記のコマンドと同じ、、のはず。
 22    * gm composite -quality 100 -geometry +10+10 {元の画像} {重ねる画像}
 23    */
 24   gm(req.files.base_img[0].path)           // 元の画像
 25     .composite(req.files.over_img[0].path) // 重ねる画像
 26     .geometry('+10+10')                    // 重ねる画像の位置(標準は左上起点)
 27     .quality(100)                          // クオリティはとりあえず最大
 28     .stream()                              // gmの出力ストリームをレスポンスにつなぐ(パイプする)
 29     .pipe(res);
 30     /*
 31      * 直接画像をレスポンスするのでなく、テンプレートに当てはめる場合は
 32      * .stream().pipe(res)
 33      * をやめて下記のようにできる。(dataURL化してimgタグに埋め込むようにする)
 34     .toBuffer('PNG', function(error, buffer){
 35       res.render('merge', {resultImg: 'data:image/png;base64,'+buffer.toString('base64')});
 36     });
 37     */

GraphicsMagickのコマンドを把握している人なら、
gm moduleのREADMEを眺めればなんとなくわかるかもしれません。

私は、GraphicsMagick自体触ったことがなかったので、
GraphicsMagickのドキュメントと照らし合わせながら、何度かトライ&エラーを繰り返しましたが、

GraphicsMagickのオプションやコマンド名とgmモジュールのメソッド名が大体同じだったので、
そんなに苦労せずにゴールに行き着くことができました。

動作デモ

ベースにする画像 (penguin.jpg)

penguin

重ねる画像 (logo.png)

logo

ブラウザ操作の動画キャプチャ

2015-11-02 16h43_30

感想

gmモジュール編、いかがだったでしょうか。

今回や前回の例のように画像をPOSTさせて合成して返すだけでは、PHPなどを使った実装と結果に大差ないですが、
WebSocketなどと組み合わせると、なんか面白いものが作れるのではないでしょうか。(アバウトですいません。)

また、今回なんとなくStreamを使ってみましたが、
node.jsを触り始めたばかりで、Streamの仕組みをよく理解できていないので、
腰を据えて勉強してみたいなと思います。