Node.js と Jimp で画像をクロップする
ちょっとした画像操作に Groovy を使ってきたが、最近 Node.js を使う機会が増えてきたので、 Jimp を使って画像を操作する方法をメモしておきます.
Chromebook のイラストの右側だけクロップする、に挑戦
このChromebook のイラスト画像を対象にして実験します。
まずは単純に読み込んで 幅 256px の JPG画像 として保存
Node.js の環境がインストールされていることは前提として以下のようにプロジェクトを作成.
mkdir image-crop
cd image-crop
npm init -y
npm install --save jimp
Jimp の使い方は こちらのオフィシャルドキュメント を見ればだいたいわかります.
プロジェクトのルートフォルダに index.js を作成し、以下のコードを記述:
const Jimp = require('jimp');
const imagePath = 'https://osima.jp/imgs/chromebook-flip-c434ta/chromebook-c434ta-w640.png';
Jimp.read(imagePath, (err, image) => {
if (err){ throw err; }
image.resize(256, Jimp.AUTO).write('chromebook.jpg');
});
ここでは画像をリサイズした上で保存していますが、 横または縦のサイズ指定に Jimp.AUTO を使うことで、片方だけ指定でもう片方はJimpにお任せできます.
node index.js
これで chromebook.jpg が生成されます. とても簡単です、すばらしい.
横幅は 256px で指定した通りです.
ただ、背景が透明のPNG画像を JPG にしたので、こんな風に背景が黒になってしまいました.
この問題はあとで対処しましょう.
次にクロップ
画像の右側の Chromebook を開いたイラストだけをクロップします. ドキュメントを見ると...
image.crop( x, y, w, h );
すれば良いようです.
// image width
const width = 256;
const height = 111;
// crop 位置
const left = width/2;
const top = 0;
const right = width;
const bottom = height;
// crop パラメータは x,y,w,h で指定するので:
const x = left;
const y = top;
const w = right - left;
const h = bottom - top;
// Let's crop.
const Jimp = require('jimp');
const imagePath = 'https://osima.jp/imgs/chromebook-flip-c434ta/chromebook-c434ta-w640.png';
Jimp.read(imagePath, (err, image) => {
if (err){ throw err; }
image
.resize(width, Jimp.AUTO)
.crop( x, y, w, h )
.write('chromebook-cropped.jpg');
});
node index.js
した結果:
うまくいきました。
最後に、背景ピクセルを修正
元は透明ピクセルだった背景がJPGにしたことで黒になっているのを修正します.
Jimpは image.scan(x, y, w, h, f);
を使うことで pixel 単位での処理が可能です.
最後のピクセルが処理された段階で、 new Jimp()
して新しいイメージを生成して背景を白にした data を与えます.
つまり:
// image width
const width = 256;
const height = 111;
// crop params
const left = width/2;
const top = 0;
const right = width;
const bottom = height;
// Let's crop.
const Jimp = require('jimp');
const imagePath = 'https://osima.jp/imgs/chromebook-flip-c434ta/chromebook-c434ta-w640.png';
Jimp.read(imagePath, (err, image) => {
if (err){ throw err; }
image.scan(0, 0, image.bitmap.width, image.bitmap.height, (x, y, idx)=> {
//const red = image.bitmap.data[idx + 0];
//const green = image.bitmap.data[idx + 1];
//const blue = image.bitmap.data[idx + 2];
const alpha = image.bitmap.data[idx + 3];
if( alpha==0 ){
// 透明ピクセルの場合→ RGB and Alpha に 255 をリセットすることで白色にチェンジする.
image.bitmap.data[idx + 0] = 255;
image.bitmap.data[idx + 1] = 255;
image.bitmap.data[idx + 2] = 255;
image.bitmap.data[idx + 3] = 255;
}
if (x == image.bitmap.width - 1 && y == image.bitmap.height - 1) {
// 全部のピクセルの処理が終わったので書き出しする.
new Jimp({ data: image.bitmap.data, width: image.bitmap.width, height: image.bitmap.height }, (err, image2) => {
if (err){ throw err; }
image2
.resize(width, Jimp.AUTO)
.crop( left, top, (right-left), (bottom-top) )
.write('chromebook-cropped-with-background-white.jpg');
});
}
});
});
うまくいきました:
まとめ
Jimp を使えば、とても簡単に画像操作ができることがわかりました. Groovy はこの用途ではいらないかな、もはや...