猫の手ツール

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です。元写真の照明と矛盾しない方向へ影を調整すると、合成の意図が伝わりやすくなります。