AI生成ピクセルアートを本物に変換する6ステージのアルゴリズム解説


TL;DR

AI生成のピクセルアート風画像は、ドットの大きさや位置が微妙にずれており、ゲームエンジンやドット絵エディタでそのまま使えない。Pixel Snapperは「色の量子化→エッジ検出→グリッド推定→統合→Elastic Walker→多数決リサンプリング」の6ステージで、崩れたグリッドを正確に揃えた本物のピクセルアートに変換する。各ステージの設計判断には、k-means++の外れ値耐性や中央値によるロバスト推定など、地味だが効果的な工夫が詰まっている。

AI生成ピクセルアートの「惜しさ」

最近のAI画像生成は驚くほど高品質になったが、ピクセルアートに関しては独特の問題がある。生成された画像を拡大してみると、本来均一なグリッドに乗っているべきドットの大きさや位置が微妙にずれている。アンチエイリアスで境界に中間色が挿入されていたり、グラデーションで本来同じ色であるべき隣接ドットが微妙に異なる色になっていたりする。

見た目は「それっぽい」のだが、SpriteFusionやAsepriteに読み込ませると破綻する。ゲーム開発でスプライトシートとして使おうとすると、グリッドが揃っていないために切り出しが正しくできない。

Pixel Snapperは、この問題を6つのステージで解決する。各ステージが何をしているのか、なぜその設計判断がされたのかを順に見ていこう。

全体の処理フロー

まず全体像を把握しておこう。

flowchart LR
    A[入力画像] --> B[Stage 1\n色の量子化]
    B --> C[Stage 2\nエッジプロファイル]
    C --> D[Stage 3\nグリッド間隔推定]
    D --> E[Stage 4\n間隔の統合]
    E --> F[Stage 5\nElastic Walker]
    F --> G[Stage 6\nリサンプリング]
    G --> H[出力画像]

入力は「AI生成のピクセルアート風画像」、出力は「正確なグリッドに揃ったピクセルアート」だ。各ステージは前のステージの出力を入力として受け取り、パイプライン的に処理が進む。

Stage 1: 色の量子化——k-means++で色数を削減する

最初のステージは、画像の色数を減らすことだ。

AI生成画像では、本来同じ色であるべき隣接ピクセルがアンチエイリアスやグラデーションにより微妙に異なる色になっている。「赤いドット」が純粋な赤ではなく、暗い赤・明るい赤・オレンジがかった赤など数十種類に分散している。この状態のまま次のステージでエッジを検出しようとすると、本来存在しない境界を大量に拾ってしまう。

アルゴリズムはk-means++クラスタリングで全色をデフォルト16色に集約する。手順は以下の通りだ。

  1. 不透明ピクセル(α>0\alpha > 0)だけを対象にRGB値を収集する
  2. k-means++初期化で、色空間上でなるべく散らばった初期代表色を選ぶ
  3. 各ピクセルを最も近い代表色に割り当て、代表色を所属ピクセルの平均に更新する
  4. 収束するか最大15回の反復まで繰り返す
  5. 全ピクセルを最も近い代表色に置き換える

k-means++初期化がポイントだ。通常のk-meansではセントロイドの初期配置がランダムなため結果が不安定になるが、k-means++は「すでに選んだセントロイドから遠い点ほど次のセントロイドに選ばれやすい」という重み付き抽選を行う。これにより色空間上でまんべんなく散らばった初期配置が得られ、結果が安定する。

収束判定は代表色の移動量が0.010.01未満になった時点だ。RGB各成分が0〜255の範囲であることを考えると、0.010.01はほぼ動かなくなった状態と言ってよい。

Stage 2: エッジプロファイル——色の変化を1次元に集約する

色を整理したら、次は「どこにグリッドの境界がありそうか」を見つける。

ピクセルアートのグリッド線上では必ず色が切り替わる。つまり、色の変化が集中している列・行を見つければ、それがグリッド境界の候補になる。

具体的には、画像の各ピクセルをグレースケールに変換した上で、隣接ピクセル間の輝度差(勾配)を計算する。グレースケール変換にはITU-R BT.601規格の重み係数を使う。

L=0.299R+0.587G+0.114BL = 0.299R + 0.587G + 0.114B

人間の目は緑に最も敏感で青には鈍いため、単純な(R+G+B)/3(R+G+B)/3よりもこの重み付きの方が知覚的に自然な明るさになる。

勾配の計算には[1,0,1][-1, 0, 1]カーネルを適用する。あるピクセルの右隣から左隣を引くと水平方向の変化量が得られる。これは最もシンプルなエッジ検出フィルタで、SobelやPrewittの簡略版と言える。

  • 列プロファイル:各ピクセルの水平勾配の絶対値を、その列について全行分合計する
  • 行プロファイル:各ピクセルの垂直勾配の絶対値を、その行について全列分合計する

結果として2本の1次元配列が得られる。値が大きい位置ほど、そこにグリッド境界がある可能性が高い。2次元の画像情報を1次元のプロファイル(射影)に畳み込むことで、後続のピーク検出が格段にシンプルになる。

Stage 3: グリッド間隔の推定——ピーク間隔の中央値を取る

プロファイルが得られたら、そこからグリッド1マスの幅を推定する。

  1. プロファイルの最大値の20%を閾値とし、それを超える局所最大値(前後の値より大きい点)をピークとして抽出する
  2. 4px未満の間隔のピークを除去する(ノイズ対策)
  3. 隣接ピーク間の間隔をすべて計算する
  4. それらの間隔の中央値をグリッド幅として返す

ここで平均値ではなく中央値を使うのは、外れ値への耐性のためだ。画像端やスプライト境界では異常に大きい間隔が出ることがあり、平均値だとこれに引っ張られる。たとえば間隔が[8,8,9,8,30][8, 8, 9, 8, 30]のとき、平均値は12.612.6だが中央値は88。実際のグリッド幅は8に近いはずなので、中央値の方が正確だ。

外れ値(●)をドラッグすると、平均は大きく動くが中央値は安定する

この処理を列プロファイルと行プロファイルそれぞれに行い、X方向・Y方向のグリッド幅を独立に推定する。

Stage 4: グリッド間隔の統合——X軸とY軸を揃える

ピクセルアートのグリッドは通常正方形だが、Stage 3ではX方向とY方向で異なる値が出ることがある。このステージではそれを1つの整合的な値にまとめる。

4つのケースに分けた条件分岐で処理する。

ケース処理
X・Y両方推定でき、比率が1.8倍以内両者の平均値を両軸に使う
X・Y両方推定でき、比率が1.8倍超小さい方を両軸に使う(大きい方は誤推定)
片方だけ推定できたその値を両軸に使う
両方推定できなかったフォールバック値(短辺 / 64)を使う

1.8倍という閾値が興味深い。比率が1.8倍を超えた場合、「片方の軸でエッジ検出がうまくいかなかった」と解釈し、小さい方を正しい推定として採用する。大きい方は複数のグリッドセルをまたいでピークを拾ってしまった可能性が高いからだ。

フォールバックの「短辺 / 64」は、一般的なピクセルアートのグリッドサイズ(8〜64px)に対して妥当な初期推定になるように設計されている。

Stage 5: Elastic Walker——エッジに吸着するグリッド線

ここがこのアルゴリズムの中核だ。

Stage 4で得たグリッド幅はあくまで「平均的な間隔」であり、AI生成画像ではドットの位置が揺らいでいるため、機械的に等間隔で切るとドットの途中で切ってしまう。Elastic Walker(弾力的な歩行者)は、等間隔を基準にしつつ実際のエッジに合わせて微調整する貪欲法だ。

  1. 位置0からスタートし、グリッド幅ぶん先を「目標位置」とする
  2. 目標位置を中心にグリッド幅の35%(最低2px)を「探索窓」とする
  3. 探索窓内でプロファイル値が最も高い位置を見つける
  4. その値がプロファイル全体の平均の50%を超えていれば、そこにカットを置く(エッジにスナップ)
  5. 超えていなければ、目標位置そのままにカットを置く(等間隔にフォールバック)
  6. 画像端に達するまで繰り返す

探索窓の幅35%は絶妙なバランスだ。広すぎるとグリッド1マス分ずれるリスクがあり、狭すぎるとエッジを捉えられない。

さらに2パスの安定化処理が後続する。

  • パス1(軸ごとの安定化):カット数が極端に少ない軸があれば、もう片方の軸の情報で補完する
  • パス2(軸間の相互検証):XとYのセル幅を比較し、1.8倍以上の差があれば小さい方に合わせて再分割する

この相互検証により、片方の軸でエッジ検出が失敗しても、もう片方の結果で補正できる。

Stage 6: リサンプリング——多数決で1ドットに集約する

最後のステージは、Stage 5で決まったグリッドに基づいて各セルを1ピクセルに集約する。

一般的な画像縮小では双線形補間(周囲のピクセルを加重平均する手法)が使われるが、ピクセルアートでは中間色が生まれると困る。そこで多数決投票を採用する。

  1. 出力画像のサイズを「(列カット数 - 1) × (行カット数 - 1)」に設定する
  2. 各セル内の全ピクセルの色を集計する
  3. 最も出現回数が多い色を代表色にする
  4. 同票の場合はRGBA値の辞書順で決定論的に選ぶ

多数決の利点は、元の色がそのまま残ることだ。セル内に赤60個・青30個・緑10個があれば赤になる。平均を取ると中間色が生まれてエッジがぼやけるが、多数決ならシャープさが保たれる。

同票時の辞書順比較も地味ながら重要だ。ランダムに選ぶと実行のたびに結果が変わるが、辞書順なら同じ入力に対して常に同じ出力が返る(決定論的)。再現性はデバッグでもテストでも基本だ。

設計判断のまとめ

各ステージに共通するのは「ロバスト(頑健)な手法を選んでいる」という点だ。

  • k-means++:ランダム初期化より安定した結果
  • 中央値:外れ値に強いグリッド幅推定
  • Elastic Walker:等間隔を基準にしつつエッジに柔軟に追従
  • 相互検証:片方の軸が失敗してももう片方で補正
  • 多数決:平均化によるぼやけを回避

AI生成画像は「だいたい合っているが微妙にずれている」という性質を持つ。完璧な入力を前提にした手法ではなく、ノイズや誤差に対して頑健な手法を各所で選択しているのが、このアルゴリズムの設計思想だと思う。

6つのステージはそれぞれ独立したシンプルな処理だが、パイプラインとして組み合わせることで、AI生成画像という不安定な入力から安定したピクセルアートを出力している。画像処理の基本的な技法——k-meansクラスタリング、エッジ検出、ピーク検出、貪欲法——を適切に組み合わせた、実用的でエレガントなアプローチだ。

以上。