猫の手ツール

Algorithm & UI/UX

全ピクセル走査をブラウザで回す
魚眼マッピングの最適化戦略

AIによる顔検知と、ピクセル単位の高度な座標変換をいかに両立させるか。Canvas 2Dでの高速レンダリングと正規化座標による汎用性の高い設計に注目します。

このツールの入力から出力まで

現行コードの確認日:

入力
人物写真を選び、魚眼風の強さを調整します。
処理
FaceDetectorの顔枠から変形中心を決め、中心からの距離に応じて参照する画素位置を変えます。
出力
長辺2000px以下の処理画像からJPEG品質0.95を出力します。

まず確認する点:顔の検出位置が変形の中心になります。見落としや横顔で中心がずれた場合、意図した場所が膨らむかを確認します。

仕上がりの例

顔を中心に、魚眼レンズ風の変形を加えた例です。

加工前・加工後
左は人物の顔と肩が写る写真。右は顔の中心を大きく膨らませた写真。

サンプルにはAIで生成した架空の人物画像を使用しています。

加工前・加工後

左は人物の顔と肩が写る写真。右は顔の中心を大きく膨らませた写真。

掲載画像はサイトにある既存の公開作例です。保存済みの資料ではAIで作成した架空の人物・手の画像を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。

保存済み検証記録の条件を見る

資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。

記録の入力画像
1788×2400px (JPEG)
記録の出力画像
1490×2000px (JPEG)
保存済み記録の主な操作・出力条件
画面の項目記録の値現行コードの初期値・範囲
膨張の強さ50初期値 50 / 範囲 10〜100
影響範囲(半径)20初期値 20 / 範囲 10〜50

記録では「この画像で保存へ進む」から完成表示へ進んでいます。初期値と記録値を比べてから、効果を1項目ずつ変えると作用を確認しやすくなります。

資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。

実装の確認箇所:src/pages/ai/face-det/fisheye-face/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。

仕組みを読み進めるときは、MediaPipeの顔検出(顔枠の入力・出力と検出漏れの扱い)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。

1. 魚眼歪曲のピクセル逆引きマッピング

本ツールでは、画像を直接変形させるのではなく、描画先の各ピクセルに対して「元画像のどの位置から色を拾ってくるか」を計算する、逆引きマッピングを採用しています。

この手法のメリットは、描画先から参照先を求めるため、画素を移動してできる穴を避けやすい点です。現行実装は座標を切り下げて色を取得するので、強い変形では画素の段差が見える場合があります。特定の半径(R)内において、中心からの距離(dist)に応じた冪乗関数(Math.pow)を用いることで、中心部ほど大きく膨らむ魚眼特有の視覚効果を生み出しています。

円内の1点が参照する半径を求める部分です。distanceは中心からの距離、radiusは効果半径、strengthは10〜100の設定値を渡します。

function sourceRadius(distance, radius, strength) {
  if (radius <= 0 || distance >= radius) return distance;
  const exponent = 1 + strength / 100;
  return radius * Math.pow(distance / radius, exponent);
}

2. requestAnimationFrameによる描画の非同期制御

全ピクセルの走査は、数百万画素の画像では非常に重い処理となります。スライダーを動かすたびにこの計算を走らせると、ブラウザのメインスレッドを占有し、入力への応答が遅れる場合があります。

そこで、requestAnimationFrame(RAF)を利用した描画のデバウンス処理を実装しています。次の描画までに複数の変更が届いた場合、まだ実行されていない予約を取り消して最新の設定で描きます。実行中のピクセル処理を中断したり、別スレッドへ移す仕組みではありません。

3. 正規化座標(0.0-1.0)による抽象化管理

検知された顔の座標を「ピクセル絶対値」ではなく、「画像全体に対する割合」で保持しています。

割合で保持した顔の範囲に描画先の幅・高さを掛けることで、処理画像と表示サイズの違いを吸収します。元写真を別の縦横比へ変形しても顔の形を補正できる、という意味ではありません。

出力の仕様と確認する点

効果半径は顔枠の大きさと「範囲」設定から求めます。強さは中心近くをどれだけ膨らませるか、範囲は周囲のどこまで変形するかを変えます。顔だけを厳密に分離する処理ではないため、髪や背景も動きます。

複数の検出位置が重なる場合、後の位置の処理が同じ画素を上書きします。意図しない部分が歪む場合は対象をタップして減らし、弱い設定から調整してください。

長辺2000px以下の処理画像からJPEG品質0.95を出力します。検出位置は手動で追加・削除できますが、元の写真を立体的な魚眼レンズで撮り直す処理ではなく、平面上の画素の座標変換です。