はじめに

Node.js の画像変換モジュール sharp を使ったリサイズ方法について調べた。

TL;DR

  • npm install sharp のみで使える
  • ImageMagick より 4〜5倍ほど高速(らしい)
  • sharp("path").resize(width, height).toFile("path") でリサイズして出力と簡単

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. sharp とは
      1. 特徴
    2. セットアップ
    3. 使い方
      1. 基本形
    4. ファイル出力
    5. リサイズ
      1. 幅のみ指定
      2. 高さのみ指定
      3. 幅、高さ指定(fit: cover)
      4. 幅、高さ指定(fit: contain)
      5. 幅、高さ指定(fit: fill)
      6. 幅、高さ指定(fit: inside)
      7. 幅、高さ指定(fit: outside)
      8. 幅、高さ指定(fit: cover/contain, position: xxx)
      9. 背景色指定
  5. まとめ
  6. その他・メモ
  7. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.5
BuildVersion: 18F132

$ nodenv --version
nodenv 1.3.0

$ node --version
v12.7.0

$ npm --version
6.10.0

$ npm v sharp
sharp@0.23.0 | Apache-2.0 | deps: 9 | versions: 87

詳細

sharp とは

Node.js で使用可能な画像変換モジュール。

特徴

  • JPEG, PNG, WebP, TIFF, DZI などに対応
  • リサイズ、回転、ガンマ補正などの操作が可能
  • npm install sharp のみ行えば OK(追加インストールなどは不要)
  • ImageMagick, GraphicsMagic より 4〜5 倍高速(らしい)

ベンチマーク
Performance - sharp より引用

セットアップ

npm install のみ。

1
$ npm install sharp

使い方

画像変換を行うにあたって、インプットは Pixabayのこの写真 を 1920x1080 でダウンロードして、aerial_1920.jpg として保存した。

オリジナル画像

基本形

require でロードして使う。

1
const sharp = require("sharp");

コールバック、Promiseasync/await どれでもいける。

画像を読み込んで、横幅960pxに変換(アスペクト比 維持)して、aerial_960.jpg として保存するコードを例に各ケースの書き方を示す。

非同期(コールバック)
1
2
3
4
5
6
7
8
9
const sharp = require("sharp");
sharp("aerial_1920.jpg")
.resize(960)
.toFile("aerial_960.jpg", (err, info) =>{
if (err) {
console.error(err);
}
console.log(info);
});
Promise
1
2
3
4
5
6
7
8
9
10
const sharp = require("sharp");
sharp("aerial_1920.jpg")
.resize(960)
.toFile("aerial_960.jpg")
.then(data =>{
console.log(data);
})
.catch(err => {
console.error(err);
});
async/await
1
2
3
4
5
6
7
8
9
10
11
12
13
const sharp = require("sharp");
async function resize() {
try{
const data = sharp("aerial_1920.jpg")
.resize(960)
.toFile("aerial_960.jpg");
console.log(data);
}
catch (err) {
console.error(err);
}
}
resize();
await/catch (おまけ)

async関数においてtry/catchではなくawait/catchパターンを活用する - Qiita を読んだので、try/catch ではなく await/catch で試しに書いてみた。

なるほど、確かにスッキリした感がある。

1
2
3
4
5
6
7
8
9
10
11
const sharp = require("sharp");
async function resize() {
const data = await sharp("aerial_1920.jpg")
.resize(960)
.toFile("aerial_960.jpg")
.catch (err => {
console.error(err);
});
console.log(data);
}
resize();

ファイル出力

デフォルトパラメータでファイル出力する場合には toFile メソッドだけで良いが、細かいパラメータを調整したい場合には拡張子ごとのメソッドを利用する。

jpeg で品質を 60(デフォルトは 80)にして保存したい場合には以下の通り。

1
2
3
4
5
6
7
sharp("input.jpg")
.jpeg({
quality: 60
})
.toFile("output.jpg", (err, info) => {
console.log({err: err, info: info});
});

リサイズ

resize を使う。

幅(width)のみ、高さ(height)のみが指定された場合は、アスペクト比を維持したまま変換となる。

幅(width)、高さ(height)両方が指定された場合は、変換方法を fit で指定することができる。

  • cover: アスペクト比維持、はみ出る部分はトリミング
  • contain: アスペクト比維持、はみ出ないように縮小し、余白部分は塗りつぶし
  • fill: アスペクト比無視、指定サイズに引き延ばし
  • inside: アスペクト比維持、小さい方に合わせて変換
  • outside: アスペクト比維持、大きい方に合わせて変換

covercontain を選択した場合、position でトリミング位置を指定することができる。

幅のみ指定

resize(<width>) とする。

1
sharp("aerial_1920.jpg").resize(500);  // 幅 500px に変換(高さは自動調整)

もしくは、resize({width: <width>}) とする。

1
sharp("aerial_1920.jpg").resize({width: 500});  // 幅 500px に変換(高さは自動調整)

幅500

高さのみ指定

resize(<height>) とする。

1
sharp("aerial_1920.jpg").resize(null, 300);  // 高さ 300px に変換(幅は自動調整)

もしくは、resize({height: <height>}) とする。

1
sharp("aerial_1920.jpg").resize({height: 300});  // 高さ 300px に変換(幅は自動調整)

高さ300

幅、高さ指定(fit: cover)

幅、高さを指定して fit を省略した場合は cover となる。

cover: Crop to cover both provided dimensions (the default).

指定サイズに合うように、はみ出た部分はトリミングされる。

1
sharp("aerial_1920.jpg").resize({width: 500, height: 200});

500x200(cover)

幅、高さ指定(fit: contain)

contain: Embed within both provided dimensions.

アスペクト比を維持して、指定サイズ内に埋め込む。余白部分は塗りつぶし。

1
2
3
4
5
sharp("aerial_1920.jpg").resize({
width: 500,
height: 200,
fit: "contain"
});

500x200(contain)

幅、高さ指定(fit: fill)

fill: Ignore the aspect ratio of the input and stretch to both provided dimensions.

指定サイズに合わせて元画像を変換(アスペクト比を無視)する。

1
2
3
4
5
sharp("aerial_1920.jpg").resize({
width: 500,
height: 200,
fit: "fill"
});

500x200(fill)

幅、高さ指定(fit: inside)

inside: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.

アスペクト比を維持して、「元画像の widthvs 指定 width」、「元画像の height vs 指定 height」の小さい方に合わせて変換。

1
2
3
4
5
sharp("aerial_1920.jpg").resize({
width: 500,
height: 200,
fit: "inside"
});

500x200(inside)

幅、高さ指定(fit: outside)

outside: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified. Some of these values are based on the object-fit CSS property.

アスペクト比を維持して、「元画像の widthvs 指定 width」、「元画像の height vs 指定 height」の大きい方に合わせて変換。

1
2
3
4
5
sharp("aerial_1920.jpg").resize({
width: 500,
height: 200,
fit: "outside"
});

500x200(outside)

幅、高さ指定(fit: cover/contain, position: xxx)

fitcover, contain にした場合、position で、トリミング位置などを変更できる。

position には top, right top, right, right bottom, bottom, left bottom, left, left top が指定できる。

今回の画像だと position: "top", position: "right top", position: "left top" は同じ結果になるので、一部のみ記載する。

あと gravity というパラメータもあったけど、これはよくわからなかった。(試したけど全部同じ画像になった。)

fit: cover, position: top
1
2
3
4
5
6
sharp("aerial_1920.jpg").resize({
width: 500,
height: 200,
fit: "cover",
position: "top"
});

500x200(cover_position_top)

fit: cover, position: bottom
1
2
3
4
5
6
sharp("aerial_1920.jpg").resize({
width: 500,
height: 200,
fit: "cover",
position: "bottom"
});

500x200(cover_position_bottom)

背景色指定

fit: "contain" の塗りつぶし色を background で変更できる。

1
2
3
4
5
6
sharp("aerial_1920.jpg").resize({
width: 500,
height: 200,
fit: "contain",
background: {r:0, g:0, b:128, alpha:1}
});

500x200(contain_bg_blue)

まとめ

  • npm install sharp のみで使える
  • ImageMagick より 4〜5倍ほど高速(らしい)
  • sharp("path").resize(width, height).toFile("path") でリサイズして出力と簡単

その他・メモ

リサイズ以外にも rotateflip などのメソッドがあるので、ちょっとした変換には便利そう。

参考文献

関連記事