マガジン

生成した絵の「輪郭」だけを使う:線画への変換と活用

遊びをつくる

執筆にClaudeを使用しています。

コード・絵・音を表す三つの作業領域を、中央の手と黄色い駒がつなぐ版画
記事のテーマをもとに制作したAI生成イラスト。

画像生成で作ったキャラクターや背景を、ゲームの素材として並べる場面を考えてみます。1枚ずつ見れば魅力的でも、並べると陰影のつけ方や質感、色の傾向がそろっておらず、同じゲームの絵に見えないことがあります。生成のたびに画風が少しずつ揺れるためです。 この記事で確かめるのは、次の問いです。生成画像から輪郭の線だけを取り出し、塗りを自分で足すと、素材の統一感は上がるのか。上がるなら、どんな条件の画像でそうなるのか。線は生成物に任せ、色の決定は自分の手に戻す、という分担が成り立つかを見ます。 結果はここには書きません。扱う画像も環境も読者ごとに違うため、答えは手元で出すものです。以下は、そのための実験の設計書です。

この記事の目次
  1. 何を比べるのか:仮説を3つに分ける
  2. 準備するもの
  3. 手順:線を取り出し、領域に色を置く
  4. 記録の取り方
  5. 結果の読み方と、起こりやすい誤り
  6. 次の行動

何を比べるのか:仮説を3つに分ける

「統一感が上がるか」は漠然としているので、確かめられる単位に分けます。

  • 仮説A:線だけを抜くと、画像ごとの質感の差が見えにくくなる。
  • 仮説B:同じパレットで塗ると、元画像のままより「同じ作品の素材」に見えやすい。
  • 仮説C:線の抽出がうまくいくかは、元画像の描き方(陰影の強さ、背景の複雑さ)で変わる。

比べる対象は「元画像のまま」と「線画+自分の塗り」の2系統です。同じ画像の2系統を、ゲーム画面を想定した同じ背景の上に並べ、見え方を評価します。

準備するもの

  • 生成画像を3〜5枚。同じ種類のもの(たとえば小さな動物のキャラクター)でそろえます。陰影が強い絵と、平坦な絵を混ぜておくと、仮説Cが確かめやすくなります。
  • 下記のHTMLを保存した lineart.html と、最新のブラウザ。外部ライブラリや通信は使いません。
  • 塗りに使うパレット。5色以内に決めておきます。
  • 記録用の表(後述)とストップウォッチ。

生成画像の商用利用や公開の可否は、使ったサービスの利用規約で決まる部分があります。ゲームに入れて公開する前に、各サービスの公式の案内で確認してください。著作権に関わる判断は、必要なら専門家にも相談してください。

手順:線を取り出し、領域に色を置く

次のコードは、画像を最大幅512に縮め、明るさの変化が大きい場所を線として抜き出します。濃淡の変化を調べる手法で、ソーベルフィルタと呼ばれます。しきい値のスライダーで線の出方を変え、閉じた領域をクリックすると、選んだ色で塗れます。

html
<input type="file" id="f" accept="image/*">
閾値<input type="range" id="t" min="20" max="400" value="120">
色<input type="color" id="k" value="#e8a33d">
<br><canvas id="c" style="max-width:100%"></canvas>
<script>
const $=i=>document.getElementById(i),c=$('c'),x=c.getContext('2d');
let src,line,col,W,H;
$('f').onchange=e=>{const im=new Image();im.onload=()=>{
  const s=Math.min(1,512/im.width);
  W=c.width=Math.round(im.width*s);H=c.height=Math.round(im.height*s);
  x.drawImage(im,0,0,W,H);src=x.getImageData(0,0,W,H).data;edge();};
  im.src=URL.createObjectURL(e.target.files[0]);};
$('t').oninput=()=>src&&edge();
function edge(){const T=+$('t').value,g=new Float32Array(W*H);
  for(let i=0;i<W*H;i++)g[i]=.3*src[i*4]+.59*src[i*4+1]+.11*src[i*4+2];
  line=new Uint8Array(W*H);col=new Array(W*H).fill(null);
  for(let y=1;y<H-1;y++)for(let p=1;p<W-1;p++){const i=y*W+p;
   const gx=g[i-W+1]+2*g[i+1]+g[i+W+1]-g[i-W-1]-2*g[i-1]-g[i+W-1];
   const gy=g[i+W-1]+2*g[i+W]+g[i+W+1]-g[i-W-1]-2*g[i-W]-g[i-W+1];
   line[i]=Math.hypot(gx,gy)>T?1:0;}
  draw();}
function draw(){const o=x.createImageData(W,H);
  for(let i=0;i<W*H;i++){const v=line[i]?[20,20,20]:(col[i]||[255,255,255]);
   o.data.set([v[0],v[1],v[2],255],i*4);}
  x.putImageData(o,0,0);}
c.onclick=e=>{if(!line)return;const r=c.getBoundingClientRect();
  const px=Math.floor((e.clientX-r.left)*W/r.width),py=Math.floor((e.clientY-r.top)*H/r.height);
  const k=$('k').value,rgb=[1,3,5].map(n=>parseInt(k.slice(n,n+2),16));
  const st=[py*W+px];
  while(st.length){const i=st.pop();if(line[i]||col[i]===rgb)continue;
   col[i]=rgb;const p=i%W;
   if(p>0)st.push(i-1);if(p<W-1)st.push(i+1);
   if(i>=W)st.push(i-W);if(i<W*(H-1))st.push(i+W);}
  draw();};
</script>

操作の流れは次のとおりです。

  1. 画像を選びます。初期値のしきい値で線が出ます。120は出発点にすぎず、画像ごとに調整します。
  2. スライダーを動かし、「主要な輪郭が残り、細かな模様は消える」位置を探します。
  3. 色を選び、領域をクリックして塗ります。しきい値を動かすと塗りはリセットされるので、線の調整を先に終えてから塗ります。
  4. 全画像を同じパレットで塗り、画面を保存します。

線が途切れていると、塗りが隣の領域へあふれます。この「塗り漏れ」が、実験で数える中心の現象です。

記録の取り方

画像1枚ごとに、しきい値を3段階(低・中・高)に固定して比べます。3段階の具体的な値は、画像を見ながら自分で決めて構いません。ただし全画像で同じ3つの値を使ってください。

同じ画像・同じしきい値なら出力は毎回同じなので、反復は不要です。反復にあたるのは画像の枚数です。3枚より5枚のほうが、傾向を読む材料が増えます。

  • 例:画像1、しきい値:低、不要なノイズ線の数:数える、塗り漏れの箇所数:数える、1領域の塗り時間:秒で測る
  • ノイズ線は、輪郭ではない細かな線の塊を、数え方を決めて数えます。たとえば「画面を4分割して、それぞれで目立つ塊を数える」といった基準を事前に決めておきます。
  • 塗り漏れは、主要な領域(体、顔、背景など)を3〜5か所決め、クリックしてあふれたものを数えます。
  • 時間は、同じ領域を塗り始めてから、きれいに収まるまでを測ります。

統一感の評価は、人の目で行います。同じ背景の上に「元画像」と「線画+塗り」を5枚ずつ並べ、順番をシャッフルします。見る人には、どれが元でどれが加工後かを伝えず、「同じゲームの素材に見える度合い」を5段階で付けてもらいます。自分で採点すると期待が入り込むため、他の人に見てもらうのが望ましいです。頼める人数は事情によって変わるので、集まった人数のまま記録します。

結果の読み方と、起こりやすい誤り

数字がそろったら、仮説ごとに次のように読みます。

  • 仮説A:しきい値が中程度の段階で、画像間のノイズ線の数が近ければ、線画にする過程で質感の差が減ったと読めます。ばらつきが大きいままなら、元画像の描き方の差が線に残っています。
  • 仮説B:2系統の評価点を比べます。線画+塗りのほうが高く、かつ画像間の点のばらつきが小さければ、パレット統一の効果が出ています。平均だけ高くてもばらつきが残るなら、一部の画像が足を引っ張っています。
  • 仮説C:陰影の強い絵と平坦な絵で、塗り漏れの箇所数を比べます。陰影が強い絵のほうで漏れやノイズが増えるなら、線を取る前提では平坦な絵を選ぶ、という素材選びの基準になります。

誤りやすい点も挙げておきます。

  • 線を濃く出しすぎる:しきい値が低いと、毛並みや布の模様まで線になり、塗り領域が細かく割れます。「たくさん線が出た」を成功と見ないでください。
  • 線が切れているのに塗る:隙間が1画素でもあれば、塗りはあふれます。あふれた時は、塗り直すよりしきい値を調整するほうが早いです。
  • 比べる条件がずれる:画像によってしきい値を自由に変えると、画像ごとの差なのか、設定の差なのか分かりません。3段階に固定して比べてください。
  • 元画像の縮小による影響を忘れる:このコードは最大幅512に縮めています。解像度が違えば線の出方も変わります。実際の素材サイズで使うなら、その解像度でも一度確かめてください。
  • 評価者に説明しすぎる:意図を伝えると採点が引きずられます。

結果がよくなかった場合も、使い道は残ります。線画を下絵として、手で描き直す前提で使うこともできます。逆に結果がよかった場合は、素材づくりの手順として固定できます。

次の行動

まず画像3枚で、コードを保存して動かし、ノイズ線と塗り漏れだけを数えてみてください。それだけで、手持ちの生成画像のうち線画に向いているものが見分けられます。

ゲームに組み込む段階では、線画と塗りの画像をPNGで書き出し、相対パスで参照します。アソバースへ投稿する場合は、直下に index.html を置いたZIP(20MiB以内)にまとめます。そのうえで、使った生成サービスの利用条件を、公開前に公式の案内で確認してください。