👩‍🦰

ChatGPT に 画像を一括でシルエット化加工する Script を作ってもらった

2023/03/26に公開

📝やりたいこと

  • シルエットクイズのアプリを作りたい
  • 背景を透過した画像を複数枚用意しているので、それを一括でシルエット画像に変換したい

🗣️chatGPTさんとのやり取り

環境:ChatGPT3.5(無料枠)

👂とりあえず CLI でシルエット化する方法を聞いてみる

👩‍🦰わたし

画像を CLI 上でシルエット加工する方法を教えてください

🤖GPT

画像をCLI上でシルエット化するためには、以下の手順を実行する必要があります。

  1. 画像処理ライブラリーのインストール

画像処理ライブラリーをインストールします。Pythonの場合は、PillowやOpenCVなどのライブラリーを使用できます。以下のコマンドでPillowをインストールできます。
(以下略...)

🧠どうせなら python より使い慣れた Node.js が良い

👩‍わたし

python を使わずに、nodejs を使うとどうなりますか

🤖GPT

Pythonを使用せずに、Node.jsを使用して画像をシルエット化することも可能です。以下の手順を実行することで、Node.jsで画像のシルエット化を行うことができます。

  1. 画像処理ライブラリのインストール

Node.jsには、画像処理ライブラリーとして「Jimp」があります。Jimpは、画像のリサイズ、回転、トリミングなど、さまざまな画像処理機能を提供しています。以下のコマンドで、Jimpをインストールします。

npm install jimp

2.画像の読み込み

Jimpを使用して、シルエット化したい画像を読み込みます。以下のようなコードで実行できます。

const Jimp = require("jimp");

Jimp.read("input_image.jpg")
.then(image => {
 // 画像の加工処理を行う
})
.catch(err => {
  console.error(err);
});

3.画像の加工

Jimpを使用して、画像の加工を行います。ここでは、画像をグレースケールに変換し、閾値処理を行い、シルエット化します。以下のコードで実行できます。

const Jimp = require("jimp");

Jimp.read("input_image.jpg")
.then(image => {
  // グレースケールに変換
  image.greyscale();

  // 閾値処理を行って、シルエット化
  image.threshold(128);

  // シルエット化した画像を保存
  image.write("output_image.jpg");
})
.catch(err => {
  console.error(err);
});

4.画像の保存

加工した画像を保存します。以下のように保存できます。

image.write("output_image.jpg");

以上の手順で、Node.jsで画像をシルエット化することができます。

この時点で、知らなかったJimpというライブラリを教えてくれたので充分仕事している。
実装方法も、ステップに分けて説明してくれるので初学者にもわかりやすそう?

👌問題なさそうだったので、本題のスクリプトを書かせてみる

👩‍わたし

上記のNode.jsのコードに、「特定のディレクトリを与えたら、その中の画像を一括で処理する」機能を追加してください
作成したシルエット化された画像は、ルートディレクトリの "dist" ディレクトリに、同名で出力してください

🤖GPT

以下は、特定のディレクトリ内の画像を一括で処理し、シルエット化した画像を指定のディレクトリに保存する、Node.jsのコード例です。

const fs = require("fs");
const path = require("path");
const Jimp = require("jimp");

// 処理対象の画像ファイルのあるディレクトリ
const imageDir = "/path/to/images";

// 出力先のディレクトリ
const outputDir = "/path/to/dist";

// 処理対象の画像ファイルの拡張子
const fileExt = ".jpg";

// 処理対象の画像ファイルを取得
fs.readdir(imageDir, (err, files) => {
 if (err) {
   console.error(err);
   return;
 }

 // ディレクトリ内の画像ファイルをフィルタリング
 const imageFiles = files.filter(file => {
   return path.extname(file) === fileExt;
 });

 // 各画像ファイルを処理
 imageFiles.forEach(file => {
   const inputFile = path.join(imageDir, file);
   const outputFile = path.join(outputDir, file);

   // 画像の加工処理を行う
   Jimp.read(inputFile)
     .then(image => {
       // グレースケールに変換
       image.greyscale();

       // 閾値処理を行って、シルエット化
       image.threshold(128);

       // シルエット化した画像を保存
       image.write(outputFile);
     })
     .catch(err => {
       console.error(err);
     });
 });
});

できた!!
path/to/ のあたりを 自分の環境に書き直して実行~!

🙅エラーになった

Error: max must be a number

ぐぬぬ...

せっかくだし、GPTでエラー解決までできるかな?と思って試してみる

👩‍わたし

上記のコードを実行したら、"max must be a number" というエラーが返ってきました。
原因と、どう対処すればよいか教えてください。

🤖GPT

"max must be a number" というエラーは、 jimp ライブラリの getBase64 メソッドで発生する可能性があります。このエラーは、指定した画像の幅と高さを使用して、デフォルトの max 値を計算しようとしたときに、 max に数値以外の値(文字列など)が設定されている場合に発生します。
このエラーを解決するためには、 jimp ライブラリの getBase64 メソッドを呼び出す前に、 Jimp.read メソッドで画像を読み込んだ後、画像の幅と高さを取得し、それらの値を getBase64 メソッドに渡す必要があります。以下は、修正したコード例です。

const fs = require("fs");
const Jimp = require("jimp");

const imageFile = "/path/to/image.jpg";

Jimp.read(imageFile)
 .then(image => {
   const width = image.bitmap.width;
   const height = image.bitmap.height;

   // Jimp.getBase64 の呼び出し前に、幅と高さの値をチェックする
   if (typeof width !== "number" || typeof height !== "number") {
     throw new Error("Invalid image dimensions");
   }

   // 画像を Base64 形式に変換
   image.getBase64(Jimp.MIME_JPEG, (err, base64) => {
     if (err) {
       console.error(err);
       return;
     }

     // Base64 形式のデータをファイルに書き出す
     fs.writeFile("image.txt", base64, "base64", err => {
       if (err) {
         console.error(err);
         return;
       }

       console.log("Base64 data saved to image.txt");
     });
   });
 })
 .catch(err => {
   console.error(err);
 });

うーん...
Base64 に変換とかやっていないし、修正後のスクリプトも、シルエット化のスクリプトと別物になってしまっているので、「上記のコードを実行したら」という部分が読み取られていないっぽいなあ

👩‍わたし

(シルエット化のスクリプトをコピペ)
先ほど教えていただいた、上記のコードを Node.js で実行したところ、"max must be a number" というエラーが返ってきました。
エラーにならないようにコードを修正してください。

🤖GPT

以下の修正を加えることで、"max must be a number" というエラーが解消されるはずです。

// 閾値処理を行って、シルエット化
image.threshold({
 max: 255,
 threshold: 128,
});

// シルエット化した画像を保存
image.write(outputFile);

threshold() メソッドにオプションを渡す場合、オブジェクトを引数として渡す必要があります。オブジェクトの中には、max というキーと、その値として 255 を指定する必要があります。これを指定しない場合、デフォルトで max には null が設定されてしまい、エラーが発生します。

動いた・・・
解説も正しそうでした!
https://github.com/jimp-dev/jimp/tree/main/packages/plugin-threshold

🤔出力された画像がグレースケールになっている

出力された画像を確認してみると、白黒にしただけになっていた
やりたいこととしては、黒塗りのシルエットの作成なのでもう一度聞いてみる

👩‍わたし

image.threshold() メソッドで、黒塗りのシルエットに加工する処理を書いてください

🤖GPT

黒塗りのシルエットに加工するには、閾値処理で黒を、閾値以下のピクセルに当てることができます。
以下のように threshold() メソッドのオプションとして max: false を指定することで、閾値以上のピクセルを黒く、閾値以下のピクセルを白く塗りつぶすことができます。

// 閾値処理を行って、シルエット化
image.threshold({ threshold: 128, max: false });

// シルエット化した画像を保存
image.write(outputFile);

max は 数値じゃないとダメって自分で言ったやん・・・

そのあとも何度か質問を繰り返してみましたが、黒塗り化は無理のようでした
手動で以下のように対応したら目的の黒塗りの画像が取得できました~~

image.threshold({ max: 0, replace: 0 });

📝まとめ

  • ChatGPTで画像を一括でシルエット化するスクリプトが書けた
  • エラーになっても、GPT にエラーになったと詰めれば解消してくれた
  • 少し目的とは違うものができたが、骨組みとしては十分
  • ちょっと修正することで目的が達成できた
  • 噂の GPT-4 を試してみたくなった

Discussion