Algorithm & UI/UX
WASMによる高速顔認識と
正規化座標によるCanvas操作の最適化
集合写真から個々の顔を切り出す作業を、いかに「安全」かつ「直感的」に実現するか。MediaPipeとCanvas APIを駆使した、クライアントサイド完結型ツールの実装の裏側を解説します。
1. MediaPipeによるエッジAI推論
本ツール最大のこだわりは、すべての画像処理をユーザーのブラウザ内(ローカル環境)で完結させている点です。 これを実現するために、Googleが開発したMediaPipe Face DetectionをWebAssembly(WASM)形式で組み込んでいます。
サーバーへ画像を送信しないため、プライバシーが完全に保護されるだけでなく、サーバーコストの削減とレスポンスの高速化を同時に達成しています。
// AIエンジンの初期化(機密パスは秘匿)
async function initAI() {
const wasmUrl = "/* 内部パス */";
const vision = await FilesetResolver.forVisionTasks(wasmUrl);
faceDetector = await FaceDetector.createFromOptions(vision, {
baseOptions: {
modelAssetPath: "/* モデルバイナリへのパス */",
delegate: "GPU" // 利用可能な場合はハードウェア加速
},
runningMode: "IMAGE",
minDetectionConfidence: 0.4 // 検出感度のしきい値
});
}
2. 正規化座標によるマルチデバイス対応
Canvas上に描画される顔の枠(バウンディングボックス)は、ピクセル絶対値ではなく、0.0〜1.0の範囲に正規化された座標で管理しています。
この工夫により、高解像度な写真でもプレビュー用の小さな画面でも、同一のデータ構造で座標を扱えます。画面リサイズやスマートフォンの縦横回転が発生しても、座標を再計算することなく即座に正しい位置へ枠を描画できるメリットがあります。
// 座標データの保持形式(正規化)
let currentBoxes = [
{
xCenter: 0.5, // 中央位置 (0.0 - 1.0)
yCenter: 0.5,
width: 0.1, // 画像幅に対する割合
height: 0.1
}
];
// レンダリング時の計算
function renderCanvas() {
const w = mainCanvas.width;
const h = mainCanvas.height;
currentBoxes.forEach(box => {
// ここで実際の描画ピクセルを算出
const drawX = box.xCenter * w;
const drawY = box.yCenter * h;
// ... 描画処理
});
}
3. iOS環境に特化したUXハック
ブラウザツールにおいて、iPhone(iOS)の画像保存は大きな壁となります。 通常の `<a download>` タグが一部のブラウザやSNS内蔵ビューワーで動作しない問題に対処するため、Web Share APIを条件分岐で組み込んでいます。
PCやAndroidでは直接ダウンロードを実行し、iOSではOS標準の共有メニュー(「画像を保存」を含む)を呼び出すことで、ユーザーの離脱を防いでいます。
// デバイスに応じた保存処理のブラックボックス化
async function saveImage(dataUrl, fileName) {
if (isIOSDevice() && navigator.canShare) {
// iOS: Share API を使用
const file = // ここにデータURLをFileオブジェクトへ変換する処理が入ります
await navigator.share({ files: [file] });
} else {
// PC/Android: aタグによる直接ダウンロード
// 独自のチューニングを加えた連続DL処理
}
}
Developer's Note
「技術のための技術」ではなく、事務作業をいかに楽にするかを重視しました。 AIの認識は完璧ではありません。だからこそ、認識漏れを「ドラッグ操作で直感的に補完できる」というハイブリッドな操作感に最もリソースを割きました。 特にCanvas上のタップ(削除)とドラッグ(枠の追加)のしきい値判定には、実際の操作感を元にした独自のチューニング値を採用しています。