みさご解体新書

セピア調

実行例

ソースコード

TypeScript

0.jpg / app.ts

解説/アルゴリズム

画像をレトロ風な茶褐色の色調に変える処理をセピア調と呼ぶ。

// セピア調として掛け合わせる定数
R = R * 0.393 + G * 0.769 + B * 0.189;
G = R * 0.349 + G * 0.686 + B * 0.168;
B = R * 0.272 + G * 0.534 + B * 0.131;

// 計算結果が色の最大値を超えないようにする
R = min(255, R);
G = min(255, G);
B = min(255, B);

セピア調としてよく利用される定数が上記で、RGB それぞれに掛け合わせて計算する。

掛け合わせる定数の合計値が 1 を超えているので、色の最大値を超えないように調整を行う。

コード例(GLSL)

attribute vec2 aPosition;
attribute vec2 aTexCoord;
uniform vec2 uResolution;
varying vec2 vTexCoord;

void main() {
  vec2 clipSpace = aPosition / uResolution * 2.0 - 1.0;
  gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
  vTexCoord = aTexCoord;
}
precision mediump float;
uniform sampler2D uImage;
varying vec2 vTexCoord;

void main() {
  vec4 color = texture2D(uImage, vTexCoord);
  float r = min(1.0, color.r * 0.393 + color.g * 0.769 + color.b * 0.189);
  float g = min(1.0, color.r * 0.349 + color.g * 0.686 + color.b * 0.168);
  float b = min(1.0, color.r * 0.272 + color.g * 0.534 + color.b * 0.131);
  gl_FragColor = vec4(r, g, b, 1.0);
}

コード例(p5.js)

import * as p5 from "p5";

new p5((p: p5) => {
  let image: p5.Image;

  p.preload = () => {
    image = p.loadImage("./0.jpg");
  };

  p.setup = () => {
    p.createCanvas(p.windowWidth, p.windowHeight);
    image.loadPixels();

    // 画像の全ピクセルを走査
    for (let y = 0; y < image.height; y++) {
      for (let x = 0; x < image.width; x++) {
        // 対象のピクセルを取り出す
        const color = getPixel(x, y);

        // RGBを取り出す
        const r = p.red(color);
        const g = p.green(color);
        const b = p.blue(color);

        // セピア調の定数を掛け合わせる
        // 色の最大値を超えないように調整する
        const nr = p.min(255, r * 0.393 + g * 0.769 + b * 0.189);
        const ng = p.min(255, r * 0.349 + g * 0.686 + b * 0.168);
        const nb = p.min(255, r * 0.272 + g * 0.534 + b * 0.131);

        // 計算したRGBをピクセルにセットし直す
        setPixel(x, y, [nr, ng, nb]);
      }
    }

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

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

  function setPixel(x: number, y: number, color: number[]): void {
    const i = (y * image.width + x) * 4;
    image.pixels[i + 0] = color[0];
    image.pixels[i + 1] = color[1];
    image.pixels[i + 2] = color[2];
  }
});

内部で利用しているアルゴリズム

クランプ処理