Algorithm & UI/UX
Canvasで実現する輝度差を使った
線画抽出アルゴリズムの裏側
写真から塗り絵用の「線画」を作るプロセス。そこには、Canvasのfilterに依存せず輪郭を広げる工夫や、画像の画素を処理するためのメモリ効率化の知恵が詰まっています。
このツールの入力から出力まで
現行コードの確認日:
- 入力
- 写真を選び、線のしきい値やぼかし・線色を指定します。
- 処理
- 縮小した画像の画素から線を強調する処理を行い、明るい地に輪郭を描きます。
- 出力
- 出力は長辺最大 1500px の JPEG(品質指定 0.9)です。
まず確認する点:しきい値で小さな毛や模様が消えたり残ったりします。線画のプレビューと保存画像を区別して確認します。
仕上がりの例
猫の写真を線画にした、完成プレビューの表示例です。
掲載画像はサイトにある既存の公開作例です。保存済みの資料では運営者の愛猫の写真を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。
保存済み検証記録の条件を見る
資料の種類は「ツールのプレビュー・結果画面を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。
- 記録の入力画像
- 800×667px (JPEG)
- 記録の画面キャプチャ
- 525×437px (PNG)
| 画面の項目 | 記録の値 | 現行コードの初期値・範囲 |
|---|---|---|
| 記録のしきい値 | 50 | — |
| 記録のぼかし | 1 | — |
資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。
実装の確認箇所:src/pages/image/sketch/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。
仕組みを読み進めるときは、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)、メモリ管理と画像処理(画素バッファ、縮小と容量予算)が参考になります。
1. 8方向の重ね描きによる輪郭の広がり
Canvasの filter は利用できるブラウザに差があります。本ツールでは、線の太さを調整するために「画像を8方向に微細にずらして重ね描きする」という重ね描きの手法を採用しました。
これにより、ブラウザのネイティブフィルタに依存せず、線の抽出前に輪郭を広げます。8方向の重ね合わせは均等なボックスフィルタそのものではなく、環境ごとの完全一致を保証しません。
現在画素と右・下・右下の3画素の輝度差を合計し、線にするか決める例です。
function sketchPixel(center, right, below, diagonal, threshold, lineColor = 0) {
const edge = Math.abs(center - right) + Math.abs(center - below)
+ Math.abs(center - diagonal);
return edge > (101 - threshold) * 0.4 ? lineColor : 255;
} 2. TypedArrayによるLuma(輝度)計算の高速化
エッジ検出を行う際、ピクセルごとにRGB値を参照して計算を行うとオーバーヘッドが大きくなります。本ツールでは、処理の初手で全ピクセルの輝度を Float32Array にキャッシュする「先行計算パス」を設けています。
計算結果を型付き配列に保持することで、メインの輪郭抽出ループ内での計算コストを最小限に抑え、RGB からの輝度計算を各比較で繰り返さずに済みます。処理時間の実測値を示すものではありません。
3. iPhoneユーザーのためのHEIC動的変換
このツールでは、HEICを選んだ場合はブラウザ内で別の画像形式に変換してから編集します。本ツールでは heic2any ライブラリを導入していますが、初期ロードを重くしないよう、HEICファイルが選択された時のみ動的にモジュールをインポートする設計にしています。
出力の仕様と確認する点
感度を上げるほど判定に必要な輝度差が小さくなり、細部やノイズも線として拾います。線の太さは 0〜3px のずらし量で、厳密に指定幅の線を描く設定ではありません。
線の濃さは 0(黒)〜200(薄い灰色)です。背景は不透明な白で、透明 PNG や編集可能なベクター線は出力しません。出力は長辺最大 1500px の JPEG(品質指定 0.9)です。
物体の意味を理解して輪郭を選ぶ AI ではありません。毛、草、細かい模様、撮影ノイズも線になり、明暗差の少ない境界は消える場合があります。まず感度を下げて主要な輪郭から確認すると調整しやすくなります。
