▍AI Dev Lab
MangaSnap

MangaSnap ができるまで — 写真をハーフトーンで漫画化する

写真を漫画風にするツールの開発ログ。 ぼかし・エッジ抽出・二値化・線の太らせ・トーンの階調化・ ハーフトーンのドット・速度線 の 7 段階を自前で組み、 少年 / 少女 / 劇画 / アメコミ に振り分けた記録です。

MangaSnap は、写真を漫画風の絵に変えるブラウザツールです。ぼかし・輪郭抽出・二値化・線の太らせ・トーンの階調化・ハーフトーンのドット・速度線という 7 段の処理を、機械学習モデルを使わず素の JavaScript で組んでいます。

#🎯 「漫画っぽく」を分解する

写真を漫画に見せる要素を書き出すと、実は 3 つしかありませんでした。

  1. 輪郭が黒い線になっている
  2. 中間色が消えて、白・黒・網点の 3 種類くらいに減っている
  3. 効果線(速度線・集中線)が入っている

逆に言うと、この 3 つを作れば漫画に見えます。「画風を学習させる」必要はなく、古典的な画像処理の組み合わせで届くという見立てから始めました。

#🌫️ Bilateral フィルタ ── 輪郭を残したままぼかす

最初の工程がぼかしです。ただし普通のぼかし(ガウシアン)を使うと、輪郭まで一緒にぼやけて次の線抽出が弱くなります。

そこで Bilateral フィルタを使いました。「距離が近い」だけでなく「明るさが近い」画素だけを混ぜるぼかしです。

ts
const intensityDiff = center - n
const wt = Math.exp(-(intensityDiff * intensityDiff) / (2 * sigmaSq))
sum += n * wt
wsum += wt

重みが明るさの差の二乗で決まるのがポイントです。肌の中のような近い明るさ同士はよく混ざり、輪郭をまたぐ大きな差はほとんど混ざらない。結果として、平らな面はのっぺりするのに、輪郭は残るという漫画向きの状態が作れます。

計算量は素直に半径の二乗で効いてきます。ここは高速化せず、代わりに処理前に画像を縮小して対応しました。

#✏️ Sobel から線を作る

輪郭は Sobel 演算子で取ります。横方向と縦方向の差分カーネルをかけて、強度を出すだけです。

そのあとが漫画にするための工夫で、二値化してから太らせて(dilation) います。

ts
export function thresholdAndDilate(...)

Sobel の生の出力はグレーの薄い線で、そのままだとペン画に見えません。閾値で白黒に落として、周囲 1 画素に広げる。線に太さを持たせるだけで一気にペン入れの絵になります。

#⚫ ハーフトーン ── 網点は「暗さ = 半径」

中間色をどう表すかが漫画表現の核です。印刷では網点(ハーフトーン)を使うので、それを再現しました。

ts
const v = toneMap[idx] / 255      // 1 = 白

if (v >= 0.2 && v <= 0.78) {
  const darkness = 1 - v
  const r = (size / 2) * darkness * preset.halftoneStrength
  if (r < 0.4) continue
  ctx.arc(cx, cy, r, 0, Math.PI * 2)
}

暗いほどドットの半径を大きくする、それだけです。グリッドを一定間隔で走査して、そのセルの明るさから半径を決めて円を打ちます。

実装しながら 3 つ気づきがありました。

  • 明るい側と暗い側にはドットを打たない (v >= 0.2 && v <= 0.78)。真っ白と真っ黒はベタで表現するほうが漫画らしく、全域に打つと灰色の絵になります。
  • 線の上には重ねない (if (lineMask[idx]) continue)。ペン線の上に網点が乗ると、線が汚れて見えます。
  • 半径 0.4 未満は打たない。1px 未満の円は Canvas ではただの薄い点になり、ノイズに見えます。

この 3 つの足切りを入れる前と後で、「漫画っぽさ」が明確に変わりました。

#💨 速度線は中心からの放射

効果線は、指定した中心点から外に向かって線を引くだけです。ユーザーが画面をクリックすると中心が移動するようにしました。中心をどこに置くかで絵の意味が変わるので、自動で決めずに触らせる判断です。

#🎭 4 つのプリセット

少年・少女・劇画・アメコミの 4 つを用意しました。違いは主にパラメータの組み合わせです。

プリセット線の太さトーン階調網点サイズ速度線
少年太い少ない中多い
少女細い多い細かい少ない
劇画太い少ない粗いなし
アメコミ中極端に少ない粗い中

同じパイプラインでも、6 つのつまみの値だけで画風が変わります。 学習モデルを 4 つ持つ代わりに、パラメータ表を 4 つ持つという解き方です。

#😓 苦労したところ

「変換されたことが分かる」出力にする。 最初の版は処理が上品すぎて、元の写真と並べても違いが分かりませんでした。線を太らせ、階調を減らし、網点を粗くする ── やりすぎに振ってから戻すほうが、結果的に良いところに着地しました。

BEFORE / AFTER を並べて出す。 変換系のツールは、出力だけ見せても効果が伝わりません。元画像と結果をポラロイド風に並べて置く構成にしました。

大きい画像で固まる。 Bilateral が半径の二乗で効くので、4000px 級を投げると数十秒かかります。処理前に長辺を縮めてから走らせ、その旨を画面に出しています。

#🔭 今後の拡張

  • スクリーントーンの種類を増やす(現在は円ドットのみ。線トーン・砂目)
  • 集中線(放射ではなく一点集中)と背景の吹き飛ばし
  • コマ割りテンプレートへの流し込み
  • Web Worker で処理を逃がして、大きい画像でも UI を止めない

#💡 このサービスから言えること

「AI でやること」に見える課題が、1970〜80 年代の画像処理で届くことがあります。 Sobel は 1968 年、Bilateral フィルタは 1998 年、網点にいたっては印刷技術です。モデルのダウンロードも推論待ちもなく、写真は端末から出ません。

そして**「らしさ」は足し算ではなく引き算で出る**ということ。色を減らし、階調を減らし、中間を捨てる。情報を削るほど漫画に近づきました。

[ ./next_action ]

読んだら、 MangaSnap を実際に動かす。

この開発ログは MangaSnap をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ShadowTwin

立体パズルの別解を作る:同じ影と最少の積み木

ShadowTwinの開発記録。3方向の図から別々の最少配置を2つ探す仕組み、5問の独立全探索、スマホの横並び比較、保存・再編集と入力検査を解説します。実照明の計算や能力診断ではありません。

read log →
SlitCard

スリットアニメーションを作る:4コマの合成と透明原稿の設計

SlitCardの開発記録。4コマの帯を交互に合成し、すき間と黒帯で別のコマを見せる仕組みを解説。同縮尺の台紙・透明シートSVG、50mm確認線、手動プレビュー・初期非表示、再編集と実出力検査を整理します。

read log →
WindowLight

日当たりシミュレーションを作る:窓の光を床へ投影する幾何モデル

WindowLightの開発記録。太陽位置近似から窓の4隅を床へ投影し、凸多角形を部屋で切り取る設計を解説。国立天文台の16時刻との限定照合、大気差・低高度・実際の採光との違い、SVGと設定の再編集を整理します。

read log →
GridLights

電源構成の比較を作る:割合と実量を100個の電球で読み解く

電源構成比較GridLightsの開発記録。12か国の年次発電データを最大剰余法で100個の電球に変換し、割合とTWhを比較する設計を解説。欠損を0補完しない7分類、予想と実値、出典付きSVGと実データ検証を整理します。

read log →
SoundLoom

音のお絵かきを作る:実WAVから絵を測り直す設計

音のお絵かきの開発記録。描画を正弦波のPCMとWAVへ変換し、保存音声の周波数をGoertzel法で測って比較する仕組みを解説。音量・測定窓・量子化の限界と、編集・離脱時の古い結果を抑止する検証を整理します。

read log →
MazeLoom

宝探し迷路を作る:寄り道まで含めた最短ルートの探索

宝探し迷路の開発記録。通路を木として生成し、現在地と3つの宝の取得状態を組み合わせて最短ルートを探す仕組みを解説。問題コードの版固定、画面と解答付きPDFの一致、手数と印刷の検証を整理します。

read log →