Algorithm & UI/UX
AI人物切り抜きと「自然な影」を両立する描画ハック
人物マスクで写真を切り抜き、Canvasの影の色・ぼかし・位置を指定して描きます。写真の光源を自動解析するのではなく、利用者が影の見え方を選ぶ2D合成です。
このツールの入力から出力まで
現行コードの確認日:
- 入力
- 人物写真を選び、影の方向・ずれ・濃さを調整します。
- 処理
- 人物マスクを影色で描き、位置をずらして背景と人物の間へ合成します。
- 出力
- 入力は長辺1500px以下、透明背景はPNG、白背景はJPEG品質0.95です。
まず確認する点:影は推定した光源から計算するものではありません。髪などのマスクの欠けが影にも現れるため、境界と方向を一緒に見ます。
仕上がりの例
人物を切り抜き、輪郭の後ろに影を付けた例です。
サンプルにはAIで生成した架空の人物画像を使用しています。
掲載画像はサイトにある既存の公開作例です。保存済みの資料ではAIで作成した架空の人物・手の画像を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。
保存済み検証記録の条件を見る
資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。
- 記録の入力画像
- 1788×2400px (JPEG)
- 記録の出力画像
- 1117×1500px (PNG)
| 画面の項目 | 記録の値 | 現行コードの初期値・範囲 |
|---|---|---|
| 記録で選択されていた項目 | 右下、透過(PNG) | — |
| 影の濃さ(不透明度) | 40 | 初期値 40 / 範囲 0〜100 |
| 影のぼかし(広がり) | 20 | 初期値 20 / 範囲 0〜80 |
| 影の距離(浮遊感) | 15 | 初期値 15 / 範囲 0〜60 |
記録では「この画像で保存へ進む」から完成表示へ進んでいます。初期値と記録値を比べてから、効果を1項目ずつ変えると作用を確認しやすくなります。
資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。
実装の確認箇所:src/pages/ai/ima-seg/drop-shadow/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。
仕組みを読み進めるときは、MediaPipeの画像セグメンテーション(人物マスクの寸法、カテゴリと資源解放)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。
1. 非破壊なクリッピング:destination-in によるマスク合成
AIが生成した「人物か背景か」の2値マスクを、元の画像にどう適用するかが最初の鍵です。ピクセルを一つずつループして透明度を書き換える手法は、高解像度画像ではJavaScriptの処理負荷が跳ね上がります。
本ツールでは、Canvasの globalCompositeOperation = 'destination-in' を活用しています。これにより、描画済みの画像に対して、マスクを使って元画像を残す領域を指定します。GPUの使用や描画速度は環境に依存します。
切り抜き済みのCanvasに影を付ける例です。dx・dyは影の移動量、opacityは0〜100、blurはぼかし量を渡します。
function drawShadow(ctx, cutout, dx, dy, opacity, blur) {
ctx.save();
ctx.shadowColor = 'rgba(0,0,0,' + opacity / 100 + ')';
ctx.shadowBlur = blur;
ctx.shadowOffsetX = dx;
ctx.shadowOffsetY = dy;
ctx.drawImage(cutout, 0, 0);
ctx.restore();
} 2. 2D Context APIによる「本物らしい」ドロップシャドウの動的生成
切り抜いた被写体に影をつける際、一般的なCSSの drop-shadow フィルタでは、書き出し時の解像度制御や、背景色(白背景など)との兼ね合いで自由度が制限されます。
このツールでは、Canvas 2D APIの shadowColor や shadowBlur 機能を、前述の「切り抜き画像」の描画時に直接適用しています。ユーザーが操作する「浮遊感(距離)」や「濃さ」を即座に計算し、オフセット値(dx, dy)に変換してレンダリングすることで、切り抜いた形に沿う影を描きます。実際の床や壁への投影形状を計算するものではありません。
3. モデルのカテゴリ定義による人物マスク
現行のMediaPipe Tasks Vision 0.10.34とselfie_segmenter.tfliteのカテゴリマスクは、人物が0、背景が255です。人物のカテゴリ0を残して背景のカテゴリ255を消し、その人物マスクから影を作ります。画像の左上が人物でも、背景カテゴリの基準は変わりません。髪や小物の誤分類は切り抜き結果と影に影響します。
出力の仕様と確認する点
マスクの大きさは返却されたwidth・heightを参照します。readMaskDataで処理Canvasの寸法へ合わせ、カテゴリは最近傍補間で所有配列へコピーします。コピー後にImageSegmenterResult.close()で元のマスク資源を解放し、編集と描画にはコピーを使います。
影の向きは右下・左下・真下から選びます。距離は0〜60px、ぼかしは0〜80、濃さは0〜100%です。距離を大きくしてもCanvasの外側に余白は追加しないため、端の影が切れる場合があります。
人物用のマスクを使うため、物やペットの輪郭が正しく残るとは限りません。髪や指先の欠けも影の形に反映されるので、まず切り抜き自体を確認します。
入力は長辺1500px以下、透明背景はPNG、白背景はJPEG品質0.95です。元写真の照明と矛盾しない方向へ影を調整すると、合成の意図が伝わりやすくなります。
