
無料Photoshop代替の開発記
基準と同じ値は白
しきい値は既知の明るさ計算と比較演算の接続です。AIと判定順序、ヒストグラム、アルファ保持を確認し、一つの値の差も編集結果の一部として記録しました。
AI生成の動作例用写真。編集画面と出力画像は実際のPhotoshotで作成しました。
ソースから始める。
既知の二値化を組み込む
明るさを基準と比較して黒または白にします。新しい二値化方式ではなく、既存調整レイヤーの計算、画面、設定保存をつなぐ仕事でした。
範囲は0〜255、初期値は128です。9回の白黒調整と違い、この判定自体は連続したグレーを残しません。
丸めてから以上で比較する
revision 2は8ビットRGBを0.3・0.59・0.11で重み付けし、整数に丸めます。基準以上なら白、未満なら黒です。
基準128でグレー127は黒、128は白。純粋な赤・緑・青の判定値は77・150・28です。線形光の物理的輝度ではなく、画面RGBの加重明るさです。丸めと比較の順序も再現に必要です。
L728–735Photoshotの実装コード ↗
if (t === 'threshold')
out.fill(
(a.revision === 2 ? Math.round(0.3 * R + 0.59 * G + 0.11 * B) : luma(r, g, b) * 255) >=
v.threshold
? 1
: 0,
);
if (t === 'gradient') {計算の
内側へ。
ヒストグラムとは重みが違う
共通ヒストグラムの係数は0.2126・0.7152・0.0722です。新しいしきい値判定とは異なるため、有彩色ではグラフ位置と判定値が完全には一致しない場合があります。
グラフの存在を式の一致の証拠にせず、両方の計算を分けて記しました。確認した実装の制限として残しています。
色の判定とアルファを分ける
共通処理は完全に透明な画素を飛ばし、残りの元のアルファを保持します。白に判定した場所を自動で透明にする機能ではありません。
判定は白黒でも、不透明度やマスクで原画像と混ぜると中間のグレーや色が見えます。判定段階と最終合成を分けて説明する必要があります。
コードを読む。
L728–735Photoshotの実装コード ↗
if (t === 'threshold')
out.fill(
(a.revision === 2 ? Math.round(0.3 * R + 0.59 * G + 0.11 * B) : luma(r, g, b) * 255) >=
v.threshold
? 1
: 0,
);
if (t === 'gradient') {L836–853Photoshotの実装コード ↗
beforeRGB: RGB = [0, 0, 0],
mixed: RGB = [0, 0, 0];
for (let i = start; i < end; i += 4) {
if (!data[i + 3]) continue;
let amount = opacity * (mask ? mask[i + 3] / 255 : 1);
if (blend === 'dissolve') amount = pixelNoise(i / 4) < amount ? 1 : 0;
if (!amount) continue;
compiled(data[i], data[i + 1], data[i + 2], out);
for (let c = 0; c < 3; c++) beforeRGB[c] = data[i + c] / 255;
if (a.values.dither && (a.type === 'lookup' || a.type === 'gradient'))
for (let c = 0; c < 3; c++) out[c] = clamp(out[c] + (pixelNoise(i / 4) - 0.5) / 255);
blendRGB(beforeRGB, out, blend, mixed);
for (let c = 0; c < 3; c++)
data[i + c] = Math.round(clamp(beforeRGB[c] + (mixed[c] - beforeRGB[c]) * amount) * 255);
}
}L253–265Photoshotの実装コード ↗
export function isIdentityAdjustment(a: Adjustment): boolean {
if (a.type === 'lookup') return !a.cube;
if (a.type === 'curves') return channels.every((c) => a.curves![c].every((p) => p.x === p.y));
if (a.type === 'photo') return a.values.density === 0;
if (['blackwhite', 'gradient', 'invert', 'posterize', 'threshold'].includes(a.type)) return false;
if (a.bands?.some((b) => b.hue || b.saturation || b.lightness)) return false;
return definitions[a.type].controls.every(
(c) =>
['preserve', 'relative', 'skin', 'legacy', 'colorizeHue', 'colorizeSaturation'].includes(
c.key,
) || (a.values[c.key] ?? c.initial) === c.initial,
);
}エディターで見る。

作例と確認した範囲
保存された写真とプロジェクトはThreshold 142、不透明度100%です。旧原稿の別の数値と混同せず、記録された設定に合わせました。AI生成人物写真を実際の編集機で処理した結果です。
本日16調整のアルファ、保存往復、合成一致を再確認しました。ヒストグラムの係数差やPhotoshop全体との同等性まで解消した意味ではありません。次回は二値でなく複数の色を配置するグラデーションマップです。
写真で見る結果。


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