Geometry & Mathematics
数式で導く完全なシンメトリー。
AI左右対称顔合成のロジック
ただ画像を半分に切って反転させるだけでは、不自然な合成になってしまいます。AIが検知した「顔の傾き」を座標変換で補正し、数学的に正しい中心線で反転させる実装の裏側を解説します。
1. 「顔の傾き」を数式で算出し、座標系を回転させる
写真の中の顔は、必ずしも垂直に写っているとは限りません。本ツールでは、AI(MediaPipe)が検知した「鼻筋の上部」と「顎の下」の2点を結ぶ線から、顔の傾き角度を Math.atan2 で算出しています。
Canvasの rotate メソッドを使用して、算出した角度分だけ一時的に座標系を回転させることで、写真が斜めであっても「常に鼻筋が垂直な状態」でクリッピングと反転を行うことができます。これが、どんな写真でも綺麗に中心で分かれる理由です。
// 顔の中心軸を定義するポイントから角度を算出
const tx = pTop.x * canvasW;
const ty = pTop.y * canvasH;
const bx = pBot.x * canvasW;
const by = pBot.y * canvasH;
// 顔の基本傾き角度(ラジアン)を求める
const baseAngle = Math.atan2(bx - tx, by - ty);
// Canvas座標系を顔の軸に合わせて調整
ctx.translate(midX, midY);
ctx.rotate(-baseAngle);
// ここに垂直な軸をベースにした反転描画処理が入ります 2. Canvasトランスフォームによる「非破壊的」な水平反転
画像の反転処理において、ピクセルデータを1つずつ配列で操作するのは処理負荷が大きく、高解像度画像ではカクつきの原因になります。本ツールでは、Canvasの変形マトリクス(ctx.scale(-1, 1))を活用し、GPUによる高速な描画処理を行っています。
特定の半分を clip() で保護した上で、座標系を水平方向にマイナス1倍することで、数学的に完全な鏡面の世界を瞬時に作り出しています。
ctx.save();
// 顔の軸を中心に座標系をセット
ctx.translate(midX, midY);
ctx.rotate(-angle);
// 反転させる側をクリッピング(ローカル座標系で指定)
ctx.beginPath();
if (symmetryMode === 'left') {
// 右半分を切り取る計算処理
ctx.rect(0, // 独自の縦幅値, // 独自の横幅値, // 独自の高さ);
}
ctx.clip();
// 水平反転を実行
ctx.scale(-1, 1);
// 元の角度と位置に戻して画像を描画
ctx.rotate(angle);
ctx.translate(-midX, -midY);
ctx.drawImage(originalImgObj, 0, 0, w, h);
ctx.restore(); Developer's Note
「左右対称の顔は美しく見える」という言葉がありますが、実際に自分の顔をシンメトリーにしてみると、想像以上に印象が変わることに驚くはずです。
このツールの開発で最もこだわったのは、AIによる自動検知のあとに、ユーザーが自分の手で「中心線のズレ」や「傾き」をスライダー調整できるようにした点です。AIは非常に優秀ですが、照明の当たり方や表情の癖によっては、わずかな違和感が生じることがあります。
その「わずかな違和感」をユーザー自身の感覚で解消できるよう、すべてのスライダー操作に対して requestAnimationFrame を用いた超高速なリアルタイムプレビューを実装しました。数学的な正しさと、個人の感覚。その両方が交差する場所で、自分でも知らなかった自分の顔に出会える体験を提供したかったのです。