Node.jsでSharpを使用して画像を処理する方法

著者は、テックファンド内のダイバーシティを、Donate for Donationsプログラムの一環として寄付先に選択しました。

はじめに

デジタル画像処理は、コンピュータを使用して画像を分析および操作する方法です。このプロセスには、画像の読み取り、画像を変更または強調するための方法の適用、そして処理された画像の保存が含まれます。ユーザがアップロードしたコンテンツを処理するアプリケーションが画像処理を行うことは一般的です。たとえば、ユーザが画像をアップロードできるウェブアプリケーションを作成している場合、ユーザは不要に大きな画像をアップロードする可能性があります。これは、アプリケーションの読み込み速度に悪影響を与えるだけでなく、サーバのスペースも無駄にします。画像処理を使用すると、アプリケーションはすべてのユーザがアップロードした画像をリサイズおよび圧縮することができ、これによりアプリケーションのパフォーマンスが大幅に向上し、サーバのディスクスペースが節約されます。

Node.jsには、sharpjimp、およびgmモジュールなど、画像を処理するために使用できるライブラリのエコシステムがあります。この記事では、sharpモジュールに焦点を当てます。sharpは、さまざまな画像ファイル形式(JPEGPNGGIFWebPAVIFSVG、およびTIFFなど)をサポートする人気のあるNode.js画像処理ライブラリです。

このチュートリアルでは、sharpを使用して画像を読み取り、メタデータを抽出し、リサイズし、画像形式を変更し、画像を圧縮します。その後、画像を切り抜き、グレースケールにし、回転させ、ぼかします。最後に、画像を合成し、画像にテキストを追加します。このチュートリアルの最後までに、Node.jsで画像を処理する方法についての良い理解が得られます。

前提条件

このチュートリアルを完了するには、以下が必要です:

ステップ1 — プロジェクトディレクトリの設定と画像のダウンロード

コードの記述を開始する前に、この記事で使用するコードと画像を含むディレクトリを作成する必要があります。

ターミナルを開いて、プロジェクトのディレクトリを作成します。mkdirコマンドを使用します:

  1. mkdir process_images

新しく作成したディレクトリに移動します。cdコマンドを使用します:

  1. cd process_images

npm initコマンドを使用してpackage.jsonファイルを作成し、プロジェクトの依存関係を追跡します:

  1. npm init -y

-yオプションは、npmにデフォルトのpackage.jsonファイルを作成するよう指示します。

次に、sharpを依存関係としてインストールします:

  1. npm install sharp

このチュートリアルでは、次の3つの画像を使用します。



次に、curlコマンドを使用してプロジェクトディレクトリ内の画像をダウンロードします。

最初の画像をダウンロードするには、次のコマンドを使用します。これにより、画像がsammy.pngとしてダウンロードされます:

  1. curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/sammy.png

次に、次のコマンドを使用して2番目の画像をダウンロードします。これにより、画像がunderwater.pngとしてダウンロードされます:

  1. curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/underwater.png

最後に、次のコマンドを使用して3番目の画像をダウンロードします。これにより、画像がsammy-transparent.pngとしてダウンロードされます:

  1. curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/sammy-transparent.png

プロジェクトディレクトリと依存関係が設定されているので、画像の処理を開始する準備が整いました。

ステップ2 — 画像の読み取りとメタデータの出力

このセクションでは、画像を読み取り、そのメタデータを抽出するコードを記述します。画像メタデータは、画像に埋め込まれたテキストであり、画像の種類、幅、高さなどの情報を含みます。

メタデータを抽出するには、まずsharpモジュールをインポートし、sharpのインスタンスを作成し、画像パスを引数として渡します。その後、インスタンスにmetadata()メソッドをチェーンして、メタデータを抽出してコンソールにログを出力します。

これを行うには、好きなテキストエディタでreadImage.jsファイルを作成し、開きます。このチュートリアルでは、nanoというターミナルテキストエディタを使用します。

  1. nano readImage.js

次に、ファイルの先頭でsharpをrequireします:

process_images/readImage.js
const sharp = require("sharp");

sharpは、プロミスベースの画像処理モジュールです。sharpのインスタンスを作成すると、プロミスが返されます。thenメソッドを使用してプロミスを解決するか、よりクリーンな構文を持つasync/awaitを使用できます。

async/await構文を使用するには、関数の先頭にasyncキーワードを配置して非同期関数を作成する必要があります。これにより、画像を読み取ったときに返されるプロミスを解決するために関数内でawaitキーワードを使用できます。

readImage.jsファイルで、画像を読み取り、そのメタデータを抽出してコンソールにログを出力する非同期関数getMetadata()を定義します。

process_images/readImage.js
const sharp = require("sharp");

async function getMetadata() {
  const metadata = await sharp("sammy.png").metadata();
  console.log(metadata);
}

getMetadata()は、functionラベルの前に定義されたasyncキーワードを使用して非同期関数です。これにより、関数内でawait構文を使用できます。 getMetadata()関数は画像を読み取り、そのメタデータを含むオブジェクトを返します。

関数本体では、sharp()を呼び出して画像を読み取ります。これには画像パスが引数として渡されます。ここではsammy.pngが使われています。

画像パスの他に、sharp()BufferUint8Array、またはUint8ClampedArrayに格納された画像データを読み取ることもできます。ただし、対象の画像はJPEG、PNG、GIF、WebP、AVIF、SVG、またはTIFFである必要があります。

画像の読み取りにsharp()を使用すると、sharpのインスタンスが作成されます。その後、sharpモジュールのmetadata()メソッドをインスタンスにチェーンします。このメソッドは画像のメタデータを含むオブジェクトを返し、その内容をmetadata変数に格納し、console.log()を使用してログに記録します。

これでプログラムは画像を読み取り、そのメタデータを返すことができます。ただし、実行中にエラーが発生した場合、プログラムはクラッシュします。これを回避するには、エラーが発生したときにそれをキャプチャする必要があります。

そのために、getMetadata() 関数内のコードを try...catch ブロックで囲んでください:

process_images/readImage.js
const sharp = require("sharp");

async function getMetadata() {
  try {
    const metadata = await sharp("sammy.png").metadata();
    console.log(metadata);
  } catch (error) {
    console.log(`An error occurred during processing: ${error}`);
  }
}

try ブロック内では、画像を読み込んでそのメタデータを抽出し、ログに記録します。このプロセス中にエラーが発生した場合、実行は catch セクションに移り、プログラムのクラッシュを防ぎながらエラーをログに記録します。

process_images/readImage.js

const sharp = require("sharp");

async function getMetadata() {
  try {
    const metadata = await sharp("sammy.png").metadata();
    console.log(metadata);
  } catch (error) {
    console.log(`An error occurred during processing: ${error}`);
  }
}

getMetadata();

最後に、getMetadata() 関数を呼び出すために、以下の行を追加してください:

ファイルを保存して終了します。ファイルで行った変更を保存するには、y を入力し、ファイル名を確認するには ENTER キーまたは RETURN キーを押してください。

  1. node readImage.js

次に、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 ファイルを作成し、開きます。

  1. nano resizeImage.js

次のコードを追加して、画像の幅を 150px、高さを 97px にリサイズします:

process_images/resizeImage.js
const sharp = require("sharp");

async function resizeImage() {
  try {
    await sharp("sammy.png")
      .resize({
        width: 150,
        height: 97
      })
      .toFile("sammy-resized.png");
  } catch (error) {
    console.log(error);
  }
}

resizeImage();

resizeImage() 関数は、sharp インスタンスに sharp モジュールの resize() メソッドを連鎖させます。このメソッドはオブジェクトを引数として受け取ります。オブジェクトでは、width プロパティと height プロパティを使用して、希望する画像の寸法を設定します。 width150height97 に設定すると、画像が 150px 幅に、97px の高さになります。

画像をリサイズした後、sharp モジュールの toFile() メソッドを連鎖させます。このメソッドは画像のパスを引数として受け取ります。引数に sammy-resized.png を渡すと、その名前で画像ファイルがプログラムの作業ディレクトリに保存されます。

ファイルを保存して終了します。ターミナルでプログラムを実行します:

  1. node resizeImage.js

出力は得られませんが、プロジェクトディレクトリに sammy-resized.png という名前の新しい画像ファイルが作成されているはずです。

ローカルマシンで画像を開きます。画像は幅が150px、高さが97pxのSammyの画像が表示されるはずです。

画像のサイズを変更できるようになったので、次にリサイズされた画像の形式をpngからjpegに変換し、画像を圧縮して作業ディレクトリに保存します。これを行うには、resize()メソッドの後にチェーンさせるtoFormat()メソッドを使用します。

ハイライトされたコードを追加して、画像の形式をjpegに変更して圧縮します。

process_images/resizeImage.js
const sharp = require("sharp");

async function resizeImage() {
  try {
    await sharp("sammy.png")
      .resize({
        width: 150,
        height: 97
      })
      .toFormat("jpeg", { mozjpeg: true })
      .toFile("sammy-resized-compressed.jpeg");
  } catch (error) {
    console.log(error);
  }
}

resizeImage();

resizeImage()関数内で、sharpモジュールのtoFormat()メソッドを使用して画像の形式を変更して圧縮します。 toFormat()メソッドの最初の引数は、画像を変換する形式を含む文字列です。2番目の引数は、画像を強化して圧縮する出力オプションを含むオプションのオブジェクトです。

画像を圧縮するには、trueに設定したmozjpegプロパティを渡します。これにより、sharpはmozjpegデフォルトで画質を犠牲にせずに画像を圧縮します。オブジェクトにはさらにオプションが指定できます。詳細については、sharpのドキュメントを参照してください。

注意:toFormat()メソッドの第2引数に関して、各画像形式は異なるプロパティを持つオブジェクトを取ります。たとえば、mozjpegプロパティはJPEG画像のみで受け入れられます。

ただし、他の画像形式にもqualitycompressionlosslessなどの同等のオプションがあります。画像の圧縮に使用する画像形式に受け入れ可能なオプションがどのようなものかを確認するためには、ドキュメントを参照してください。

次に、toFile()メソッドに異なるファイル名を指定して、圧縮された画像をsammy-resized-compressed.jpegとして保存します。

今、ファイルを保存して終了し、次のコマンドを使用してコードを実行します:

  1. node resizeImage.js

出力は得られませんが、プロジェクトディレクトリに画像ファイルsammy-resized-compressed.jpegが保存されます。

ローカルマシンで画像を開くと、以下の画像が表示されます:

画像が圧縮されたので、圧縮が成功したことを確認するためにファイルサイズをチェックします。ターミナルで、sammy.pngのファイルサイズを確認するためにduコマンドを実行します:

  1. du -h sammy.png

-hオプションは、キロバイトメガバイトなどのファイルサイズを人間が読みやすい形式で表示します。

コマンドを実行した後、次のような出力が表示されます:

Output
120K sammy.png

この出力は、元の画像が120キロバイトであることを示しています。

次に、sammy-resized.pngのファイルサイズを確認します:

  1. du -h sammy-resized.png

コマンドを実行した後、以下の出力が表示されます:

Output
8.0K sammy-resized.png

sammy-resized.pngは、120キロバイトから8キロバイトに減少しました。これは、リサイズ操作がファイルサイズに影響を与えることを示しています。

次に、sammy-resized-compressed.jpegのファイルサイズを確認します:

  1. du -h sammy-resized-compressed.jpeg

コマンドを実行した後、次の出力が表示されます:

Output
4.0K sammy-resized-compressed.jpeg

sammy-resized-compressed.jpegは、8キロバイトから4キロバイトに減少し、4キロバイト節約されたことを示しています。これにより、圧縮が機能したことがわかります。

画像をリサイズし、形式を変更して圧縮したので、画像をトリミングしてグレースケールに変換します。

ステップ4 — 画像のトリミングとグレースケールへの変換

このステップでは、画像をトリミングし、グレースケールに変換します。トリミングとは、画像から不要な部分を除去するプロセスです。 sammy.png 画像をトリミングするために extend() メソッドを使用します。その後、トリミングされた画像インスタンスに grayscale() メソッドをチェーンしてグレースケールに変換します。

テキストエディターで cropImage.js を作成し、開きます:

  1. nano cropImage.js

cropImage.js ファイルに、以下のコードを追加します:

process_images/cropImage.js
const sharp = require("sharp");

async function cropImage() {
  try {
    await sharp("sammy.png")
      .extract({ width: 500, height: 330, left: 120, top: 70  })
      .toFile("sammy-cropped.png");
  } catch (error) {
    console.log(error);
  }
}

cropImage();

cropImage()関数は、画像を読み込み、切り取られた画像を返す非同期関数です。 try ブロック内では、sharp インスタンスが画像を読み込みます。その後、インスタンスに連結された sharp モジュールの extract() メソッドは、次のプロパティを持つオブジェクトを取ります:

  • width: 切り取りたい領域の幅。
  • height: 切り取りたい領域の高さ。
  • top: 切り取りたい領域の垂直位置。
  • left: 切り取りたい領域の水平位置。

例えば、width500height330 に設定すると、sharp は切り取りたい画像の上に透明なボックスを作成します。ボックスに収まる画像の部分は残り、それ以外は切り取られます:

topleft プロパティは、ボックスの位置を制御します。たとえば、left120 に設定すると、ボックスは画像の左端から 120px の位置に配置され、top70 に設定すると、ボックスは画像の上端から 70px の位置に配置されます。

ボックスに収まる画像の領域が切り取られ、sammy-cropped.png として別の画像として保存されます。

ファイルを保存して終了します。ターミナルでプログラムを実行します:

  1. node cropImage.js

出力は表示されませんが、画像 sammy-cropped.png がプロジェクトディレクトリに保存されます。

ローカルマシンで画像を開きます。画像が切り取られているはずです。

画像を切り抜いたので、画像をグレースケールに変換します。これを行うには、sharp インスタンスに grayscale() メソッドをチェーンします。以下のコードを追加して、画像をグレースケールに変換します:

process_images/cropImage.js
const sharp = require("sharp");

async function cropImage() {
  try {
    await sharp("sammy.png")
      .extract({ width: 500, height: 330, left: 120, top: 70 })
      .grayscale()
      .toFile("sammy-cropped-grayscale.png");
  } catch (error) {
    console.log(error);
  }
}

cropImage();

cropImage() 関数は、切り抜いた画像をグレースケールに変換し、sharp モジュールの grayscale() メソッドをsharp インスタンスにチェーンします。次に、プロジェクトディレクトリに画像を sammy-cropped-grayscale.png として保存します。

ファイルを保存して終了するには、CTRL+X を押します。

ターミナルでコードを実行します:

  1. node cropImage.js

ローカルマシンで sammy-cropped-grayscale.png を開きます。これで、画像がグレースケールで表示されます:

画像を切り抜いて抽出したので、次に回転とぼかしを行います。

ステップ 5 — 画像の回転とぼかし

このステップでは、sammy.png 画像を 33 度の角度で回転させます。そして、回転した画像にガウシアンぼかしを適用します。ガウシアンぼかしは、ガウス関数を使用して画像をぼかす手法であり、ノイズレベルと画像の詳細を減少させます。

テキストエディタで rotateImage.js ファイルを作成します。

  1. nano rotateImage.js

rotateImage.jsファイルに、次のコードブロックを記述して、sammy.png33度の角度で回転させる関数を作成します:

process_images/rotateImage.js
const sharp = require("sharp");

async function rotateImage() {
  try {
    await sharp("sammy.png")
      .rotate(33, { background: { r: 0, g: 0, b: 0, alpha: 0 } })
      .toFile("sammy-rotated.png");
  } catch (error) {
    console.log(error);
  }
}

rotateImage();

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は最高の青です。

  • alpharg、およびbプロパティで定義された色の不透明度を制御します。0または0.0は色を透明にし、1または1.1は色を不透明にします。

alphaプロパティが機能するためには、rg、およびbの値を定義して設定する必要があります。rg、およびbの値を0に設定すると黒色が作成されます。透明な背景を作成するには、まず色を定義し、その後alpha0に設定して透明にします。

今、ファイルを保存して終了します。ターミナルでスクリプトを実行します:

  1. node rotateImage.js

プロジェクトディレクトリでsammy-rotated.pngの存在を確認してください。ローカルマシンで開きます。

画像が33度の角度で回転しているのを見るはずです:

次に、回転した画像をぼかします。これは、sharpインスタンスにblur()メソッドを連結することで実現します。

以下に示すハイライトされたコードを入力して画像をぼかします:

process_images/rotateImage.js
const sharp = require("sharp");

async function rotateImage() {
  try {
    await sharp("sammy.png")
      .rotate(33, { background: { r: 0, g: 0, b: 0, alpha: 0 } })
      .blur(4)
      .toFile("sammy-rotated-blurred.png");
  } catch (error) {
    console.log(error);
  }
}

rotateImage();

rotateImage()関数は、画像を読み込み、回転させ、画像にガウシアンぼかしを適用します。sharpモジュールのblur()メソッドを使用して画像にガウシアンぼかしを適用します。このメソッドは、0.3から1000までのシグマ値を含む単一の引数を受け取ります。たとえば、4を渡すと、シグマ値が4のガウシアンぼかしが適用されます。画像がぼかされた後、ぼかされた画像を保存するパスを定義します。

スクリプトは今、回転した画像をシグマ値4でぼかします。ファイルを保存して終了し、次にターミナルでスクリプトを実行します:

  1. node rotateImage.js

スクリプトを実行した後、ローカルマシン上でsammy-rotated-blurred.pngファイルを開きます。これで、回転した画像がぼやけて表示されるはずです:

画像を回転させてぼかしたので、別の画像を別の画像の上に重ねます。

ステップ6 — composite()を使用した画像の合成

画像合成は、2つ以上の別々の画像を組み合わせて単一の画像を作成するプロセスです。これは、異なる写真から最良の要素を借りて効果を作成するために行われます。別の一般的な用途は、画像にロゴをウォーターマークすることです。

このセクションでは、sammy-transparent.pngunderwater.png の上に合成します。これにより、サミーが海の底を泳いでいるような錯覚が生まれます。画像を合成するには、sharp インスタンスにcomposite() メソッドを連結します。

テキストエディターでcompositeImage.js ファイルを作成し、開きます:

  1. nano compositeImages.js

次に、以下のコードをcompositeImages.js ファイルに追加して、2つの画像を合成するための関数を作成します:

process_images/compositeImages.js
const sharp = require("sharp");

async function compositeImages() {
  try {
    await sharp("underwater.png")
      .composite([
        {
          input: "sammy-transparent.png",
          top: 50,
          left: 50,
        },
      ])
      .toFile("sammy-underwater.png");
  } catch (error) {
    console.log(error);
  }
}

compositeImages()

compositeImages() 関数は、まずunderwater.png 画像を読み込みます。次に、sharp モジュールのcomposite() メソッドを連結します。このメソッドは、引数として配列を取ります。配列には、sammy-transparent.png 画像を読み込む単一のオブジェクトが含まれます。オブジェクトには、次のプロパティがあります:

  • input: 処理された画像の上に合成したい画像のパスを取ります。また、BufferUint8Array、またはUint8ClampedArray を入力として受け取ります。
  • top: 合成したい画像の垂直位置を制御します。top50 に設定すると、sammy-transparent.png 画像がunderwater.png 画像の上端から50pxオフセットされます。
  • left: 画像を他の画像の上に合成する際の水平位置を制御します。 left50 に設定すると、sammy-transparent.pngunderwater.png 画像の左端から50pxオフセットされます。

composite() メソッドは、処理される画像と同じサイズまたはそれより小さいサイズの画像が必要です。

composite() メソッドが行っていることを視覚化するには、それが画像のスタックを作成していると考えてください。 sammy-transparent.png 画像が underwater.png 画像の上に配置されます。

topleft の値は、sammy-transparent.png 画像を underwater.png 画像に対して相対的に配置します。

スクリプトを保存してファイルを終了します。スクリプトを実行して画像合成を作成します。

node compositeImages.js

ローカルマシンで sammy-underwater.png を開きます。これで、sammy-transparent.pngunderwater.png 画像の上に合成された画像が表示されます。

composite() メソッドを使用して画像を合成しました。次のステップでは、composite() メソッドを使用して画像にテキストを追加します。

ステップ7 — 画像にテキストを追加する

このステップでは、画像にテキストを書き込みます。書き込み時点では、Sharpには画像にテキストを追加するためのネイティブな方法がありません。テキストを追加するには、まずスケーラブル・ベクター・グラフィックス(SVG)を使用してテキストを描画するコードを書きます。SVG画像を作成したら、sammy.png画像と合成するためのコードを書き、compositeメソッドを使用します。

SVGは、ウェブ用のベクターグラフィックを作成するためのXMLベースのマークアップ言語です。テキストや円、三角形などの形状を描画したり、イラストやロゴなどの複雑な形状を描画したりできます。複雑な形状は、Inkscapeなどのグラフィックツールを使用して作成し、SVGコードを生成します。SVG形状は、品質を失うことなく任意のサイズにレンダリングおよびスケーリングできます。

テキストエディタでaddTextOnImage.jsファイルを作成して開きます。

  1. nano addTextOnImage.js

addTextOnImage.jsファイルに、以下のコードを追加してSVGコンテナを作成します。

process_images/addTextOnImage.js
const sharp = require("sharp");

async function addTextOnImage() {
  try {
    const width = 750;
    const height = 483;
    const text = "Sammy the Shark";

    const svgImage = `
    <svg width="${width}" height="${height}">
    </svg>
    `;
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage();

addTextOnImage()関数では、widthheighttextsvgImageの4つの変数を定義しています。 widthには整数750が格納され、heightには整数483が格納されています。textには文字列Sammy the Sharkが格納されています。これはSVGを使用して描画するテキストです。

変数svgImagesvg要素を保持しています。 svg要素には、widthおよびheightの2つの属性があります。これらは、前に定義したwidthおよびheight変数を補間します。 svg要素は、指定された幅と高さに応じて透明なコンテナを作成します。

svg要素にwidth750height483として与えたので、SVGイメージはsammy.pngと同じサイズになります。 これにより、テキストがsammy.pngイメージの中心に見えるようになります。

次に、テキストグラフィックを描画します。 Sammy the SharkをSVGコンテナに描画するために、以下のコードを追加します:

process_images/addTextOnImage.js
async function addTextOnImage() {
    ...
    const svg = `
    <svg width="${width}" height="${height}">
    <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;
  ....
}

SVGのtext要素には、xytext-anchorclassの4つの属性があります。 xyは、SVGコンテナに描画するテキストの位置を定義します。 x属性はテキストの水平方向の位置を設定し、y属性は垂直方向の位置を設定します。

x50%に設定すると、テキストがx軸の中央に描画され、y50%に設定すると、テキストがSVGイメージのy軸の中央に配置されます。

text-anchorはテキストを水平方向に揃えます。 text-anchormiddleに設定すると、指定したx座標の中央にテキストが配置されます。

classtext要素にクラス名を定義します。このクラス名を使用してtext要素にCSSスタイルを適用します。

${text}text変数に格納された文字列Sammy the Sharkを補間します。これはSVG画像に描画されるテキストです。

次に、ハイライトされたコードを追加してテキストにCSSを適用します:

process_images/addTextOnImage.js
    const svg = `
    <svg width="${width}" height="${height}">
      <style>
      .title { fill: #001; font-size: 70px; font-weight: bold;}
      </style>
      <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;

このコードでは、fillがテキストの色を黒に変更し、font-sizeがフォントサイズを変更し、font-weightがフォントの太さを変更します。

この時点で、SVGでテキストSammy the Sharkを描画するためのコードを書きました。次に、SVG画像をpngとして保存し、SVGがテキストを描画している様子を確認します。それが完了したら、SVG画像とsammy.pngを合成します。

ハイライトされたコードを追加して、SVG画像をpngとして保存します:

process_images/addTextOnImage.js
    ....
    const svgImage = `
    <svg width="${width}" height="${height}">
    ...
    </svg>
    `;
    const svgBuffer = Buffer.from(svgImage);
    const image = await sharp(svgBuffer).toFile("svg-image.png");
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage();

Buffer.from()はSVG画像からBufferオブジェクトを作成します。バッファはバイナリデータを一時的に格納するメモリ上の領域です。

バッファオブジェクトを作成した後、バッファオブジェクトを入力として使用してsharpインスタンスを作成します。画像パスの他に、sharpはバッファUint9Array、またはUint8ClampedArrayも受け入れます。

最後に、プロジェクトディレクトリにSVG画像をsvg-image.pngとして保存します。

以下は完成したコードです:

process_images/addTextOnImage.js
const sharp = require("sharp");

async function addTextOnImage() {
  try {
    const width = 750;
    const height = 483;
    const text = "Sammy the Shark";

    const svgImage = `
    <svg width="${width}" height="${height}">
      <style>
      .title { fill: #001; font-size: 70px; font-weight: bold;}
      </style>
      <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;
    const svgBuffer = Buffer.from(svgImage);
    const image = await sharp(svgBuffer).toFile("svg-image.png");
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage()

ファイルを保存して終了し、次のコマンドでスクリプトを実行します:

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をインストールします。

  1. sudo apt update
  2. sudo apt install fontconfig

ローカルマシンでsvg-image.pngを開いてください。今、透明な背景でSammy the Sharkというテキストがレンダリングされているはずです。

SVGコードがテキストを描画していることを確認したので、テキストグラフィックスをsammy.pngに合成します。

以下のハイライトされたコードをsammy.png画像にSVGテキストグラフィックス画像を合成するために追加します。

process_images/addTextOnImage.js
const sharp = require("sharp");

async function addTextOnImage() {
  try {
    const width = 750;
    const height = 483;
    const text = "Sammy the Shark";

    const svgImage = `
    <svg width="${width}" height="${height}">
      <style>
      .title { fill: #001; font-size: 70px; font-weight: bold;}
      </style>
      <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;
    const svgBuffer = Buffer.from(svgImage);
    const image = await sharp("sammy.png")
      .composite([
        {
          input: svgBuffer,
          top: 0,
          left: 0,
        },
      ])
      .toFile("sammy-text-overlay.png");
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage();

composite()メソッドは、SVG画像をsvgBuffer変数から読み取り、それをsammy.pngの上端から0ピクセル、左端から0ピクセルの位置に配置します。次に、合成された画像をsammy-text-overlay.pngとして保存します。

ファイルを保存して閉じた後、次のコマンドを使用してプログラムを実行してください。

  1. node addTextOnImage.js

ローカルマシンで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