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

基準と同じ値は白

しきい値は既知の明るさ計算と比較演算の接続です。AIと判定順序、ヒストグラム、アルファ保持を確認し、一つの値の差も編集結果の一部として記録しました。

PHOTOSHOT DEVELOPMENT JOURNAL / 15

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

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

ソースから始める。

既知の二値化を組み込む

明るさを基準と比較して黒または白にします。新しい二値化方式ではなく、既存調整レイヤーの計算、画面、設定保存をつなぐ仕事でした。

範囲は0〜255、初期値は128です。9回の白黒調整と違い、この判定自体は連続したグレーを残しません。

丸めてから以上で比較する

revision 2は8ビットRGBを0.3・0.59・0.11で重み付けし、整数に丸めます。基準以上なら白、未満なら黒です。

基準128でグレー127は黒、128は白。純粋な赤・緑・青の判定値は77・150・28です。線形光の物理的輝度ではなく、画面RGBの加重明るさです。丸めと比較の順序も再現に必要です。

src/adjustments.ts · v0.16.0
L728–735
Photoshotの実装コード ↗
    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') {
Photoshot無料Photoshop代替の開発記 / 15IMPLEMENTATION

計算の
内側へ。

ヒストグラムとは重みが違う

共通ヒストグラムの係数は0.2126・0.7152・0.0722です。新しいしきい値判定とは異なるため、有彩色ではグラフ位置と判定値が完全には一致しない場合があります。

グラフの存在を式の一致の証拠にせず、両方の計算を分けて記しました。確認した実装の制限として残しています。

色の判定とアルファを分ける

共通処理は完全に透明な画素を飛ばし、残りの元のアルファを保持します。白に判定した場所を自動で透明にする機能ではありません。

判定は白黒でも、不透明度やマスクで原画像と混ぜると中間のグレーや色が見えます。判定段階と最終合成を分けて説明する必要があります。

PHOTOSHOT / SOURCE NOTES15

コードを読む。

src/adjustments.ts · v0.16.0
L728–735
Photoshotの実装コード ↗
    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') {
src/adjustments.ts · v0.16.0
L836–853
Photoshotの実装コード ↗
    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);
  }
}
src/adjustments.ts · v0.16.0
L253–265
Photoshotの実装コード ↗
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,
  );
}
Photoshot無料Photoshop代替の開発記 / 15IN PRACTICE

エディターで見る。

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

作例と確認した範囲

保存された写真とプロジェクトはThreshold 142、不透明度100%です。旧原稿の別の数値と混同せず、記録された設定に合わせました。AI生成人物写真を実際の編集機で処理した結果です。

本日16調整のアルファ、保存往復、合成一致を再確認しました。ヒストグラムの係数差やPhotoshop全体との同等性まで解消した意味ではありません。次回は二値でなく複数の色を配置するグラデーションマップです。

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

写真で見る結果。

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

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