著者は、テックファンド内のダイバーシティを、Donate for Donationsプログラムの一環として寄付先に選択しました。
はじめに
デジタル画像処理は、コンピュータを使用して画像を分析および操作する方法です。このプロセスには、画像の読み取り、画像を変更または強調するための方法の適用、そして処理された画像の保存が含まれます。ユーザがアップロードしたコンテンツを処理するアプリケーションが画像処理を行うことは一般的です。たとえば、ユーザが画像をアップロードできるウェブアプリケーションを作成している場合、ユーザは不要に大きな画像をアップロードする可能性があります。これは、アプリケーションの読み込み速度に悪影響を与えるだけでなく、サーバのスペースも無駄にします。画像処理を使用すると、アプリケーションはすべてのユーザがアップロードした画像をリサイズおよび圧縮することができ、これによりアプリケーションのパフォーマンスが大幅に向上し、サーバのディスクスペースが節約されます。
Node.jsには、sharp、jimp、およびgmモジュールなど、画像を処理するために使用できるライブラリのエコシステムがあります。この記事では、sharpモジュールに焦点を当てます。sharpは、さまざまな画像ファイル形式(JPEG、PNG、GIF、WebP、AVIF、SVG、およびTIFFなど)をサポートする人気のあるNode.js画像処理ライブラリです。
このチュートリアルでは、sharpを使用して画像を読み取り、メタデータを抽出し、リサイズし、画像形式を変更し、画像を圧縮します。その後、画像を切り抜き、グレースケールにし、回転させ、ぼかします。最後に、画像を合成し、画像にテキストを追加します。このチュートリアルの最後までに、Node.jsで画像を処理する方法についての良い理解が得られます。
前提条件
このチュートリアルを完了するには、以下が必要です:
-
ローカル開発環境にNode.jsをセットアップします。Node.jsとnpmのインストール方法については、Node.jsのインストール方法とローカル開発環境の作成に従ってください。
-
Node.jsプログラムの書き方と実行方法の基本知識。基本を学ぶには、Node.jsで最初のプログラムを書いて実行する方法に従ってください。
-
JavaScriptで非同期プログラミングの基本理解。非同期プログラミングのレビューには、JavaScriptでのイベントループ、コールバック、プロミス、および非同期/待機の理解に従ってください。
ステップ1 — プロジェクトディレクトリの設定と画像のダウンロード
コードの記述を開始する前に、この記事で使用するコードと画像を含むディレクトリを作成する必要があります。
ターミナルを開いて、プロジェクトのディレクトリを作成します。mkdir
コマンドを使用します:
新しく作成したディレクトリに移動します。cd
コマンドを使用します:
npm init
コマンドを使用してpackage.json
ファイルを作成し、プロジェクトの依存関係を追跡します:
-y
オプションは、npm
にデフォルトのpackage.json
ファイルを作成するよう指示します。
次に、sharp
を依存関係としてインストールします:
このチュートリアルでは、次の3つの画像を使用します。
次に、curl
コマンドを使用してプロジェクトディレクトリ内の画像をダウンロードします。
最初の画像をダウンロードするには、次のコマンドを使用します。これにより、画像がsammy.png
としてダウンロードされます:
次に、次のコマンドを使用して2番目の画像をダウンロードします。これにより、画像がunderwater.png
としてダウンロードされます:
最後に、次のコマンドを使用して3番目の画像をダウンロードします。これにより、画像がsammy-transparent.png
としてダウンロードされます:
プロジェクトディレクトリと依存関係が設定されているので、画像の処理を開始する準備が整いました。
ステップ2 — 画像の読み取りとメタデータの出力
このセクションでは、画像を読み取り、そのメタデータを抽出するコードを記述します。画像メタデータは、画像に埋め込まれたテキストであり、画像の種類、幅、高さなどの情報を含みます。
メタデータを抽出するには、まずsharpモジュールをインポートし、sharp
のインスタンスを作成し、画像パスを引数として渡します。その後、インスタンスにmetadata()
メソッドをチェーンして、メタデータを抽出してコンソールにログを出力します。
これを行うには、好きなテキストエディタでreadImage.js
ファイルを作成し、開きます。このチュートリアルでは、nano
というターミナルテキストエディタを使用します。
次に、ファイルの先頭でsharp
をrequireします:
sharp
は、プロミスベースの画像処理モジュールです。sharp
のインスタンスを作成すると、プロミスが返されます。then
メソッドを使用してプロミスを解決するか、よりクリーンな構文を持つasync/await
を使用できます。
async/await
構文を使用するには、関数の先頭にasync
キーワードを配置して非同期関数を作成する必要があります。これにより、画像を読み取ったときに返されるプロミスを解決するために関数内でawait
キーワードを使用できます。
readImage.js
ファイルで、画像を読み取り、そのメタデータを抽出してコンソールにログを出力する非同期関数getMetadata()
を定義します。
getMetadata()
は、function
ラベルの前に定義されたasync
キーワードを使用して非同期関数です。これにより、関数内でawait
構文を使用できます。 getMetadata()
関数は画像を読み取り、そのメタデータを含むオブジェクトを返します。
関数本体では、sharp()
を呼び出して画像を読み取ります。これには画像パスが引数として渡されます。ここではsammy.png
が使われています。
画像パスの他に、sharp()
はBuffer、Uint8Array、またはUint8ClampedArrayに格納された画像データを読み取ることもできます。ただし、対象の画像はJPEG、PNG、GIF、WebP、AVIF、SVG、またはTIFFである必要があります。
画像の読み取りにsharp()
を使用すると、sharp
のインスタンスが作成されます。その後、sharpモジュールのmetadata()
メソッドをインスタンスにチェーンします。このメソッドは画像のメタデータを含むオブジェクトを返し、その内容をmetadata
変数に格納し、console.log()
を使用してログに記録します。
これでプログラムは画像を読み取り、そのメタデータを返すことができます。ただし、実行中にエラーが発生した場合、プログラムはクラッシュします。これを回避するには、エラーが発生したときにそれをキャプチャする必要があります。
そのために、getMetadata()
関数内のコードを try...catch
ブロックで囲んでください:
try
ブロック内では、画像を読み込んでそのメタデータを抽出し、ログに記録します。このプロセス中にエラーが発生した場合、実行は catch
セクションに移り、プログラムのクラッシュを防ぎながらエラーをログに記録します。
最後に、getMetadata()
関数を呼び出すために、以下の行を追加してください:
ファイルを保存して終了します。ファイルで行った変更を保存するには、y
を入力し、ファイル名を確認するには ENTER
キーまたは RETURN
キーを押してください。
次に、node
コマンドを使用してファイルを実行します:
Output{
format: 'png',
width: 750,
height: 483,
space: 'srgb',
channels: 3,
depth: 'uchar',
density: 72,
isProgressive: false,
hasProfile: false,
hasAlpha: false
}
出力は以下のようになります:
画像のリサイズ、画像形式の変更、および画像の圧縮
リサイズは、画像の寸法を変更するプロセスで、画像ファイルのサイズに影響を与えるものです。このセクションでは、画像のリサイズ、画像タイプの変更、および画像の圧縮を行います。画像の圧縮は、画質を落とさずに画像ファイルのサイズを削減するプロセスです。
最初に、sharp
インスタンスから resize()
メソッドを連鎖させて画像をリサイズし、プロジェクトディレクトリに保存します。次に、リサイズされた画像に format()
メソッドを連鎖させて、そのフォーマットを png
から jpeg
に変更します。さらに、format()
メソッドにオプションを渡して画像を圧縮し、ディレクトリに保存します。
テキストエディタで resizeImage.js
ファイルを作成し、開きます。
次のコードを追加して、画像の幅を 150px
、高さを 97px
にリサイズします:
resizeImage()
関数は、sharp
インスタンスに sharp モジュールの resize()
メソッドを連鎖させます。このメソッドはオブジェクトを引数として受け取ります。オブジェクトでは、width
プロパティと height
プロパティを使用して、希望する画像の寸法を設定します。 width
を 150
、height
を 97
に設定すると、画像が 150px
幅に、97px
の高さになります。
画像をリサイズした後、sharp モジュールの toFile()
メソッドを連鎖させます。このメソッドは画像のパスを引数として受け取ります。引数に sammy-resized.png
を渡すと、その名前で画像ファイルがプログラムの作業ディレクトリに保存されます。
ファイルを保存して終了します。ターミナルでプログラムを実行します:
出力は得られませんが、プロジェクトディレクトリに sammy-resized.png
という名前の新しい画像ファイルが作成されているはずです。
ローカルマシンで画像を開きます。画像は幅が150px
、高さが97px
のSammyの画像が表示されるはずです。
画像のサイズを変更できるようになったので、次にリサイズされた画像の形式をpng
からjpeg
に変換し、画像を圧縮して作業ディレクトリに保存します。これを行うには、resize()
メソッドの後にチェーンさせるtoFormat()
メソッドを使用します。
ハイライトされたコードを追加して、画像の形式をjpeg
に変更して圧縮します。
resizeImage()
関数内で、sharpモジュールのtoFormat()
メソッドを使用して画像の形式を変更して圧縮します。 toFormat()
メソッドの最初の引数は、画像を変換する形式を含む文字列です。2番目の引数は、画像を強化して圧縮する出力オプションを含むオプションのオブジェクトです。
画像を圧縮するには、true
に設定したmozjpeg
プロパティを渡します。これにより、sharpはmozjpegデフォルトで画質を犠牲にせずに画像を圧縮します。オブジェクトにはさらにオプションが指定できます。詳細については、sharpのドキュメントを参照してください。
注意:toFormat()
メソッドの第2引数に関して、各画像形式は異なるプロパティを持つオブジェクトを取ります。たとえば、mozjpeg
プロパティはJPEG
画像のみで受け入れられます。
ただし、他の画像形式にもquality
、compression
、lossless
などの同等のオプションがあります。画像の圧縮に使用する画像形式に受け入れ可能なオプションがどのようなものかを確認するためには、ドキュメントを参照してください。
次に、toFile()
メソッドに異なるファイル名を指定して、圧縮された画像をsammy-resized-compressed.jpeg
として保存します。
今、ファイルを保存して終了し、次のコマンドを使用してコードを実行します:
出力は得られませんが、プロジェクトディレクトリに画像ファイルsammy-resized-compressed.jpeg
が保存されます。
ローカルマシンで画像を開くと、以下の画像が表示されます:
画像が圧縮されたので、圧縮が成功したことを確認するためにファイルサイズをチェックします。ターミナルで、sammy.png
のファイルサイズを確認するためにdu
コマンドを実行します:
-h
オプションは、キロバイト、メガバイトなどのファイルサイズを人間が読みやすい形式で表示します。
コマンドを実行した後、次のような出力が表示されます:
Output120K sammy.png
この出力は、元の画像が120キロバイトであることを示しています。
次に、sammy-resized.png
のファイルサイズを確認します:
コマンドを実行した後、以下の出力が表示されます:
Output8.0K sammy-resized.png
sammy-resized.png
は、120キロバイトから8キロバイトに減少しました。これは、リサイズ操作がファイルサイズに影響を与えることを示しています。
次に、sammy-resized-compressed.jpeg
のファイルサイズを確認します:
コマンドを実行した後、次の出力が表示されます:
Output4.0K sammy-resized-compressed.jpeg
sammy-resized-compressed.jpeg
は、8キロバイトから4キロバイトに減少し、4キロバイト節約されたことを示しています。これにより、圧縮が機能したことがわかります。
画像をリサイズし、形式を変更して圧縮したので、画像をトリミングしてグレースケールに変換します。
ステップ4 — 画像のトリミングとグレースケールへの変換
このステップでは、画像をトリミングし、グレースケールに変換します。トリミングとは、画像から不要な部分を除去するプロセスです。 sammy.png
画像をトリミングするために extend()
メソッドを使用します。その後、トリミングされた画像インスタンスに grayscale()
メソッドをチェーンしてグレースケールに変換します。
テキストエディターで cropImage.js
を作成し、開きます:
cropImage.js
ファイルに、以下のコードを追加します:
cropImage()
関数は、画像を読み込み、切り取られた画像を返す非同期関数です。 try
ブロック内では、sharp
インスタンスが画像を読み込みます。その後、インスタンスに連結された sharp モジュールの extract()
メソッドは、次のプロパティを持つオブジェクトを取ります:
width
: 切り取りたい領域の幅。height
: 切り取りたい領域の高さ。top
: 切り取りたい領域の垂直位置。left
: 切り取りたい領域の水平位置。
例えば、width
を 500
、height
を 330
に設定すると、sharp は切り取りたい画像の上に透明なボックスを作成します。ボックスに収まる画像の部分は残り、それ以外は切り取られます:
top
と left
プロパティは、ボックスの位置を制御します。たとえば、left
を 120
に設定すると、ボックスは画像の左端から 120px の位置に配置され、top
を 70
に設定すると、ボックスは画像の上端から 70px の位置に配置されます。
ボックスに収まる画像の領域が切り取られ、sammy-cropped.png
として別の画像として保存されます。
ファイルを保存して終了します。ターミナルでプログラムを実行します:
出力は表示されませんが、画像 sammy-cropped.png
がプロジェクトディレクトリに保存されます。
ローカルマシンで画像を開きます。画像が切り取られているはずです。
画像を切り抜いたので、画像をグレースケールに変換します。これを行うには、sharp
インスタンスに grayscale()
メソッドをチェーンします。以下のコードを追加して、画像をグレースケールに変換します:
cropImage()
関数は、切り抜いた画像をグレースケールに変換し、sharp
モジュールの grayscale()
メソッドをsharp
インスタンスにチェーンします。次に、プロジェクトディレクトリに画像を sammy-cropped-grayscale.png
として保存します。
ファイルを保存して終了するには、CTRL+X
を押します。
ターミナルでコードを実行します:
ローカルマシンで sammy-cropped-grayscale.png
を開きます。これで、画像がグレースケールで表示されます:
画像を切り抜いて抽出したので、次に回転とぼかしを行います。
ステップ 5 — 画像の回転とぼかし
このステップでは、sammy.png
画像を 33
度の角度で回転させます。そして、回転した画像にガウシアンぼかしを適用します。ガウシアンぼかしは、ガウス関数を使用して画像をぼかす手法であり、ノイズレベルと画像の詳細を減少させます。
テキストエディタで rotateImage.js
ファイルを作成します。
rotateImage.js
ファイルに、次のコードブロックを記述して、sammy.png
を33
度の角度で回転させる関数を作成します:
rotateImage()
関数は非同期関数であり、画像を読み込み、33
度の角度で回転した画像を返します。関数内では、sharpモジュールのrotate()
メソッドが2つの引数を取ります。第1引数は33
度の回転角度です。デフォルトでは、sharpは回転した画像の背景を黒くします。黒い背景を削除するには、第2引数としてオブジェクトを渡して背景を透明にします。
オブジェクトにはbackground
プロパティがあり、RGBAカラーモデルを定義するオブジェクトを保持しています。RGBAは赤、緑、青、アルファの頭文字を取ります。
-
r
: 赤色の強度を制御します。整数値0
から255
までを受け付けます。0
は色が使用されていないことを意味し、255
は最高の赤です。 -
g
: 緑色の強度を制御します。整数値0
から255
までを受け付けます。0
は色が使用されていないことを意味し、255
は最高の緑です。 -
b
:青色
の強度を制御します。同様に0
から255
までの整数値を受け付けます。0
は青色が使用されていないことを意味し、255
は最高の青です。 -
alpha
:r
、g
、およびb
プロパティで定義された色の不透明度を制御します。0
または0.0
は色を透明にし、1
または1.1
は色を不透明にします。
alpha
プロパティが機能するためには、r
、g
、およびb
の値を定義して設定する必要があります。r
、g
、およびb
の値を0
に設定すると黒色が作成されます。透明な背景を作成するには、まず色を定義し、その後alpha
を0
に設定して透明にします。
今、ファイルを保存して終了します。ターミナルでスクリプトを実行します:
プロジェクトディレクトリでsammy-rotated.png
の存在を確認してください。ローカルマシンで開きます。
画像が33
度の角度で回転しているのを見るはずです:
次に、回転した画像をぼかします。これは、sharp
インスタンスにblur()
メソッドを連結することで実現します。
以下に示すハイライトされたコードを入力して画像をぼかします:
rotateImage()
関数は、画像を読み込み、回転させ、画像にガウシアンぼかしを適用します。sharpモジュールのblur()
メソッドを使用して画像にガウシアンぼかしを適用します。このメソッドは、0.3
から1000
までのシグマ値を含む単一の引数を受け取ります。たとえば、4
を渡すと、シグマ値が4
のガウシアンぼかしが適用されます。画像がぼかされた後、ぼかされた画像を保存するパスを定義します。
スクリプトは今、回転した画像をシグマ値4
でぼかします。ファイルを保存して終了し、次にターミナルでスクリプトを実行します:
スクリプトを実行した後、ローカルマシン上でsammy-rotated-blurred.png
ファイルを開きます。これで、回転した画像がぼやけて表示されるはずです:
画像を回転させてぼかしたので、別の画像を別の画像の上に重ねます。
ステップ6 — composite()
を使用した画像の合成
画像合成は、2つ以上の別々の画像を組み合わせて単一の画像を作成するプロセスです。これは、異なる写真から最良の要素を借りて効果を作成するために行われます。別の一般的な用途は、画像にロゴをウォーターマークすることです。
このセクションでは、sammy-transparent.png
をunderwater.png
の上に合成します。これにより、サミーが海の底を泳いでいるような錯覚が生まれます。画像を合成するには、sharp
インスタンスにcomposite()
メソッドを連結します。
テキストエディターでcompositeImage.js
ファイルを作成し、開きます:
次に、以下のコードをcompositeImages.js
ファイルに追加して、2つの画像を合成するための関数を作成します:
compositeImages()
関数は、まずunderwater.png
画像を読み込みます。次に、sharp モジュールのcomposite()
メソッドを連結します。このメソッドは、引数として配列を取ります。配列には、sammy-transparent.png
画像を読み込む単一のオブジェクトが含まれます。オブジェクトには、次のプロパティがあります:
input
: 処理された画像の上に合成したい画像のパスを取ります。また、Buffer、Uint8Array、またはUint8ClampedArray を入力として受け取ります。top
: 合成したい画像の垂直位置を制御します。top
を50
に設定すると、sammy-transparent.png
画像がunderwater.png
画像の上端から50pxオフセットされます。left
: 画像を他の画像の上に合成する際の水平位置を制御します。left
を50
に設定すると、sammy-transparent.png
がunderwater.png
画像の左端から50pxオフセットされます。
composite()
メソッドは、処理される画像と同じサイズまたはそれより小さいサイズの画像が必要です。
composite()
メソッドが行っていることを視覚化するには、それが画像のスタックを作成していると考えてください。 sammy-transparent.png
画像が underwater.png
画像の上に配置されます。
top
と left
の値は、sammy-transparent.png
画像を underwater.png
画像に対して相対的に配置します。
スクリプトを保存してファイルを終了します。スクリプトを実行して画像合成を作成します。
node compositeImages.js
ローカルマシンで sammy-underwater.png
を開きます。これで、sammy-transparent.png
が underwater.png
画像の上に合成された画像が表示されます。
composite()
メソッドを使用して画像を合成しました。次のステップでは、composite()
メソッドを使用して画像にテキストを追加します。
ステップ7 — 画像にテキストを追加する
このステップでは、画像にテキストを書き込みます。書き込み時点では、Sharpには画像にテキストを追加するためのネイティブな方法がありません。テキストを追加するには、まずスケーラブル・ベクター・グラフィックス(SVG)を使用してテキストを描画するコードを書きます。SVG画像を作成したら、sammy.png
画像と合成するためのコードを書き、composite
メソッドを使用します。
SVGは、ウェブ用のベクターグラフィックを作成するためのXMLベースのマークアップ言語です。テキストや円、三角形などの形状を描画したり、イラストやロゴなどの複雑な形状を描画したりできます。複雑な形状は、Inkscapeなどのグラフィックツールを使用して作成し、SVGコードを生成します。SVG形状は、品質を失うことなく任意のサイズにレンダリングおよびスケーリングできます。
テキストエディタでaddTextOnImage.js
ファイルを作成して開きます。
addTextOnImage.js
ファイルに、以下のコードを追加してSVGコンテナを作成します。
addTextOnImage()
関数では、width
、height
、text
、svgImage
の4つの変数を定義しています。 width
には整数750
が格納され、height
には整数483
が格納されています。text
には文字列Sammy the Shark
が格納されています。これはSVGを使用して描画するテキストです。
変数svgImage
はsvg
要素を保持しています。 svg
要素には、width
およびheight
の2つの属性があります。これらは、前に定義したwidth
およびheight
変数を補間します。 svg
要素は、指定された幅と高さに応じて透明なコンテナを作成します。
svg
要素にwidth
を750
、height
を483
として与えたので、SVGイメージはsammy.png
と同じサイズになります。 これにより、テキストがsammy.png
イメージの中心に見えるようになります。
次に、テキストグラフィックを描画します。 Sammy the Shark
をSVGコンテナに描画するために、以下のコードを追加します:
SVGのtext
要素には、x
、y
、text-anchor
、class
の4つの属性があります。 x
とy
は、SVGコンテナに描画するテキストの位置を定義します。 x
属性はテキストの水平方向の位置を設定し、y
属性は垂直方向の位置を設定します。
x
を50%
に設定すると、テキストがx軸の中央に描画され、y
を50%
に設定すると、テキストがSVGイメージのy軸の中央に配置されます。
text-anchor
はテキストを水平方向に揃えます。 text-anchor
をmiddle
に設定すると、指定したx
座標の中央にテキストが配置されます。
class
はtext
要素にクラス名を定義します。このクラス名を使用してtext
要素にCSSスタイルを適用します。
${text}
はtext
変数に格納された文字列Sammy the Shark
を補間します。これはSVG画像に描画されるテキストです。
次に、ハイライトされたコードを追加してテキストにCSSを適用します:
このコードでは、fill
がテキストの色を黒に変更し、font-size
がフォントサイズを変更し、font-weight
がフォントの太さを変更します。
この時点で、SVGでテキストSammy the Shark
を描画するためのコードを書きました。次に、SVG画像をpng
として保存し、SVGがテキストを描画している様子を確認します。それが完了したら、SVG画像とsammy.png
を合成します。
ハイライトされたコードを追加して、SVG画像をpng
として保存します:
Buffer.from()
はSVG画像からBufferオブジェクトを作成します。バッファはバイナリデータを一時的に格納するメモリ上の領域です。
バッファオブジェクトを作成した後、バッファオブジェクトを入力として使用してsharp
インスタンスを作成します。画像パスの他に、sharpはバッファ、Uint9Array、またはUint8ClampedArrayも受け入れます。
最後に、プロジェクトディレクトリにSVG画像をsvg-image.png
として保存します。
以下は完成したコードです:
ファイルを保存して終了し、次のコマンドでスクリプトを実行します:
node addTextOnImage.js
注意: もし、Option 2 — NodeSource PPAを使用してNode.jsをインストールまたはOption 3 — Node Version Managerを使用してNode.jsをインストールし、fontconfig error: cannot load default config file: no such file: (null)
というエラーが発生した場合は、フォント構成ファイルを生成するためにfontconfig
をインストールしてください。
サーバーのパッケージインデックスを更新し、その後、apt install
を使用してfontconfig
をインストールします。
ローカルマシンでsvg-image.png
を開いてください。今、透明な背景でSammy the Shark
というテキストがレンダリングされているはずです。
SVGコードがテキストを描画していることを確認したので、テキストグラフィックスをsammy.png
に合成します。
以下のハイライトされたコードをsammy.png
画像にSVGテキストグラフィックス画像を合成するために追加します。
composite()
メソッドは、SVG画像をsvgBuffer
変数から読み取り、それをsammy.png
の上端から0
ピクセル、左端から0
ピクセルの位置に配置します。次に、合成された画像をsammy-text-overlay.png
として保存します。
ファイルを保存して閉じた後、次のコマンドを使用してプログラムを実行してください。
ローカルマシンでsammy-text-overlay.png
を開いてください。画像にテキストが追加されているはずです。
composite()
メソッドを使用して、別の画像にSVGで作成したテキストを追加しました。
結論
この記事では、Node.jsで画像を処理するためのシャープな方法を学びました。まず、画像を読み込むためのインスタンスを作成し、metadata()
メソッドを使用して画像のメタデータを抽出しました。次に、resize()
メソッドを使用して画像のサイズを変更しました。その後、format()
メソッドを使用して画像の種類を変更し、画像を圧縮しました。次に、画像を切り取り、グレースケールに変換し、回転させ、ぼかすためにさまざまなシャープな方法を使用しました。最後に、composite()
メソッドを使用して画像を合成し、画像にテキストを追加しました。
追加のシャープな方法についてのさらなる洞察を得るには、sharpのドキュメントを参照してください。Node.jsの学習を続けたい場合は、Node.jsでのコーディングシリーズをご覧ください。
Source:
https://www.digitalocean.com/community/tutorials/how-to-process-images-in-node-js-with-sharp