巨大な画像を任意のサイズに分割する処理を自動化する

Android をはじめとするモバイルデバイスでは、回線の貧弱さやスペックの問題から余りに大きな画像を読み込むのは難しい。「特定のサイズを超える」あるいは「特定の容量を超える」画像は、端末によってはそもそも表示できない場合もある。
リクエスト数と天秤にかける必要はあるが、余りにも巨大な画像はいっそ分割してしまうのがひとつの回避策だ。

ただし、いちいちグラフィックツールなどを利用してスライスするのは手間がかかりすぎるので、Gulp に組み込んだ。前置きが長くなったけど今回は以下の2点の要件で作った。

  1. 任意のサイズで分割できる
  2. Gulp のタスクとして定義し、自動化する

シンプル! 定義したタスクは次のようなものだ。

gm = require("gm"),

gulp.task("crop", function () {

  // 分割する元画像
  var cropImagePath = "./path/to/image"; // 分割する元画像
  var outputPath = "./path/to/dir/"; // 分割した画像群の出力先
  var cropImageSizeX = 1800; // 元画像の幅
  var cropImageSizeY = 3000; // 元画像の高さ
  var cropSizeX = 300; // 分割する画像の幅
  var cropSizeY = 300; // 分割する画像の高さ
  var cropLength = cropImageSizeX * cropImageSizeY / ( cropSizeX * cropSizeY ); // 分割する数を計算
  var cropLengthX = cropImageSizeX / cropSizeX; // x軸にいくつ分割するかを計算
  var cropLengthY = cropImageSizeY / cropSizeY; // y軸にいくつ分割するかを計算

  var x  = 0;
  var y  = 0;

  var cropPosX;
  var cropPosY;

  // 分割する数だけ for 文を回す
  for ( var i = 1; i <= cropLength; i++ ) {
    x += 1;

    if( x > cropLengthX ) {
      x = 1;
      y += 1;
    }

    if( y > cropLengthY ) {
      y = 1;
    }

    // 切り出す座標を計算
    cropPosX = ( cropSizeX * x ) - cropSizeX;
    cropPosY = ( cropSizeY * y );

    // gm を使って crop
    gm( cropImagePath )
      .crop( cropSizeX, cropSizeY, cropPosX, cropPosY )
      .write( outputPath + "map_01-" + i + ".jpg", function ( i_err ) {

        // エラー処理
        if ( !i_err ) {
          console.log( "cropped!" );
        } else {
          console.log( err );
        }
      })
  };
});

これを実行するとこのようになる。 gulpの実行結果

実装内容はコード内のコメントの通りで、最初に諸々の情報を計算して変数に入れておいて、ループ処理させ、ループ内で分割する座標の位置を計算して切り出す。 crop 処理には gm という npm モジュールを利用しているので、他にもやりたいことがあれば github を参照してください。

このように自由にカスタマイズできるのが Gulp のいいところですね。 ただし、この方法は「規則性がある分割」に限定されるので、トリッキーにやりたい場合はやっぱりなにかしらのツールを利用した方がいい。はあ...