色
関連
色
16進数(Hex)カラーコード
6桁の16進数で表され、各2桁がそれぞれ赤、緑、青(RGB)の強度を示します。例えば、#FFFFFFは白、#000000は黒です。
RGB
RGB(Red, Green, Blue)の3つの値で色を指定します。各値は0から255の範囲で指定されます。例えば、rgb(255, 255, 255)は白、rgb(0, 0, 0)は黒です。
RGBA
RGBに加えて透明度(Alpha)を指定します。Alphaは0(完全に透明)から1(完全に不透明)の範囲です。例えば、rgba(255, 255, 255, 0.5)は半透明の白です。
HSBとHSV
HSB(Hue, Saturation, Brightness)とHSV(Hue, Saturation, Value)は色の表現方法で、特に色調整や画像処理でよく使われます。これらはRGB(Red, Green, Blue)よりも直感的に色を操作しやすいとされています。
HSB(Hue, Saturation, Brightness)
色相 (Hue): 色相環(カラーホイール)を回るように、赤、橙、黄、緑、青、紫といった色を表します。
彩度 (Saturation): 色がどれだけ強く発色するか。低い彩度は灰色に近く、高い彩度は鮮やかな色です。
明度 (Value / Brightness): 色の明るさ。低い値は暗く、高い値は明るくなります。
HSV(Hue, Saturation, Value)
Hue(色相): 色の種類を0から360度の範囲で表します。例えば、0度は赤、120度は緑、240度は青です。
Saturation(彩度): 色の鮮やかさを0%から100%で表します。0%は灰色(無彩色)、100%は最も鮮やかな色です。
Value(明度): 色の明るさを0%から100%で表します。0%は黒、100%は最大の明るさです。
HSBとHSVはほぼ同じ概念です。「Brightness」を「Value」をと呼んでいるだけです。実質的には同じものと考えてよいです。
コード書いてると変数名h, s, bと省略してbがblueと被るのでh, s, vのが楽かもしれません。逆に変数名をちゃんと書くならvalueがなんのこっちゃ分からなくなるかもしれません。
HSL
HSL(Hue, Saturation, Lightness)は、色相(Hue)、彩度(Saturation)、輝度(Lightness)の3つの値で色を指定します。RGBやHSB/HSVに比べて、自然な色の変化をより直感的に扱うことができます。
Hue(色相): 0度から360度の範囲で色の種類を表します。0度は赤、120度は緑、240度は青です。
Saturation(彩度): 色の鮮やかさを0%から100%で表します。0%は灰色、100%は最も鮮やかな色です。
Lightness(輝度): 色の明るさを0%から100%で表します。0%は黒、100%は白です。
HSBとHSL
HSBあるいはHSV (Hue, Saturation, Value) と HSL (Hue, Saturation, Lightness) は、色を表現する方法としてよく似ていますが、彼らのサチュレーションと明度/値の計算方法が異なります。その結果、同じ色でも、HSVとHSLでの数値が異なる場合があります。
具体的には:
Hue (H):
どちらのモデルにおいても色相は同じです。通常、0°から360°の範囲で表されます。Saturation (S):
この計算方法はモデルによって異なります。HSV: $${ S_{HSV} = \frac{\text{max} - \text{min}}{\text{max}} }$$
HSL: $${ S_{HSL} = \frac{\text{max} - \text{min}}{1 - |\text{max} + \text{min} - 1|} }$$
Value/Lightness (V/L):
こちらも計算方法が異なります。HSV: $${ V = \text{max} ) (RGB値の中で最も高い値}$$
HSL: $${ L = \frac{\text{max} + \text{min}}{2} }$$
上記の式では、$${\text{max}}$$ と $${\text{min}}$$ は、与えられた色のRGB成分の最大値と最小値を示しています。
これらの違いのため、HSLとHSVで色の見え方や特性を説明する際に、いくつかの状況で異なる結果や感じ方が生じる可能性があります。
CMYK
CMYK(Cyan, Magenta, Yellow, Key/Black)は印刷で使用される色の表現方法で、シアン、マゼンタ、イエロー、ブラックの4色の組み合わせで色を表します。
Cyan(シアン)
Magenta(マゼンタ)
Yellow(イエロー)
Key/Black(黒)
各値は0%から100%の範囲で指定され、これにより特定の色が生成されます。RGBとは異なり、CMYKは減法混色であり、色を混ぜることで暗くなります。
YCbCr
YCbCrはデジタル映像処理で使用される色の表現方法です。輝度(Y)、青色差(Cb)、赤色差(Cr)の3つの値で色を表します。
Y(輝度): 明るさの成分。
Cb(青色差): 青色の成分と輝度の差。
Cr(赤色差): 赤色の成分と輝度の差。
YCbCrはRGBよりも映像の圧縮に適しており、JPEGや動画圧縮などで広く使用されています。
Lab
Lab色空間は、人間の視覚に基づいた色の表現方法で、L(明度)、a(赤から緑の範囲)、b(青から黄の範囲)の3つの成分で色を表します。
L(明度): 色の明るさを0から100の範囲で表します。0は黒、100は白です。
a: 赤から緑の色成分を示し、正の値は赤、負の値は緑を示します。
b: 青から黄の色成分を示し、正の値は黄、負の値は青を示します。
Lab色空間はRGBやCMYKに比べて、人間の視覚により近い形で色を表現でき、色の違いをより正確に測定できます。
変換
HexToRGB
function hexToRgb(hex) {
let bigint = parseInt(hex, 16);
let r = (bigint >> 16) & 255;
let g = (bigint >> 8) & 255;
let b = bigint & 255;
return [r, g, b];
}
・上の例では入力は6桁の16進数文字列が前提
・#などが存在する場合の処理もしていない。
#込みのアプローチ、ただしリターンはオブジェクト。
static HexToRgb(hex) {
if (hex.startsWith("#")) {
hex = hex.slice(1);
}
let bigint = parseInt(hex, 16);
let r = (bigint >> 16) & 255;
let g = (bigint >> 8) & 255;
let b = bigint & 255;
return {r, g, b};
}
parseIntで文字列から16進数の数値へ。
Hexは16進数のRRGGBBだから、2進数だと8bit×3の24bit。
1色につき$${2^8=256}$$であって、0-255が3色である。
ビットシフトによって16桁、および8桁ずらすことで
bigint変数は
00000000 00000000 RRRRRRRR
00000000 RRRRRRRR GGGGGGGG
RRRRRRRR GGGGGGGG BBBBBBBB
となる。ここで255は
00000000 00000000 11111111
であるから、アンドを取ることで下8桁を取得する。
また、こんなアプローチも。
let red = parseInt(hex.slice(1, 3), 16);
let green = parseInt(hex.slice(3, 5), 16);
let blue = parseInt(hex.slice(5, 7), 16);
これは#込みHexからsliceでRGBを切り出す。
RGBToHex
export function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
}
RGBToHSB
RGB(Red, Green, Blue)とHSB(Hue, Saturation, Brightness、別名HSV:Hue, Saturation, Value)は色を表現する2つの異なる方式です。RGBは光の三原色を利用して色を表現します。一方、HSBは色の視覚的側面を強調しており、色相、彩度、明度の3つの要素で色を表現します。
RGBの値は0~255の範囲で与えられると仮定します。まず、これを0~1の範囲に正規化します。
$$
R' = R/255\\
G' = G/255\\
B' = B/255
$$
2.最大値 Max と最小値 Min を求めます。
$$
Max = max(R', G', B')\\
Min = min(R', G', B')
$$
3.明度 V を計算します。
$$
V = Max
$$
4.彩度 S を計算します。
$$
S =
\begin{cases}
0 & \text{if Max} = 0 \\
\frac{\text{Max} - \text{Min}}{\text{Max}} & \text{otherwise}
\end{cases}
$$
5.色相 H を計算します。
$$
H=
\begin{cases}
60 \times \left( \frac{G' - B'}{\text{Max} - \text{Min}} + 0 \right) & \text{if } & \text{Max} = R': \\
60 \times \left( \frac{B' - R'}{\text{Max} - \text{Min}} + 2 \right) &
\text{if} & \text{Max} = G': \\
60 \times \left( \frac{R' - G'}{\text{Max} - \text{Min}} + 4 \right) & \text{if} & \text{Max} = B': \\
\end{cases}
$$
Hが負の場合、360を加えて正の値にします。
$$
\text{if } H < 0: \\
H = H + 360
$$
function rgbToHsb(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
let max = Math.max(r, g, b);
let min = Math.min(r, g, b);
let h, s, v = max;
let d = max - min;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0; // achromatic
} else {
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h * 360, s * 100, v * 100];
}
HSBToRGB
この変換は少し複雑ですが、基本的な考え方は、Hの値(色相)に基づいてRGBの各成分を決定することです。
Hを60で割った結果をHiとして、その余りをfとして定義します。
$$
Hi = \lfloor H / 60 \rfloor \text{mod} 6\\
f = (H / 60) - \lfloor H / 60 \rfloor \\
$$
色空間であるHSB(またはHSV)におけるH(Hue: 色相)は通常、0°から359°の範囲で表されます。このHの値は、色環上の位置を示しており、具体的には、赤が0°、緑が120°、青が240°となります。
これをHを60で割ったときの商と余りに分解すると、次のような意味になります:
Hi (商): 色相Hがどの部分色環に属しているかを示します。色環は6つの部分色環に分けられると考えることができ、それぞれが次のようになります:
0: 赤から黄色の間
1: 黄色から緑の間
2: 緑からシアンの間
3: シアンから青の間
4: 青から紫の間
5: 紫から赤の間
f (余り): 該当する部分色環内でのHの位置を示します。fは0から59の間の値を持ち、それにより具体的な色相の位置が示されます。
このように、Hを60で割ることで、簡単に色相の位置を特定することができます。そして、これを利用すると、HSVからRGBへの変換の際に、どの2つの主要な色(赤、緑、青)を基にして補間すればよいか、またその補間の方法を容易に計算することができます。
2.中間値を計算します。
$$
p = V * (1 - S) \\
q = V * (1 - f * S)\\
t = V * (1 - (1 - f) * S)
$$
3.Hiの値に応じてRGBの各成分を決定します。
$$
(R',G',B')=
\begin{cases}
(V, t, p) & \text{if } & \text{Hi} = 0: \\
(q, V, p) & \text{if } & \text{Hi} = 1: \\
(p, V, t) & \text{if } & \text{Hi} = 2: \\
( p, q, V) & \text{if } & \text{Hi} = 3: \\
(t, p, V) & \text{if } & \text{Hi} = 4: \\
( V, p, q) & \text{if } & \text{Hi} = 5: \\
\end{cases}
$$
最後に、RGBの値を0~255の範囲に変換します。
$$
R = int(R' * 255)\\
G = int(G' * 255)\\
B = int(B' * 255)
$$
function hsbToRgb(h, s, v) {
h /= 360;
s /= 100;
v /= 100;
let r, g, b;
let i = Math.floor(h * 6);
let f = h * 6 - i;
let p = v * (1 - s);
let q = v * (1 - f * s);
let t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
RGBToHSL
function rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
let max = Math.max(r, g, b);
let min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
let d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100)
};
}
// 使用例
let hsl = rgbToHsl(255, 0, 0);
console.log(`H: ${hsl.h}, S: ${hsl.s}%, L: ${hsl.l}%`); // H: 0, S: 100%, L: 50%
HSLToRBG
function hslToRgb(h, s, l) {
s /= 100;
l /= 100;
let c = (1 - Math.abs(2 * l - 1)) * s;
let x = c * (1 - Math.abs((h / 60) % 2 - 1));
let m = l - c / 2;
let r = 0, g = 0, b = 0;
if (0 <= h && h < 60) {
r = c;
g = x;
b = 0;
} else if (60 <= h && h < 120) {
r = x;
g = c;
b = 0;
} else if (120 <= h && h < 180) {
r = 0;
g = c;
b = x;
} else if (180 <= h && h < 240) {
r = 0;
g = x;
b = c;
} else if (240 <= h && h < 300) {
r = x;
g = 0;
b = c;
} else if (300 <= h && h < 360) {
r = c;
g = 0;
b = x;
}
r = Math.round((r + m) * 255);
g = Math.round((g + m) * 255);
b = Math.round((b + m) * 255);
return {
r: r,
g: g,
b: b
};
}
// 使用例
let rgb = hslToRgb(0, 100, 50);
console.log(`R: ${rgb.r}, G: ${rgb.g}, B: ${rgb.b}`); // R: 255, G: 0, B: 0
RGBToLab
export function rgbToLab(rgb) {
const [r, g, b] = rgb.map(v => v / 255);
let x = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
let y = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
let z = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
x *= 100;
y *= 100;
z *= 100;
x = x * 0.4124 + y * 0.3576 + z * 0.1805;
y = x * 0.2126 + y * 0.7152 + z * 0.0722;
z = x * 0.0193 + y * 0.1192 + z * 0.9505;
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
const l = (116 * y) - 16;
const a = 500 * (x - y);
const b_lab = 200 * (y - z);
return [l, a, b_lab];
}
LabToRGB
export function labToRgb(l, a, b) {
let y = (l + 16) / 116;
let x = a / 500 + y;
let z = y - b / 200;
x = 95.047 * ((x ** 3 > 0.008856) ? x ** 3 : (x - 16 / 116) / 7.787);
y = 100 * ((y ** 3 > 0.008856) ? y ** 3 : (y - 16 / 116) / 7.787);
z = 108.883 * ((z ** 3 > 0.008856) ? z ** 3 : (z - 16 / 116) / 7.787);
x = x / 100;
y = y / 100;
z = z / 100;
let r = x * 3.2406 + y * -1.5372 + z * -0.4986;
let g = x * -0.9689 + y * 1.8758 + z * 0.0415;
let b_rgb = x * 0.0557 + y * -0.2040 + z * 1.0570;
r = r > 0.0031308 ? 1.055 * (r ** (1 / 2.4)) - 0.055 : r * 12.92;
g = g > 0.0031308 ? 1.055 * (g ** (1 / 2.4)) - 0.055 : g * 12.92;
b_rgb = b_rgb > 0.0031308 ? 1.055 * (b_rgb ** (1 / 2.4)) - 0.055 : b_rgb * 12.92;
return [Math.max(0, Math.min(255, Math.round(r * 255))),
Math.max(0, Math.min(255, Math.round(g * 255))),
Math.max(0, Math.min(255, Math.round(b_rgb * 255)))];
}
RGBToYCbCr
export function rgbToYCbCr(r, g, b) {
const y = 0.299 * r + 0.587 * g + 0.114 * b;
const cb = 128 - 0.168736 * r - 0.331264 * g + 0.5 * b;
const cr = 128 + 0.5 * r - 0.418688 * g - 0.081312 * b;
return [y, cb, cr];
}
YCbCrToRGB
export function ycbcrToRgb(y, cb, cr) {
const r = y + 1.402 * (cr - 128);
const g = y - 0.344136 * (cb - 128) - 0.714136 * (cr - 128);
const b = y + 1.772 * (cb - 128);
return [Math.max(0, Math.min(255, Math.round(r))),
Math.max(0, Math.min(255, Math.round(g))),
Math.max(0, Math.min(255, Math.round(b)))];
}
距離
RGB
function rgbDistance(color1, color2) {
const dr = color1.r - color2.r;
const dg = color1.g - color2.g;
const db = color1.b - color2.b;
return Math.sqrt(dr*dr + dg*dg + db*db);
}
// 使用例
const color1 = {r: 255, g: 0, b: 0};
const color2 = {r: 0, g: 255, b: 0};
console.log(rgbDistance(color1, color2));
HSV
function hsvDistance(hsv1, hsv2) {
const dh = Math.min(Math.abs(hsv1.h - hsv2.h), 360 - Math.abs(hsv1.h - hsv2.h)) / 180.0;
const ds = Math.abs(hsv1.s - hsv2.s);
const dv = Math.abs(hsv1.v - hsv2.v) / 100.0;
return Math.sqrt(dh*dh + ds*ds + dv*dv);
}
// 使用例
const hsv1 = {h: 0, s: 100, v: 100};
const hsv2 = {h: 120, s: 100, v: 100};
console.log(hsvDistance(hsv1, hsv2));
Lab
function labDistance(lab1, lab2) {
const dl = lab1.l - lab2.l;
const da = lab1.a - lab2.a;
const db = lab1.b - lab2.b;
return Math.sqrt(dl*dl + da*da + db*db);
}
// 使用例
const lab1 = {l: 50, a: 50, b: 50};
const lab2 = {l: 60, a: 60, b: 60};
console.log(labDistance(lab1, lab2));
CMYK
function cmykDistance(cmyk1, cmyk2) {
const dc = cmyk1.c - cmyk2.c;
const dm = cmyk1.m - cmyk2.m;
const dy = cmyk1.y - cmyk2.y;
const dk = cmyk1.k - cmyk2.k;
return Math.sqrt(dc*dc + dm*dm + dy*dy + dk*dk);
}
// 使用例
const cmyk1 = {c: 0, m: 100, y: 100, k: 0};
const cmyk2 = {c: 100, m: 0, y: 100, k: 0};
console.log(cmykDistance(cmyk1, cmyk2));
色の生成とソート
ランダムに作成された色オブジェクトの配列を、指定された要素数分作成する関数を作ってください。
また、色配列をソートする関数を作ってください。ソートの基準はrgbaの値を並べた12桁の整数の大小です。昇順と降順にできるようにしてください。
ランダムな色オブジェクトの配列を生成する関数
function generateRandomColors(count) {
const colors = [];
for (let i = 0; i < count; i++) {
const color = {
a: Math.floor(Math.random() * 256),
r: Math.floor(Math.random() * 256),
g: Math.floor(Math.random() * 256),
b: Math.floor(Math.random() * 256)
};
colors.push(color);
}
return colors;
}
色配列をソートする関数
この関数は、色配列を引数として受け取り、ソート順(昇順または降順)を指定するフラグも受け取ります。RGBAの各値を結合して生成される12桁の整数を基準にソートを行います。
function sortColors(colors, ascending = true) {
return colors.sort((a, b) => {
const aValue = parseInt(`${String(a.r).padStart(3, '0')}${String(a.g).padStart(3, '0')}${String(a.b).padStart(3, '0')}${String(a.a).padStart(3, '0')}`);
const bValue = parseInt(`${String(b.r).padStart(3, '0')}${String(b.g).padStart(3, '0')}${String(b.b).padStart(3, '0')}${String(b.a).padStart(3, '0')}`);
if (ascending) {
return aValue - bValue;
} else {
return bValue - aValue;
}
});
}
使用例
以下のコードは、これらの関数の使用例を示しています。
// ランダムな色オブジェクトの配列を10個生成
const randomColors = generateRandomColors(10);
console.log("Generated Colors:", randomColors);
// 色配列を昇順でソート
const sortedColorsAscending = sortColors(randomColors, true);
console.log("Sorted Colors (Ascending):", sortedColorsAscending);
// 色配列を降順でソート
const sortedColorsDescending = sortColors(randomColors, false);
console.log("Sorted Colors (Descending):", sortedColorsDescending);
これらの関数を使用して、ランダムに生成された色オブジェクトの配列を簡単に作成し、指定した基準に基づいてソートすることができます。ソート関数は、ビジュアルプログラミングやデータの視覚化で特に有用です。
この記事が気に入ったらサポートをしてみませんか?