九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、私事ですが高速処理に向いているNode.jsのフレームワーク「Express」の開発をはじめてから少し時間が経ち、徐々に慣れてきた感じがしています(やはり高速レスポンスの噂はホントでした😊)
そして、他の開発でもやっていた複雑な内容も少しずつ取り入れている最中なのですが、やはりと言うかサイト開発をすすめる上で、避けては通れない機能に遭遇しました。
それが・・・
画像の操作
です。
PHPで言うと有名パッケージの「Intervention」があり、以前「完全網羅!Intervention Image(PHP)で画像を編集する全実例」でご紹介しましたが、実はNode.jsにも同じような画像編集パッケージがあったりします。
その名も「sharp」です。
ということで、今回はこの「sharp」の使い方を実例を使って紹介していきたいと思います。
ぜひ皆さんのお役に立てると嬉しいです😊✨

開発環境: Node 8.10
目次
- 1 インストールする
- 2 基本的な使い方
- 3 画像を編集する
- 4 画像を回転させる:rotate()
- 5 画像を反転させる
- 6 鮮明にする(シャープ): sharpen()
- 7 ぼかしをかける
- 8 アルファチャンネルと融合する
- 9 ガンマ補正をかける:gamma()
- 10 色を反転させる(ネガポジ反転):negate()
- 11 ノーマライズ処理をする:normalise()
- 12 コンボリューション(畳み込み処理):convolve()
- 13 しきい値を使って色を変更する:threshold()
- 14 真偽値のビット演算をする:boolean()
- 15 レベルを調整する:linear()
- 16 Recombする:recomb()
- 17 モデレートする:modelate()
- 18 ウォーターマーク(ロゴ)をつける
- 19 画像形式を変換する
- 20 バッファ形式に変換する
- 21 画像のデータを取得する
- 22 元画像のデータ(EXIF, XMP, IPTC)を引き継ぐ:withMetadata()
- 23 おわりに
インストールする
まずはパッケージをインストールします。
以下のコマンドを実行してください。
npm install --save sharp
※ちなみにこのパッケージはlibvipsというライブラリを使うようで、インストール時にダウンロードし、コンパイルをします。そのため、もしかすると少し時間がかかるかもしれません。(C++11が必要)
では、以下の2画像を使って各機能を紹介していきます。(なお、分かりやすくするためにサンプル画像に枠線をつけてるものがありますのでご注意ください)

(sharp_smile.png: 縦256ピクセル x 横256ピクセル)

(vancouver_false_cleek.jpg: 縦640ピクセル x 横360ピクセル)
基本的な使い方
パッケージを読み込む
sharpパッケージを読み込むには以下のようにします。
const sharp = require('sharp');
画像を読み込む:コンストラクタ
画像を読み込む場合は以下のようにします。
const image = sharp('sharp_smile.png');
※なお、引数は画像のパスだけでなくBufferデータでもOKです。
画像を保存する
画像を保存する場合はtoFile()を使います。
image.toFile('new_smile.png')
.then(info => {
console.log(info);
})
.catch(error => {
console.log(error);
});
なお、画像の保存が成功したときに取得できるinfoは以下のような内容になっています。
- format ・・・ 画像の種類(jpegなど)
- width ・・・ 横幅(px)
- height ・・・ 縦幅(px)
- channel ・・・ 色チャンネル数
- premultiplied ・・・ アルファチャンネル
- size ・・・ ファイルサイズ(bytess)
※画像のフォーマット(jpeg, pngなど)が明確でない場合は拡張子から推測して自動的にフォーマットが変換されることになります。
※Exifなどの画像データは全て削除された状態です。もし元画像のデータを引き継ぎたい場合はwithMetadata()を併用してください。
画像を編集する
リサイズ:resize()
シンプルな使い方
もっとも簡単にリサイズするには、第1引数と第2引数にそれぞれ横幅と縦幅をセットします。
const width = 200; const height = 100; image.resize(width, height)
ただし、縦横比が元画像と違う場合は以下のようにカットされてしまいます。

もし、縦か横を基準にして自動的に計算してほしい場合はnullを使います。
image.resize(100, null); // 横を基準に縦を自動計算
image.resize(null, 100); // 縦を基準に横を自動計算
この場合は、どちらも以下のようになります。

画像が歪ませてリサイズ
例えば、強制的に横を200px、縦を100pxにしてリサイズする場合です。
image.resize({
width: 200,
height: 100,
fit: 'fill'
})
結果はこうなります。
→ 
画像を歪ませず、サイズは指定のものにする場合
例えば、画像の縦横比は変更せず(画像を歪ませず)、画像のサイズは指定したものにしたい場合です。
image.resize({
width: 200,
height: 100,
fit: 'contain'
})
→ 
縦 or 横の短い方に合わせてリサイズ
元画像の縦横比はそのままで、「短い方」を基準にしてリサイズする方法です。
image.resize({
width: 200,
height: 100,
fit: 'inside'
})
→ 
※縦の100pxの方が短いので100pxになっています。
縦 or 横の長い方に合わせてリサイズ
元画像の縦横比はそのままで、「長い方」を基準にしてリサイズする方法です。
image.resize({
width: 200,
height: 100,
fit: 'outside'
})
→ 
※横の200pxの方が長いので200pxになっています。
画像がカットされる基準を上下左右で指定する
例えば、オプションのfitをcontainにすると画像が以下のようになりますが、この基準を上下左右どこからにするか指定する場合です。

使えるパラメーターは以下の4つです。
- top
- right
- bottom
- left
では、例として左を基準にしてみましょう。
image.resize({
width: 200,
height: 100,
fit: 'contain',
position: 'left'
})
実際の例はこうなります。

なお、top leftなどとして2つ指定することもできます。
背景色を指定する
fitがcontainの場合、デフォルトでは以下のように空白の部分は色が黒になりますが、例えばこれを赤に変更したい場合です。

image.resize({
width: 200,
height: 100,
fit: 'contain',
background: '#ff0000'
})
結果はこうなります。

もしくは、RGBAで指定する場合はこちら。
image.resize({
width: 200,
height: 100,
fit: 'contain',
background: {
r: 255,
g: 0,
b: 0,
alpha: 0.5
}
})

余白をつける
元画像の周りに余白をつけてリサイズしたい場合です。
image.extend({
top: 10,
bottom: 20,
left: 10,
right: 10,
background: '#ff0000',
// もしくはRGBA
// background: { r: 255, g: 0, b: 0, alpha: 0.5 }
});
→ 
余白を色を基準にして削除する
例えば、元画像のまわりにある余白を自動で削除する場合です。なお、余白エリアは画像の一番上&一番左の色(今回は白)が基準になります。
image.trim(100);
→ 
元画像より大きくならないようにする
例えば、元画像が256x256ピクセルの場合で、これよりもサイズを大きくしないようにリサイズする場合です。
image.resize(300, 350, {
fit: 'contain', // お好みで変更してください
withoutEnlargement: true
});
元画像が256x256ピクセルなので、この場合サイズの変化はありません。
基準の場所から指定のサイズを切り出す
例えば、上20px・左10pxの位置から横100px、縦80pxを切りだす場合です。
→ 
※なお、元画像の範囲内に収まらないパラメーターを使うと、Error: extract_area: bad extract areaというようなエラーになるので気をつけてください。
画像を回転させる:rotate()
例えば、画像を30度右回転させる場合です。
image.rotate(30); // マイナスの数字もOK!
結果はこうなります。

また、背景色を変更する場合は以下のようにします。
image.rotate(30, {
background: '#ff0000',
// もしくはRGBA
// background: { r: 255, g: 0, b: 0, alpha: 0.5 }
});
画像を反転させる
縦方向に反転:flip()
鏡に写したように「縦」に画像を反転する場合です。
image.flip();
→ 
横方向に反転:flop()
鏡に写したように「横」に画像を反転する場合です。
image.flop();
→ 
鮮明にする(シャープ): sharpen()
画像をより鮮明に際立たせる場合です。
const sigma = 30,
flat = 5,
jagged = 8;
image.sharpen(sigma, flat, jagged);
→ 
※パラメーターは省略可です。
ぼかしをかける
通常のぼかしをかける:blur()
image.blur(5);
元画像はこちら。

結果はこうなります。

油絵のようなぼかし(ミディアンブラ−)をかける:median()
image.median(15);
元画像はこちら。

結果はこうなります。

アルファチャンネルと融合する
image.flatten(5);
→ 
※元画像は透過しています。
ガンマ補正をかける:gamma()
image.gamma(3, 3);
元画像はこちら。

結果はこうなります。

※パラメータは省略可です。
色を反転させる(ネガポジ反転):negate()
image.negate();
元画像はこちら。

結果はこうなります。

ノーマライズ処理をする:normalise()
image.normalise();
元画像はこちら。(分かりやすくするために明るく調整しています)

結果はこうなります。

コンボリューション(畳み込み処理):convolve()
image.convolve({
width: 3,
height: 3,
kernel: [-1, 0, 1, -2, 0, 2, -1, 0, 1]
});
元画像はこちら。

結果はこうなります。

しきい値を使って色を変更する:threshold()
例えば、指定したしきい値100以上の色は255、それ以外は0にする場合です。
image.threshold(100);
元画像はこちら。

結果はこうなります。

なお、初期状態では白黒になりますが、以下のようにすると色付きのままで処理ができます。
image.threshold(100, {
grayscale: false
});

真偽値のビット演算をする:boolean()
image.boolean('vancouver_false_cleek_2.jpg', 'and');
例えば、以下の2枚の画像にビット演算をかけます。
1枚目

2枚目

結果はこうなります。

レベルを調整する:linear()
const a = 5; const b = 5; image.linear(a, b);
元画像はこちら。

結果はこうなります。

Recombする:recomb()
image.recomb([ [0.3588, 0.7044, 0.1368], [0.2990, 0.5870, 0.1140], [0.2392, 0.4696, 0.0912], ]);
元画像はこちら。

結果はこうなります。

モデレートする:modelate()
image.modulate({
brightness: 2,
saturation: 50,
hue: 100
});
元画像はこちら。

結果はこうなります。

ウォーターマーク(ロゴ)をつける
例えば、写真の中にロゴを上から重ねた画像を作りたい場合です。
image.composite([
{
input: 'logo.png',
top: 10,
left: 15
}
]);
結果はこうなります。

画像形式を変換する
例えば、画像をpng形式に変換する場合です。
image.toFormat('png');
利用できるパラメータは以下のとおりです。
- jpg
- jpeg
- png
- webp
- tiff
- heic
- heif
- raw
です。
なお、以下のように第2引数でオプションも利用することができます。全てのオプションはこちらのページをご覧ください。
image.toFormat('jpg', {
quality: 80,
progressive: true
});
バッファ形式に変換する
image.toBuffer();
画像のデータを取得する
メタデータを取得する:metadata()
例えば、画像の縦横の幅や形式を取得する場合です。
image.metadata()
.then(metadata => {
console.log(metadata);
});
取得されるデータは以下のようになります。
{
format: 'png',
width: 256,
height: 256,
space: 'srgb',
channels: 4,
depth: 'uchar',
density: 72,
isProgressive: false,
hasProfile: false,
hasAlpha: true
}
チャンネルの統計データを取得する:stats()
image.stats()
.then(stats => {
console.log(stats);
});
例えば、以下のようなデータが取得できます。
{
channels:
[{
min: 231,
max: 255,
sum: 16275890,
squaresSum: 4049541136,
mean: 248.35037231445312,
stdev: 10.638998661970957,
minX: 116,
minY: 11,
maxX: 0,
maxY: 0 },
{ min: 76,
max: 255,
sum: 11490766,
squaresSum: 2329496164,
mean: 175.33517456054688,
stdev: 69.30324983185474,
minX: 117,
minY: 11,
maxX: 0,
maxY: 0 },
{ min: 60,
max: 255,
sum: 11838173,
squaresSum: 2527408971,
mean: 180.6361846923828,
stdev: 77.0445307401497,
minX: 117,
minY: 11,
maxX: 0,
maxY: 0 },
{ min: 255,
max: 255,
sum: 16711680,
squaresSum: 4261478400,
mean: 255,
stdev: 0,
minX: 0,
minY: 0,
maxX: 0,
maxY: 0
}],
isOpaque: true,
entropy: 1.8841541173751466
}
元画像のデータ(EXIF, XMP, IPTC)を引き継ぐ:withMetadata()
image.withMetadata()
.toFile('new_smile.jpg');
おわりに
ということで今回は画像ライブラリ「sharp」の使い方をご紹介しました。
Node.jsでも気軽に画像を回転したり反転させることができるので、とても便利かと思います。
ただ、例えばテキストの追加機能などGitHubのIssueでも要望があるようですが、まだ実装がないようですので、それほど新機能の追加は積極的ではないのかな??とも感じました。(ただし、この記事を書いている時点で1ヶ月以内に複数のアップデートがあるようでしたので、メンテナンス自体はきっちり行われているようです)
ぜひ皆さんも「sharp」を使って画像編集をしてみてくださいね。
ではでは〜!






