에듀테크 프로젝트 대시보드
색 공간 (RGB · HSL · HSV)
rgb(255, 138, 0) 같은 색 하나가 정해진다.
하지만 사람이 "조금 더 밝게", "채도를 낮춰서"라고 생각할 때는 RGB가 불편하다.
그래서 같은 색을 색상(Hue)·채도(Saturation)·밝기라는 축으로 다시 적은 것이 HSL / HSV다.
True Color 방식은 R·G·B에 각각 8비트를 쓴다. 채널당 0~255의 256단계,
전체는 256 × 256 × 256 = 16,777,216가지 색이다.
웹에서 #RRGGBB 로 쓰는 6자리 16진수가 바로 이 3바이트다.
# #FF8A00 을 비트로 풀어 보면 R = 255 = 1111 1111 G = 138 = 1000 1010 B = 0 = 0000 0000 32비트 정수 한 개로 뭉치면: (R << 16) | (G << 8) | B = 0xFF8A00 # 알파(투명도)까지 넣으면 RGBA = 32비트 0xFF8A00FF ← 마지막 FF 가 불투명
HDR·전문가용 화면은 채널당 10비트(30비트, 약 10억 색) 나 부동소수점을 쓰기도 하고, 오래된 시스템은 16비트(5-6-5) 나 팔레트 기반 8비트(256색) 를 썼다. 비트를 줄이면 디더링으로 부족한 색을 눈속임한다.
R·G·B를 0~1로 정규화한 뒤 최댓값 M, 최솟값 m, 차이 C = M − m(크로마)를 구한다.
| 값 | 의미 | 공식 |
|---|---|---|
| L (밝기) | 가장 밝은 채널과 가장 어두운 채널의 평균 | (M + m) / 2 |
| S (채도) | 회색에서 얼마나 벗어났나 | C / (1 − |2L − 1|) |
| H (색상) | 어떤 채널이 최대냐로 60° 구간을 정함 | 아래 코드 |
function rgbToHsl(r, g, b) { r /= 255; g /= 255; b /= 255; const M = Math.max(r, g, b), m = Math.min(r, g, b); const C = M - m, L = (M + m) / 2; let h = 0; if (C !== 0) { if (M === r) h = ((g - b) / C) % 6; // 빨강 근처 else if (M === g) h = (b - r) / C + 2; // 초록 근처 else h = (r - g) / C + 4; // 파랑 근처 h *= 60; if (h < 0) h += 360; } const S = C === 0 ? 0 : C / (1 - Math.abs(2 * L - 1)); return [h, S * 100, L * 100]; }
| 작업 | 편한 색 공간 | 이유 |
|---|---|---|
| 모니터·카메라 픽셀 저장 | RGB | 하드웨어가 빛 3개를 직접 켠다 |
| 인쇄 | CMYK | 잉크는 빛을 빼는 감산 혼합 |
| "색조는 유지, 밝기만 +10" | HSL / HSV | L 또는 V 하나만 건드리면 됨 |
| 특정 색 물체 추적(영상처리) | HSV | 그림자가 져도 H는 잘 안 변함 |
| 사람 눈에 균일한 색 차이 | CIELAB | 거리 = 실제로 느끼는 차이 |
| 영상 압축·방송 | YCbCr | 밝기(Y)와 색(CbCr) 분리 후 색 정보만 줄임 |
화면이 계산하는 "밝기"에도 종류가 있다. 눈은 초록에 가장 민감해서
Luma = 0.299·R + 0.587·G + 0.114·B 처럼 가중 평균을 쓴다.
단순 평균 (R+G+B)/3 으로 흑백 변환하면 초록이 너무 어둡게 나온다.