Chapter 80

モーションブラー

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

実行例


解説

あるピクセルに注目して、そのピクセルと斜めにあるピクセルの平均値をRGBごとに算出する。算出したRGBごとの平均値を、別に用意した新しい画像の同じ位置にピクセルとして代入する。

同じ画像のピクセルに計算結果を上書きすると、別のピクセルで平均値を算出する際に、もとのピクセルの値ではなく計算済みの平均値を取得してしまう場合がでてくるため、新しいピクセルに計算結果を入れる必要がある。

斜めの範囲を広げると、その分大きくぶれたブラーができる。

範囲計算の際にインデックスがはみ出る場合はその部分の計算は行わない。

上記だと、 (171 + 180) / 2 ≒ 175 を新しい画像のピクセルに代入する。

コード例

const range = 5;
let img;

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

function setup() {
  createCanvas(img.width, img.height);

  const dest = createImage(img.width, img.height);
  dest.copy(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
  dest.loadPixels();

  for (let y = 0; y < img.height; y++) {
    for (let x = 0; x < img.width; x++) {
      let tr = 0;
      let tg = 0;
      let tb = 0;

      let count = 0;

      for (let i = -range; i <= range; i++) {
        const ty = y + i;
        const tx = x + i;
        if (0 < ty && ty < img.height && 0 < tx && tx < img.width) {
          const pixel = getPixel(img, tx, ty);
          const r = red(pixel);
          const g = green(pixel);
          const b = blue(pixel);
          tr += r;
          tg += g;
          tb += b;
          count++;
        }
      }

      tr /= count;
      tg /= count;
      tb /= count;

      for (let yy = 0; yy < range; yy++) {
        for (let xx = 0; xx < range; xx++) {
          const ty = y + yy;
          const tx = x + xx;
          if (ty < img.height && tx < img.width) {
            setPixel(dest, tx, ty, [tr, tg, tb]);
          }
        }
      }
    }
  }

  dest.updatePixels();
  image(dest, 0, 0);
}

function getPixel(img, x, y) {
  const i = (y * img.width + x) * 4;
  return [
    img.pixels[i],
    img.pixels[i + 1],
    img.pixels[i + 2],
    img.pixels[i + 3],
  ];
}

function setPixel(img, x, y, pixel) {
  const i = (y * img.width + x) * 4;
  img.pixels[i + 0] = pixel[0];
  img.pixels[i + 1] = pixel[1];
  img.pixels[i + 2] = pixel[2];
}