第06回 ディザリング

ディザリングとは、実際には少ない色だけを使いながら、遠目には多くの色を使っているかのように見せる手法のことをいう。

今回のプログラムの最終的な機能

プログラムを実行すると実行画面上に元画像が表示される。
さらに実行画面上でクリックすると、dataフォルダに の6つの画像が作られる。
コンソールに「完了」が表示されてからキーボードの0~6のキーを押すとこれらの画像が画面に表示される。
(画像サイズが大きい場合は出力画像が作られるまでに時間がかかる)
(クリックだけですべての画像が作成されるので、キーボードを使うのは結果確認のためだけ)
キー画像
0元jpg
11減色.png
21二値化.png
32組織カラー.png
42組織グレー.png
53ランダムカラー.png
63ランダムグレー.png


「1」「3」「5」キーで表示される画像で使われているのはいずれも8色のみだが、1キーで表示される「1減色.png」に比べて残り2つではかなり元画像の構造や色合いが残る。
同様に「2」「4」「6」ではいずれも白・黒の2種類のピクセルしか使われていないが、2キーで表示される「1二値化.png」に対して残り2つでは元の構造がかなり残る。

減色・二値化

概要

単純にピクセルの色数を減らすと情報が大きく失われることを確認するために、まずは減色・二値化の処理を行う。
Processingでは、これらの処理はfilter関数で簡単に実行できる。
(引数に「POSTERIZE」を入れると減色 (ポスタリゼーション)、「THRESHOLD」を入れると二値化の処理が行われる)

課題 1

ベースのプログラム (クリックすると6枚の画像ができるが、課題1のものは元画像と同じもの、課題2, 3のものは黒い画像になる)
// 出力用画像の変数
PImage[] img = new PImage[7];
// 出力ファイル名
String[] fName = {"1減色", "1二値化", "2組織カラー", "2組織グレー", "3ランダムカラー", "3ランダムグレー"};
int w, h;

void setup() {
  size(600, 450);
  img[0] = loadImage("元.jpg");
  w = img[0].width;
  h = img[0].height;
  // 1~6番を同じサイズの黒画像にする
  for (int i=1; i<=6; i++) {
    img[i] = img[0].get();
  }
  background(0);
  image(img[0], 0, 0, width, height);
}

void draw() {
}

// type(1~6)に応じた処理を行い、その画像を保存
void colorFilter(int type) {
  if (type==1) {
    // 減色 (課題1で変更を加える)
  } else if (type==2) {
    // 二値化 (課題1で変更を加える)
  } else {
    // 画像の範囲内での繰り返し
    for (int y=0; y<h; y++) {
      for (int x=0; x<w; x++) {
        img[type].pixels[x+y*w] = getDitheredColor(x, y, type);
      }
    }
  }
  img[type].save("data/" + fName[type-1] + ".png");
}

// 組織的ディザリング、ランダムディザリングでの出力画像の(x, y)のピクセルの色を返す
// (課題2, 3で変更を加える)
color getDitheredColor(int x, int y, int type) {
  if (type<=4) {
  } else {
  }
  color c = img[0].pixels[x+y*img[0].width];
  float r=0;
  float g=0;
  float b=0;
  if (type%2==1) {
  } else {
  }
  return color(r, g, b);
}

// フィルタ処理を実行
void mousePressed() {
  for (int i=1; i<=6; i++) {
    colorFilter(i);
  }
  println("完了");
}

// 押したキーに応じて対応する画像を表示
void keyPressed() {
  int k = key-'0';
  if (k>=0 && k<7) {
    background(0);
    image(img[k], 0, 0, width, height);
  }
}
  1. Processingのエディタに上のサンプルプログラムのコードをコピー&ペーストする。
  2. 「img06」という名前で保存する。
  3. 適当に画像検索してサンプル用の画像を用意し、「元.jpg」という名前で保存する (明るさ、色合いの異なる部分がそれなりにあるもの。漫画・アニメの絵のような単色部分の多いものはNG)。
  4. 「元.jpg」をProcessingのエディタにドラッグ&ドロップする。
  5. colorFilter関数の「// 減色 (課題1で変更を加える)」の下に以下のコードを追加して実行して画面をクリックする。
  6. (「完了」が表示されたあとでキーボードの1キーを押すと、白・黒・赤・緑・青・シアン・マゼンタ・黄の8色に減色したものが表示される)
    (ちなみに、この場合の第2引数はR, G, Bの色成分を何段階にするかを決めるもの。ここでは2にしたため、0か255の2段階になる。この数値を増やせば色数はもっと多くなる)
    img[1].filter(POSTERIZE, 2);


  7. colorFilter関数の「// 二値化 (課題1で変更を加える)」の下に以下のコードを追加して実行して画面をクリックする。
  8. (「完了」が表示されたあとでキーボードの2キーを押すと、白黒になったものが表示される)
    (それぞれのピクセルが黒になるか白になるかは、元画像のピクセルの明るさが閾値 (「しきいち」、または「いきち」と読む) より高いか低いかで決まる。ここではこの値を0.5にしたので、明るさが255*0.5に満たない場合に黒、そうでない場合は白になる)
    img[2].filter(THRESHOLD, 0.5);


  9. 見せたい部分の構造が白黒の画像に最もよく残るように、上記 (説明の6番目) で加えたコードの第2引数の値を調整する。
  10. (第2引数の値を小さくすると出力画像は白部分が多くなり、値を大きくすると黒部分が多くなる。有効な設定範囲は0~1)
    (値を変えて試した結果、結局0.5が一番良い結果になった場合は、課題提出のメール本文にそのことを明記する)
    (どうしても構造がつぶれてしまう場合は元画像を別のものにする)
    第2引数出力画像
    0.3
    0.5
    0.7
  • この段階でdataフォルダの出力画像のうち「1減色.png」「1二値化.png」は然るべきもの、それ以外は真っ黒な状態になっている。
  • 最終的なcolorFilter関数は元画像によって異なるので、今回は「お手本」は省略する。

組織的ディザリング

概要

課題1で見たように、普通に二値化や8色への減色を行うと、元画像の情報が大きく失われてしまう。そこで、
  • ピクセルによって閾値を変える
  • ただし、画像内のどこでも「狭い範囲の閾値の平均値」が同じになるようにする
という工夫をすると、
のように、実際は白と黒の点しかなくても、その密度の違いで疑似的な明るさを表わせるようになる。具体的には、


のようなマスクを用意し、元画像を4×4のブロックに分割して、対応するマスクの値に応じて閾値を変える。


それぞれのピクセルに対する閾値は、

「そこに対応するマスクのマスの数値」×16 + 8

で求められる。例えば、上の図の赤矢印、青矢印の先のピクセルに対する閾値はそれぞれ168, 24になる。マスクの値は平均して7.5なので、全体としての閾値の平均値は7.5×16+8=128になる。
このようにして、4×4のマスの中では、明るさが同じピクセルでも、あるものは白、あるものは黒に変わることになる。これをもっと広い範囲で見ると、元画像で「暗めの灰色」のところでは黒い点が多めに、「明るい灰色」では白い点が多めになり、遠くから見ると明るさの情報が保たれているように見える画像をつくることができる。
明るさではなくR, G, B成分の値について同様の処理を行えば、「1減色.png」と同様に8色だけのピクセルをつかいながらも、元画像の色情報を多く残した画像になる。

ここで上げたマスクは組織的ディザリングで使われるものの一つでBayer型と呼ばれるもの。このほかにも

渦型


網点型


などが存在する。

課題 2

  1. getDitheredColor関数の先頭に以下のコードを入れる。
  2. (maskは「概要」で説明したマスクの値を入れた配列。tは閾値を入れる変数)
      float[] mask = {
        0, 8, 2, 10, 
        12, 4, 14, 6, 
        3, 11, 1, 9, 
        15, 7, 13, 5
      };
      float t=0;

  3. 「if (type<=4) {」の下に以下のコードを入れる。
  4. (参照ピクセルの元画像上の座標が(x, y)なら、の4×4に分割したブロックの中での横座標はxを4で割った余り(0~3の値をとる)→Processingのコードでは「x%4」。縦座標も同様に「y%4」になる。マスクの左上から右下まで振った通し番号0~15は、「マスクの中の横座標 + マスクの中の縦座標×4」で得られる)
        t = mask[x%4 + y%4*4]*16+8;

  5. 「if (type%2==1) {」の下に以下のコードを入れる。
  6. (変数cには、元画像の座標(x, y)のピクセルの色情報が入っている)
        r = red(c) < t ? 0 : 255;
        g = green(c) < t ? 0 : 255;
        b = blue(c) < t ? 0 : 255;
    補足
    「?」は条件演算子と呼ばれるもの。「条件 ? 値1 : 値2;」のように記述すると、条件が満たされるときは値1、満たされないときは値2が得られる。例えばこの1行目では「red(c) < t」つまり「元画像のそのピクセルの赤成分が t より小さい」という条件が満たされれば0, そうでなければ255が変数 r に代入される。ここで追加した3行を、条件演算子を使わずに書くと以下のようになる。
        if (red(c) < t){
          r = 0;
        } else {
          r = 255;
        }
        if (green(c) < t){
          g = 0;
        } else {
          g = 255;
        }
        if (blue(c) < t){
          b = 0;
        } else {
          b = 255;
        }

  7. 実行して画面をクリックする。
  8. (「完了」が表示されたあとでキーボードの3キーを押すと、白・黒・赤・緑・青・シアン・マゼンタ・黄の8色でディザリングしたものが表示される)
    (1キーで表示される単純な減色の結果と比較すると、元画像 (0キーで表示される) の色合いが大きく残っていることがわかる)


  9. 上記 (3番目のステップ) で追加したコードの下の「} else {」の下に、「cの明るさが t に満たなければr, g, bを0に、そうでなければ255にする」という処理を追加し、実行して画面をクリックする。
  10. (明るさはbrightness関数で取得できる。書式は3番目のステップで使ったred, green, blue関数と同様)
    (「完了」が表示されたあとでキーボードの4キーを押すと、白・黒の2色でディザリングしたものが表示される)
    (2キーで表示される単純な二値化の結果と比較すると、元画像 (0キーで表示される) の構造が大きく残っていることがわかる)


  11. この時点で「2組織カラー.png」「2組織グレー.png」も然るべき状態になる。
  12. うまくいかない場合は、この時点のgetDitheredColor関数とコードを見比べる。

ランダムディザリング

概要

組織的ディザリングでは4×4のマスクを使っているため、得られた画像を拡大すると「繰り返し感」が目立つ。


マスクを使わずにすべてのピクセルについて0~255のランダムな値を閾値として使うことで、この「繰り返し感」をなくすことができる (もっとも、組織的ディザリングに比べると人間の目での見え方で「(なら)される」のに広い範囲が必要になるので、大きな画像でないと汚く見える)。この手法をランダムディザリングという。

課題 3

  1. getDitheredColor関数の「} else {」の下 (2つあるうちの上の方。この時点で空っぽになっているところ。「typeが4以下でない」ときに実行される部分) に以下のコードを追加する。
  2. (random関数では、0からその引数までのランダムな実数値が得られる。この場合は0以上256未満の値になる)
    ((int)を使うと、実数の小数部分が切り捨てられる。得られる乱数は0.0000...~255.999...なので、0~255の値になる)
        t = (int)random(256);

  3. 実行して画面をクリックする。
  4. (「完了」が表示されたあとでキーボードの5キーを押すと、白・黒・赤・緑・青・シアン・マゼンタ・黄の8色でランダムディザリングしたものが表示される)


    (キーボードの6キーを押すと、白・黒の2色でランダムディザリングしたものが表示される)


提出

小テスト予告

次回の授業の初めに今回の内容についての小テストを行う。

戻る

inserted by FC2 system