グラフィック

ざっくり解説【色空間について】

グラフィック
この記事は約10分で読めます。

こんにちは、gonzoです。
この度は、専門用語をなるべく使わないで色の表現方法である『色空間』についてざっくり解説していきたいと思います。

我々が日ごろ遊んでいる『ビデオゲーム』はその名の通り、映像を主としたコンテンツの事で視覚とは密接な関係にあります。そんな中で皆さんは『色』について考えたことがありますか?
デジタルの世界では色を表す方法がいくつもあります。
そして、色を座標(数値)で示す表現は『色空間』と呼ばれ、それぞれ表現できる色の範囲や種類が異なります。

それらは場合によって使い分けられるのですが、それぞれどのような特徴があるのでしょうか。
身近に良く使われているものをいくつかざっくりと紹介したいと思います。

RGB

rgb

赤(Red)、緑(Green)、青(Blue)の3つの成分から成る色空間です。
これらは光の三原色とも言われますが、各成分が強いほど明るくなり、均等に増やしていくことで白に近づきます。
デジタルの世界では最も頻繁に用いられる表現であり、現代ではその3色をそれぞれ256段階に区切ったものを組み合わせることで表現できる約1677万色(256x256x256)が『フルカラー』と呼ばれたりします。

人間の眼球にはこの3色の波長に敏感に反応する細胞があるため、多くの映像機器ではそれに合わせて、このRGBの光を混合で出力しています。
例えばカラー液晶ディスプレイは大量のRGBの素子が敷き詰められていて、それらを照らしているバックライトからの光を通す量を調節することで色彩を表現しています。

液晶ディスプレイをマクロ撮影したもの

また、RGBの値は3つをまとめて16進数の6桁のカラーコード(例:#AB12EF)で表されたりもしますが、インターネット上でよく見かける方は多いのではないでしょうか。

gonzo
gonzo
ちなみに波長の長い赤色は、遠くからでも比較的近くにあるように見えるから信号機の停止の色になっているらしいニャ
言われてみればそうかもしれないニャー
ソフィアちゃん
ソフィアちゃん
でも猫さんは赤色が見えないって聞きましたよ?
gonzo
gonzo
(もしや、吾輩は猫ではない・・・!?)

CMYK

cmy

シアン(Cyan)、マゼンタ(Magenta)、イエロー(Yellow)、ブラック(Key plate)の4つの成分から成る色空間です。
主に印刷の分野で使われ、カラープリンターのインクの色でよく見かけると思います。
ブラックはどうして『K』なのかというと、輪郭や文字を刷るのに用いる黒インク用の版である『キー・プレート』が元になっているからだそうです。
CMYだけでも黒色は表現できますが、印刷する際にはコストを抑えるために頻繁に使われるブラックが単独で追加されています。

RGBとは反対にこれらの成分は分量が増えるほど暗くなり、黒に近づきます。

gonzo
gonzo
CMYKでは色が濃くなるほど暗くなる理由を簡単に言えば、RGBが光源なのに対して、こちらは光の反射材を示すものだからだニャー
ソフィアちゃん
ソフィアちゃん
なるほど!絵の具は白い紙に濃く塗れば塗るほど、紙が隠されて暗くなっていきますよねっ

HSV

色相(Hue)、彩度(Saturation)、明度(Value)の3つの成分による色空間です。HSB(BはBrightness)とも呼ばれたりします。
色の相対表現がしやすい特徴があり、直観的に分かりやすいのでフォトレタッチやイラストを描いたりする方には重宝されていると思います。

ほとんど同様の方式であるHLS(Hue、Lightness、Saturation)という表現もありますが、こちらは明度の替わりに輝度が使われます。
ちなみに『輝度』は視覚的な明るさ、『明度』は実際の明るさの事を指します。

gonzo
gonzo
色味を調整するのが簡単♪
試しにソフィアちゃんの髪色の色相をいじってイメチェンしてみるニャー(ぐりぐり)

ソフィアちゃん
ソフィアちゃん
わわっ、勝手に変えちゃだめですよ~!💦

YUV

輝度信号(Y)、輝度信号と青色成分の差(U)、輝度信号と赤色成分の差(V)の3つの組み合わせによる色空間です。
映像機器の分野でよく用いられます。

輝度を軸とした表現で直感的にはわかりにくいと思いますが、実は大きなメリットがあります。
『人間の目は明るさの変化には敏感だが、色の変化には鈍感である』という特性に基づき、色差成分は間引いてもあまり劣化を感じないという特徴があります。
例として用意した下の2枚の画像を見比べてみてください。

クリックで拡大表示

左の元画像に対して、右は輝度をそのままに色差情報を1/4に間引いたものです。RGBに還元した後では情報量に差はありませんが、YUVでは元の完全な状態と比べて情報量は1/2になります。
詳しいことについては『クロマサブサンプリング』を調べてみると良いでしょう。

ソフィアちゃん
ソフィアちゃん
えっ、本当に間引かれてるんですかっ!?ほとんど画質の差を感じませんねっ
gonzo
gonzo
それでも画質は多少落ちてるから、細かいところは動きでごまかせる動画と相性がいいニャー
ちなみにJPEG画像でも同様の方式が使われてるニャ-

あとがき

色空間の種類について簡単に説明してきましたが、皆さんはどのくらいご存じでしたか?
デジタルの世界では色を絶対的な数値で表すことができますが、実際のところは視覚神経の個人差の他に、環境、気分や知識によって人それぞれ違って見える主観的なものです。
それが良く感じられる例として、色と深い関わりがあるファッションの分野などがあると思います。衣服の配色に人それぞれのセンスが良く表われるのは、やはり各個人が見えている色には差があるからこそ生じる多様性なのだと思います。

色覚は本来、動物が生存の為に環境に合わせて獲得してきた能力ですが、感情の発達した人間にとっては感性に働きかける要素となりました。

せっかく我々は色彩に対する神秘性を見出いだせるのですから、たまには夕焼けは何で赤いのだろう、海は何で青いのだろう、宇宙って何色なんだろう・・・
そんな途方も無い色の不思議について想いを馳せて心を潤わせてみるのもいいかもしれませんね。

おしまい。

付録

クロマサブサンプリングをウェブブラウザで試せるプログラムのコードを掲載します。
ウェブサーバー経由で表示しないと、画像データの処理でセキュリティエラーになるので注意してください。

クリックしてソースコード全文を見る
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Chroma subsampling Demo</title>
</head>
<body>
  <figure>
    <!-- お好きな画像ファイルを指定してください -->
    <img id="srcimg" src="sample.png" alt="">
    <figcaption>元画像</figcaption>
  </figure>
  <figure>
    <canvas id="dstcvs"></canvas>
    <figcaption>色差間引き後</figcaption>
  </figure>
  <script>
  const rgb2yuv = (r, g, b) => ({
    y:  0.299*r + 0.587*g + 0.114*b,
    u: -0.169*r - 0.331*g + 0.500*b,
    v:  0.500*r - 0.419*g - 0.081*b,
  });
  const yuv2rgb = ({ y, u, v }) => [
    1.000*y + 1.402*v,
    1.000*y - 0.334*u - 0.714*v,
    1.000*y + 1.772*u,
  ];

  document.getElementById("srcimg").onload = function() {
    const dstcvs = document.getElementById("dstcvs");
    [ dstcvs.width, dstcvs.height ] = [ this.width, this.height ];
    const ctx = dstcvs.getContext("2d");
    ctx.drawImage(this, 0, 0);
    const imageData = ctx.getImageData(0, 0, dstcvs.width, dstcvs.height);
    const pRGB = imageData.data;

    for (let y = 0; y < dstcvs.height; y += 2) {
      for (let x = 0; x < dstcvs.width; x += 2) {
        const scan = dstcvs.width*4
        const p00 = y*scan + 4*x;
        const p01 = p00 + 4;
        const p10 = p00 + scan;
        const p11 = p01 + scan;

        const yuv00 = rgb2yuv(pRGB[p00], pRGB[p00+1], pRGB[p00+2]);
        const yuv01 = rgb2yuv(pRGB[p01], pRGB[p01+1], pRGB[p01+2]);
        const yuv10 = rgb2yuv(pRGB[p10], pRGB[p10+1], pRGB[p10+2]);
        const yuv11 = rgb2yuv(pRGB[p11], pRGB[p11+1], pRGB[p11+2]);

        const uv = {
          u: (yuv00.u + yuv01.u + yuv10.u + yuv11.u) / 4,
          v: (yuv00.v + yuv01.v + yuv10.v + yuv11.v) / 4,
        };

        [ pRGB[p00], pRGB[p00+1], pRGB[p00+2] ] = yuv2rgb({ ...yuv00, ...uv });
        [ pRGB[p01], pRGB[p01+1], pRGB[p01+2] ] = yuv2rgb({ ...yuv01, ...uv });
        [ pRGB[p10], pRGB[p10+1], pRGB[p10+2] ] = yuv2rgb({ ...yuv10, ...uv });
        [ pRGB[p11], pRGB[p11+1], pRGB[p11+2] ] = yuv2rgb({ ...yuv11, ...uv });
      }
    }

    ctx.putImageData(imageData, 0, 0);
  };
  </script>
</body>
</html>

参考

タイトルとURLをコピーしました