Chapter 87

ヒストグラム

miku
miku
2021.11.19に更新
このチャプターの目次

クラスでテストを行い、それぞれが何点を取ったかのグラフを作りたい。普通は1点ごとではなく20点おきのようなある程度の区間を持って区別をするはずだ。そのような区間を階級と呼び、各区間ごとに属する人数、たとえば40~60点の範囲には10人のような数を度数と呼ぶ。

ヒストグラムは上記のような、横軸に階級を、縦軸に度数をとるグラフのことで、この章では画像のヒストグラムを扱う。

ヒストグラム

画像のRGBの範囲は0~255だが、その256種類を横軸、縦軸をそれに属する数、たとえば赤100のピクセルの数がいくつで・・・と計算してヒストグラムを作ると上記のようになる。横は256種類しかないので、1pxずつ割り当てるとすると、256pxあれば区間が潰れずに表現ができるが、縦はかなりの数になることが予想される。たとえば1920x1080の画像なら、最大で200万を超えるので縦幅は意図的に抑えなければならない。

const h = map(v, 0, max_v, 0, max_h);

この場合、もっとも度数が多い階級 max_v を計算しておき、 map() を利用して 0~max_v の範囲を 0~max_len に変えて配置すればいい。

let img;

function preload() {
  img = loadImage("0.png");
}

function setup() {
  createCanvas(windowWidth, windowHeight);

  img.loadPixels();
  let vr = new Array(256).fill(0);
  let vg = new Array(256).fill(0);
  let vb = new Array(256).fill(0);
  for (let i = 0; i < img.width * img.height; i++) {
    const r = img.pixels[i * 4];
    const g = img.pixels[i * 4 + 1];
    const b = img.pixels[i * 4 + 2];
    vr[r]++;
    vg[g]++;
    vb[b]++;
  }

  drawHist(vr, 0, 0, "red");
  drawHist(vg, 0, height / 3, "green");
  drawHist(vb, 0, (height / 3) * 2, "blue");
}

function drawHist(hist, x, y, c) {
  translate(x, y);
  stroke(c);
  noFill();

  const maxLen = height / 3;
  const maxR = max(hist);
  for (let i = 0; i < 256; i++) {
    const len = map(hist[i], 0, maxR, 0, maxLen);
    line(i, maxLen, i, maxLen - len);
  }

  resetMatrix();
}

ヒストグラム その2

上記作例ではRGBごとの最大値を maxLen に変換しているので、たとえば赤の範囲内の比較はできるが、赤と緑とある区間の棒の長さが同じであっても本当に度数が同じかどうかの保証はない。この場合は、もっとも度数が多い階級 max_v の定義を、各RGBごとではなく、RGBすべての階級でもっとも度数が多い数を入れる、と変更すればいい。

const max_v = max([max(vr), max(vg), max(vb)]);

具体的には上記のようになる。

let img;

function preload() {
  img = loadImage("0.png");
}

function setup() {
  createCanvas(windowWidth, windowHeight);

  img.loadPixels();
  let vr = new Array(256).fill(0);
  let vg = new Array(256).fill(0);
  let vb = new Array(256).fill(0);
  for (let i = 0; i < img.width * img.height; i++) {
    const r = img.pixels[i * 4];
    const g = img.pixels[i * 4 + 1];
    const b = img.pixels[i * 4 + 2];
    vr[r]++;
    vg[g]++;
    vb[b]++;
  }

  const max_v = max([max(vr), max(vg), max(vb)]);

  drawHist(vr, max_v, 0, 0, "red");
  drawHist(vg, max_v, 0, height / 3, "green");
  drawHist(vb, max_v, 0, (height / 3) * 2, "blue");
}

function drawHist(hist, max_v, x, y, c) {
  translate(x, y);
  stroke(c);
  noFill();

  const maxLen = floor(height / 3);
  for (let i = 0; i < 256; i++) {
    const len = map(hist[i], 0, max_v, 0, maxLen);
    line(i, maxLen, i, maxLen - len);
  }

  resetMatrix();
}