Algorithm & Canvas Tech
ブラウザで実現する「光る太いフチ」の
高度な描画アルゴリズム
本ツールは、AI(MediaPipe)による人物セグメンテーションと、Canvas APIを用いた独自の合成処理を組み合わせています。人物のマスクをずらして重ね、Canvasの影を加えて光彩を作る処理を説明します。
このツールの入力から出力まで
現行コードの確認日:
- 入力
- 人物写真とネオン色・太さなどを指定します。
- 処理
- 人物マスクの境界から輪郭を作り、ぼかした光と輪郭の線を写真へ重ねます。
- 出力
- 長辺1500px以下へ縮小し、透明背景はPNG、黒背景はJPEG品質0.95で保存します。
まず確認する点:光の見た目の前に人物マスクの境界を確認します。細い髪や持ち物の欠けが、光る輪郭として目立つ場合があります。
仕上がりの例
人物を切り抜き、輪郭に水色のネオンを加えた例です。
サンプルにはAIで生成した架空の人物画像を使用しています。市松模様は透明部分を示す表示用の背景です。ツールで保存する透過PNGには含まれません。
掲載画像はサイトにある既存の公開作例です。保存済みの資料ではAIで作成した架空の人物・手の画像を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。
保存済み検証記録の条件を見る
資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。
- 記録の入力画像
- 1788×2400px (JPEG)
- 記録の出力画像
- 1117×1500px (PNG)
| 画面の項目 | 記録の値 | 現行コードの初期値・範囲 |
|---|---|---|
| 記録で選択されていた項目 | 透過(PNG) | — |
| 色 | #00ffff | 初期値 #00ffff |
| フチの太さ(芯) | 10 | 初期値 10 / 範囲 0〜50 |
| 光の広がり(グロウ) | 30 | 初期値 30 / 範囲 0〜100 |
| 光の強さ(重ね掛け) | 2 | 初期値 2 / 範囲 1〜5 |
記録では「この画像で保存へ進む」から完成表示へ進んでいます。初期値と記録値を比べてから、効果を1項目ずつ変えると作用を確認しやすくなります。
資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。
実装の確認箇所:src/pages/ai/ima-seg/neon-outline/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。
仕組みを読み進めるときは、MediaPipeの画像セグメンテーション(人物マスクの寸法、カテゴリと資源解放)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。
1. シルエット・オフセット法による「太いフチ」の生成
Canvas APIには標準的な stroke() メソッドがありますが、複雑な形状の切り抜き画像に対して「外側に太い均一なフチ」を付けるには不十分です。本ツールでは、AIが生成したマスク画像をネオンカラーで塗りつぶし、それを全方位にわずかにずらして重ね描きする「シルエット・オフセット法」を採用しています。
この手法のメリットは、シルエットを8方向へずらして輪郭の周りを塗れる点にあります。有限方向の近似なので、細い隙間が埋まったり、強い設定で角張って見えたりすることがあります。
色付きのシルエットを45度ずつ、8方向へずらす例です。widthはフチの太さで、シルエットの中心にある人物は最後に元写真で上書きします。
function neonBorder(ctx, silhouette, width) {
for (let degrees = 0; degrees < 360; degrees += 45) {
const angle = degrees * Math.PI / 180;
ctx.drawImage(silhouette, Math.cos(angle) * width, Math.sin(angle) * width);
}
} 2. 多重スタック・シャドウによる「爆光ネオン」エフェクト
単一の shadowBlur だけでは、発光感が弱く、デジタルなネオン管のような質感を出すのが困難です。そこで、同一のシルエットに対して「影の描画処理を何度も重ね掛けする」というスタック描画アルゴリズムを実装しています。
これにより、中心部は非常に明るく、外側に向かってふんわりと光が減衰していく「グロウ効果」をブラウザ上でシミュレートしています。globalCompositeOperation を活用し、人物レイヤーと光彩レイヤーの重なり順を制御することで、光が人物の背後から溢れ出しているような視覚効果を生んでいます。
出力の仕様と確認する点
マスクの大きさは返却されたwidth・heightを参照します。readMaskDataで処理Canvasの寸法へ合わせ、カテゴリは最近傍補間で所有配列へコピーします。コピー後にImageSegmenterResult.close()で元のマスク資源を解放し、編集と描画にはコピーを使います。
太さは周囲へずらす距離、光の広がりはshadowBlur、光の強さはシルエットを重ねる回数(1〜5回)を変えます。広がり0では発光の重ね描きを省き、フチと人物を描きます。
現行のMediaPipe Tasks Vision 0.10.34とselfie_segmenter.tfliteのカテゴリマスクは、人物が0、背景が255です。人物のカテゴリ0からフチを作ります。髪や小物の切り抜き誤りもフチとして強調されるため、まず人物の形を確認します。画像端の外へCanvasを拡張しないので、太いフチや光が途中で切れる場合があります。
長辺1500px以下へ縮小し、透明背景はPNG、黒背景はJPEG品質0.95で保存します。CanvasのclearRectは画素を消す操作であり、Canvas自体のメモリ確保を解放する操作ではありません。
