ピクセルと表色系の関係

# はじめに
画像を色調補正やフィルタリングなどの操作するには，表色系について知っておく必要があるので，ピクセルと色についてまとめた記事になります．
この記事で上げた表色系（色空間）以外にもたくさんありますが，良く使いそうな表色系を取り上げてます．
RGBとの変換処理はC/C++でのサンプルソース付きです．

# 表色系の種類

## RGB表色系

ディスプレイで表示するときに必要．
各値は0～255の範囲で表現されている．

```math
{\begin{align}
Color&=(r,g,b)\\
\\
White&=(255,255,255)\\
Black&=(0,0,0)\\
\\
Red&=(255,0,0)\\
Green&=(0,255,0)\\
Blue&=(0,0,255)\\
Yellow&=(255,255,0)\\
Cyan&=(0,255,255)\\
Magenta&=(255,0,255)\\
\end{align}
}
```

※[ピクセル](http://qiita.com/ryouka0122/items/f8a2aa58d3e613e02fa6#%E3%83%94%E3%82%AF%E3%82%BB%E3%83%AB--pixel)を再掲

## HSV表色系

RGB表色系より感覚的に値を設定できる．
Hは0.0～360.0，SとVは0.0～1.0が取りうる範囲．
さらにHは角度を表してるため，360を超えている場合は，H-360と計算して範囲内に収めることが可能．

```cpp
// ------------------------------------------------
// rgb2hsv
// rgb[] : R,G,Bの順番に値が格納されている
// hsv[] : H,S,Vの順番に値が格納される
void rgb2hsv(int rgb[3], double hsv[3]) {
 int x = max(rgb[0], max(rgb[1], rgb[2]));
 int n = min(rgb[0], min(rgb[1], rgb[2]));
 hsv[2] = (double)x/255.0;
 if(hsv[2]==0.0) {
  hsv[1]=0.0;
  hsv[0]=0.0;
  return;
 }
 hsv[1] = (double)(x-n)/x;
 if(hsv[1]==0.0) {
  hsv[0]=0.0;
  return;
 }
 double h = 0.0;
 if(x==rgb[0])      h = 60.0*(rgb[1]-rgb[2])/(x-n);
 else if(x==rgb[1]) h = 120.0+60.0*(rgb[2]-rgb[0])/(x-n);
 else if(x==rgb[2]) h = 240.0+60.0*(rgb[0]-rgb[1])/(x-n);

 if(h<0.0) h+=360.0;
 if(h>360.0) h-=360.0;
 hsv[0]=h;
}

// ------------------------------------------------
// hsv2rgb
// hsv[] : H,S,Vの順番
// rgb[] : R,G,Bの順番
void hsv2rgb(double hsv[3], int rgb[3]) {
 double H = hsv[0];
 double S = hsv[1];
 double V = hsv[2];

 if(H>=360.0) H-=360.0;
 if(H<0.0) H+=360.0;

 S = max(0.0, min(S, 1.0));
 V = max(0.0, min(V, 1.0));
 int h = ((int)floor(H/60.0));
 double f = H/60.0 - (double)h;

 if(!(h&1)) f=1-f;

 double p = V*(1.0-S);
 double q = V*(1.0-f*S);
 double r=0.0, g=0.0, b=0.0;

 // switch文でもOK
 if(h==0)	{ r=V; g=q; b=p; }
 else if(h==1)	{ r=q; g=V; b=p; }
 else if(h==2)	{ r=p; g=V; b=q; }
 else if(h==3)	{ r=p; g=q; b=V; }
 else if(h==4)	{ r=q; g=p; b=V; }
 else if(h==5)	{ r=V; g=p; b=q; }
 rgb[0] = (int)(r*255);
 rgb[1] = (int)(g*255);
 rgb[2] = (int)(b*255);
}
```

## YCbCr表色系

JPEGの圧縮時にこの表色系を使用する．
CbとCrのチャンネルは少し削って（間引き）劣化させても，人間の目は劣化したと認識されにくいため，圧縮時に利用．
Y値はグレースケール値として使用することが多い．（安定かつ手軽で良い）
似たものに，YUV表色系やYPbPr表色系があり，厳密な切り分けがない模様．
SDTVではYCbCr表色系，HDTVではYPbPr表色系を使っているようだ．（YPbPr表色系は後述）
Cb値とCr値は負数もとるため，ディスプレイ表示させたいときは128のオフセット値を足しておく．

```cpp
// ------------------------------------------------
// rgb2ycbcr
// rgb[] : R,G,Bの順番に値が格納されている
// ycbcr[] : Y,Cb,Crの順番に値が格納される
void rgb2ycbcr(int rgb[3], double ycbcr[3], double offset = 128.0) {
 ycbcr[0] = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
 ycbcr[1] =-0.169 * rgb[0] - 0.332 * rgb[1] + 0.500 * rgb[2] + offset;
 ycbcr[2] = 0.500 * rgb[0] - 0.419 * rgb[1] - 0.081 * rgb[2] + offset;
}

// ------------------------------------------------
// ycbcr2rgb
// ycbcr[] : Y,Cb,Crの順番
// rgb[] : R,G,Bの順番
void ycbcr2rgb(double ycbcr[3], int rgb[3], double offset = 128.0) {
 double cb = ycbcr[1] - 128.0;
 double cr = ycbcr[2] - 128.0;
 int r = (int)(ycbcr[0]              + 1.402 * cr);
 int g = (int)(ycbcr[0] - 0.344 * cb - 0.714 * cr);
 int b = (int)(ycbcr[0] + 1.772 * cb);
 rgb[0] = max(0, min(r, 255));
 rgb[1] = max(0, min(g, 255));
 rgb[2] = max(0, min(b, 255));
}
```

## YPbPr表色系

こちらはHDTV向け．
変換方法はYCbCr表色系と同じで係数が違うだけ．

```cpp
// ------------------------------------------------
// rgb2ypbpr
// rgb[] : R,G,Bの順番に値が格納されている
// ypbpr[] : Y,Pb,Prの順番に値が格納される
void rgb2ypbpr(int rgb[3], double ypbpr[3], double offset = 128.0) {
 ypbpr[0] = 0.213 * rgb[0] + 0.715 * rgb[1] + 0.072 * rgb[2];
 ypbpr[1] =-0.115 * rgb[0] - 0.385 * rgb[1] + 0.500 * rgb[2] + offset;
 ypbpr[2] = 0.500 * rgb[0] - 0.454 * rgb[1] - 0.046 * rgb[2] + offset;
}

// ------------------------------------------------
// ypbpr2rgb
// ypbpr[] : Y,Pb,Prの順番
// rgb[] : R,G,Bの順番
void ypbpr2rgb(double ypbpr[3], int rgb[3], double offset = 128.0) {
 double pb = ypbpr[1] - 128.0;
 double pr = ypbpr[2] - 128.0;
 int r = (int)(ypbpr[0]              + 1.579 * pr);
 int g = (int)(ypbpr[0] - 0.187 * pb - 0.468 * pr);
 int b = (int)(ypbpr[0] + 1.856 * pb);
 rgb[0] = max(0, min(r, 255));
 rgb[1] = max(0, min(g, 255));
 rgb[2] = max(0, min(b, 255));
}
```

## その他の表色系

XYZ表色系やLa*b*表色系などもある．

# 終わりに
以前良く使っていたRGBとHSV，輝度値Yなどの変換式を記載しました．
XYZ表色系やLa*b*表色系はそのうち…