AI生成の動作例用写真 / 編集前
DEVELOPMENT JOURNAL / 07
Photoshot
GITHUB × AI
無料Photoshop代替の開発記

色の範囲を選ぶコード

色相を変える実装やRGB・HSL変換はすでにありました。しかし写真で選んだ色を、編集できる範囲につなぐ作業は残っていました。Photoshotの色相・彩度は、範囲計算、スポイト、設定保存を既存エンジンへAIと接続した例です。

PHOTOSHOT DEVELOPMENT JOURNAL / 07

AI生成の動作例用写真。編集画面と出力画像は実際のPhotoshotで作成しました。

Photoshot無料Photoshop代替の開発記 / 07FOUNDATION

ソースから始める。

色変換だけでは操作できる道具にならない

HSLの式があることと、写真の青を選んで調整できることは別の段階です。既存の色変換とマスクに、範囲の境界、画面入力、保存ルールをつなぐ必要がありました。AIはその間のコードの作成と修正に使いました。

既存のエンジンにはRGBとHSLの変換関数、マスクとレイヤー不透明度を扱う共通処理がありました。そこに色範囲を保存するbandsと、画像から色を拾うツールを接続しました。

初期セッションにはKonvaのHSL.tsを参照した記録もあります。ただし、それだけで範囲選択コードをコピーしたとは言えません。ここで示すのはGitHubに公開したポトショット自身の実装です。外部フィルターの導入と、既存エンジンへの追加実装を区別しています。

359度と1度は隣り合う色

普通に引き算すると359度と1度の差は358度ですが、色相環では赤を挟んだ隣の色です。剰余演算で差を−180〜180度に折り返してから絶対値を取ると、距離は2度になります。

その距離をオン・オフの判定ではなく重みwに変換します。中心範囲では1、ぼかし領域では徐々に小さくなり、外側で0になります。色相・彩度・明度の変化量に同じ重みを掛けています。

色相環の距離は既知の考え方です。実装の課題は、その距離を各調整量に共通する重みに結び付けることにあります。AIと書いた接続コードが、独立した式を編集できる設定に変えます。

src/adjustments.ts · v0.16.0
L603–610
Photoshotの実装コード ↗
if (!v.colorize)
  for (const band of a.bands ?? []) {
    const d = Math.abs(((h * 360 - band.center + 540) % 360) - 180),
      w = clamp(1 - (d - band.width / 2) / band.feather);
    hue += band.hue * w;
    sat += band.saturation * w;
    light += band.lightness * w;
  }
Photoshot無料Photoshop代替の開発記 / 07IMPLEMENTATION

計算の
内側へ。

範囲とぼかしを数値で読む

写真例の青の範囲は中心240度、幅30度、ぼかし30度です。中心から15度以内は全量、30度離れると半分、45度以上では0になります。幅30度は左右それぞれ30度という意味ではありません。

スポイトは選んだ画素の色相を計算し、最も近い色グループの中心を変更します。ほぼ灰色の画素は色相を適切に決めにくいため受け付けません。クリックした位置を、後から編集できる設定値へ変換するのが役割です。

彩度と明度の計算にも版を持たせる

revision 2では、正の彩度値に使う倍率を変更しました。例えば+50は、この経路では1.5倍ではなく1÷(1−0.5)、つまり2倍です。最終的な彩度は許容範囲に制限します。

明度もHSLのLだけを直接変えるのではなく、RGB各成分を調整してからHSLへ再変換します。以前のプロジェクトは従来の計算を維持します。AIにスライダーを追加してもらう作業と、保存結果を守りながら計算を直す作業は別でした。

PHOTOSHOT / SOURCE NOTES07

コードを読む。

src/tone-tools.ts · v0.16.0
L45–48
Photoshotの実装コード ↗
const bands = structuredClone(a.bands ?? makeAdjustment('hsl').bands!);
const i = Math.round(hue / 60) % 6;
bands[i].center = hue;
return { ...a, bands };
src/adjustments.ts · v0.16.0
L619–626
Photoshotの実装コード ↗
const ns = v.colorize
    ? v.colorizeSaturation / 100
    : clamp(
        inputS *
          (a.revision === 2 && sat > 0
            ? 1 / Math.max(0.0001, 1 - sat / 100)
            : 1 + sat / 100),
      ),
src/adjustments.ts · v0.16.0
L614–618
Photoshotの実装コード ↗
if (a.revision === 2) {
  const lit = (x: number) =>
    light < 0 ? x * (1 + light / 100) : x + ((1 - x) * light) / 100;
  [inputH, inputS, inputL] = hsl(lit(r), lit(g), lit(b), scratch);
}
Photoshot無料Photoshop代替の開発記 / 07IN PRACTICE

エディターで見る。

実際のPhotoshot作業画面。設定と写真を一緒に確認できます。
実際のPhotoshot作業画面。設定と写真を一緒に確認できます。

色範囲の選択と物体選択は別

保存済みの例は、青の範囲に色相−25、彩度+15を適用し、全体の色相・彩度・明度を0にしています。AI生成の人物写真を使った動作例で、Photoshopとの数値一致試験ではありません。

分散した機能を組み合わせるとは、外部パッケージを複数導入することだけではありません。既存の変換関数やマスクに新しい選択コードをつなぐのも同じ仕事です。AIは、それらが共通の文書と設定で動く実装を助けました。

青い服だけでなく青い背景も変わり得ます。服だけを対象にするならレイヤーマスクを併用します。次回のカラーバランスでは、色相ではなくシャドウ・中間調・ハイライトを分けます。

Photoshot無料Photoshop代替の開発記 / 07BEFORE / AFTER

写真で見る結果。

AI生成の動作例用写真 / 編集前
01 / AI生成の動作例用写真 / 編集前
Photoshotの出力 / 編集結果
02 / Photoshotの出力 / 編集結果

AI生成の動作例用写真。編集画面と出力画像は実際のPhotoshotで作成しました。

参考資料とコード

Photoshot / tone-tools.ts ↗