Algorithm & UI/UX
AI人物切り抜きと「自然な影」を両立する描画ハック
背景透過ツールは数多くありますが、切り抜いた人物をそのまま配置すると「浮いて」見えてしまいがちです。本ツールでは、MediaPipeによる高精度なマスク抽出と、Canvasの合成モードを組み合わせ、どんな背景にも馴染む「実在感のある影」をブラウザ完結で実現しています。
1. 非破壊なクリッピング:destination-in によるマスク合成
AIが生成した「人物か背景か」の2値マスクを、元の画像にどう適用するかが最初の鍵です。ピクセルを一つずつループして透明度を書き換える手法は、高解像度画像ではJavaScriptの処理負荷が跳ね上がります。
本ツールでは、Canvasの globalCompositeOperation = 'destination-in' を活用しています。これにより、描画済みの画像に対して「マスクが存在する部分だけを残す」という処理をブラウザの描画エンジン(GPU)側で一括実行でき、1500pxクラスの画像でもストレスのないレスポンスを維持しています。
// マスクによる切り抜き処理の最適化
function createCutout(ctx, originalImg, maskCanvas, w, h) {
ctx.drawImage(originalImg, 0, 0, w, h);
// 描画済みの画像に対して、マスクが重なっている部分だけを抽出
ctx.globalCompositeOperation = 'destination-in';
ctx.drawImage(maskCanvas, 0, 0, w, h);
// 合成モードを元に戻す
ctx.globalCompositeOperation = 'source-over';
}
2. 2D Context APIによる「本物らしい」ドロップシャドウの動的生成
切り抜いた被写体に影をつける際、一般的なCSSの drop-shadow フィルタでは、書き出し時の解像度制御や、背景色(白背景など)との兼ね合いで自由度が制限されます。
このツールでは、Canvas 2D APIの shadowColor や shadowBlur 機能を、前述の「切り抜き画像」の描画時に直接適用しています。ユーザーが操作する「浮遊感(距離)」や「濃さ」を即座に計算し、オフセット値(dx, dy)に変換してレンダリングすることで、プロのデザイナーが手動で調整したような立体感を生み出しています。
// 影のパラメータ計算と適用
function renderWithShadow(ctx, cutoutCanvas, state) {
// 向きに基づいた座標変換
let dx = // 角度と距離から計算されたオフセットX;
let dy = // 角度と距離から計算されたオフセットY;
// Canvasの影設定(ブラウザのネイティブ機能を活用)
ctx.shadowColor = `rgba(0, 0, 0, ${state.shadowOpacity / 100})`;
ctx.shadowBlur = state.shadowBlur;
ctx.shadowOffsetX = dx;
ctx.shadowOffsetY = dy;
// 影の設定を持った状態で、切り抜かれた人物レイヤーを一度に描画
ctx.drawImage(cutoutCanvas, 0, 0);
}
3. WebAssemblyによる「境界の自動判定」ロジック
AIが人物を切り抜く際、モデルの出力は単なる「カテゴリIDの配列」です。本ツールでは、処理開始時に「左上のピクセルを背景の基準とする」という独自の推論ロジックを組み込んでいます。これにより、AIが「背景」とみなしたIDを動的に特定し、人物がどこにいても正確にマスクを反転・生成できる柔軟な処理フローを構築しました。
Developer's Note
開発において最もこだわったのは、スマホでの「サクサク感」です。MediaPipeのWasmモジュールは数MBのサイズがあるため、あえて「画像がアップロードされた瞬間」にダウンロードを開始する Lazy Loading 戦略をとっています。
また、影をつけた後の画像をiPhoneで保存する際、Safariの仕様で「ボタンが効かない」といった不具合が起きやすいため、最終プレビューを `` タグに変換して「長押し保存」を促すモーダルを用意するなど、技術的な実装と同じくらい、ユーザーの「最終的な出力」までの導線を大切に設計しました。