
無料Photoshop代替の開発記
フォトフィルターの光と色
色変換の数学、フィルターの考え方、レイヤー合成のコードはすでにありました。必要だったのは、一つの文書と設定へつなぐことです。AIとフォトフィルターを修正した過程から、10回にわたる再利用と接続が代替エディターの機能として積み重なる様子をまとめます。
AI生成の動作例用写真。編集画面と出力画像は実際のPhotoshotで作成しました。
ソースから始める。
部品を持つことと製品をつくることの間
フォトフィルターには既知の色変換と既存の調整エンジンを利用できました。ただし濃度、レイヤーの不透明度、マスクの役割は分ける必要があります。AIと書いたコードが、それらを同じ文書内で正しい順番に実行するよう接続しました。
Adobeの文書はフィルター色・濃度・輝度維持オプションの意味を確認する資料です。ただし、この計算を特定の外部GitHubリポジトリから導入した記録は確認できませんでした。ここで示すのはポトショット自身の公開実装です。
設定の保存とマスク・不透明度の合成は既存処理を再利用し、フィルター色を画素変化へ変換する計算をAIと作成・修正しました。出所の説明はリポジトリのリンクを付けるだけでは足りません。
初期版は元の色と直接混ぜた
初期実装はamountを濃度÷100とし、元の色×(1−amount)+フィルター色×amountで混ぜました。読みやすい出発点で、維持オプションを有効にすると後から輝度補正を呼びます。
公開コードでは、この式をrevision 2以外のプロジェクト用に残しています。新しい計算を追加しても、古いファイルの処理を黙って置き換えないためです。
最初の計算をつなげられたのは、設定保存とレイヤー合成がすでにあったからです。その上で計算を修正し、以前の文書の経路を残せました。再利用できる構造は、AIと書いたコードを追加するときにも修正するときにも役立ちます。
L702–706Photoshotの実装コード ↗
if (t === 'photo' && a.revision !== 2) {
const amount = v.density / 100;
for (let c = 0; c < 3; c++) out[c] = out[c] * (1 - amount) + colors![0][c] * amount;
if (v.preserve) preserveLuma(out, luma(r, g, b));
}計算の
内側へ。
線形RGBで行列を適用する
revision 2ではRGBを線形値へ変換します。フィルター色と濃度からgainを作り、コード内のRGB↔XYZ行列と組み合わせてphotoMatrixを準備します。準備は画素ごとではなく、調整処理を構成する段階で行います。
各画素では行列の各行と線形RGBの積和を求め、範囲を制限してsRGBへ戻します。単にフィルター色を重ねる初期版から、チャンネルを変換する計算へ変わった部分です。
濃度とレイヤー不透明度は違う
濃度はフィルターの色変換自体を構成する値です。不透明度は計算結果を元の画像へどれだけ合成するかを決め、マスクや合成モードもこの段階で働きます。文書エンジンは調整値、layer.opacity、mask、blendを別々にadjustPixelsへ渡します。
濃度22%と不透明度50%を使った結果が、一般に濃度11%と同じとは言えません。線形化・行列・輝度維持・合成が間にあるため、別の操作として残しています。
コードを読む。
L581–586Photoshotの実装コード ↗
const photoMatrix =
t === 'photo'
? inv.map((row) =>
[0, 1, 2].map((c) => row.reduce((sum, x, i) => sum + x * gain[i] * xyz[i][c], 0)),
)
: [];L707–718Photoshotの実装コード ↗
if (t === 'photo' && a.revision === 2) {
scratch[0] = linear(r);
scratch[1] = linear(g);
scratch[2] = linear(b);
for (let c = 0; c < 3; c++)
out[c] = srgb(clamp(photoMatrix[c].reduce((sum, x, i) => sum + x * scratch[i], 0)));
if (v.preserve) {
scratch[0] = r;
scratch[1] = g;
scratch[2] = b;
blendRGB(scratch, out, 'color', out);
}L284–293Photoshotの実装コード ↗
adjustPixels(
pixels.data,
layer.adjustment,
layer.opacity,
mask,
layer.blend,
start,
Math.min(start + chunk, pixels.data.length),
compiled,
);エディターで見る。

実例とここまでの開発のつながり
写真例はCooling (80)、フィルター色#006dff、濃度22%、輝度維持オンです。AI生成原画像を実際の編集画面で処理して出力しました。改善後の維持経路はフィルター後にcolor合成を適用します。厳密な輝度保存やAdobeとの全面的な一致を保証するものではありません。
10回を通じた論点は、技術がなかったところへAIがすべてを発明したということではありません。多くの機能を実装するライブラリ、公開例、既知の計算はすでにあり、AIはそれらを読み、つなぎ、不足部分を書いて修正する作業を助けました。Photoshotはその組み合わせで複数のPhotoshop型の編集機能を実装した記録です。製品全体や全出力の一致、開発速度の定量比較を示すものではありません。
6〜10回は異なる計算を使いますが、原画像を残し、設定を保存し、マスクと合成を扱う構造は共通です。GitHubで読んだ例、既存コード、AIと新しく書いた計算をそこへ接続しました。次回はチャンネルミキサーで、RGB各成分が出力チャンネルへ寄与する割合を調整します。
写真で見る結果。


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