📁 File3. 비디오04 · YUV 색공간 · 크로마 서브샘플링

04 · YUV 색공간 · 크로마 서브샘플링

이 문서가 답하는 질문: 왜 비디오는 RGB가 아니라 YUV로 압축되는가? 4:2:0이란 무엇인가? 선행: 02-media-image (sRGB·색공간 기초)


한 줄 답

사람의 눈은 색차(chroma)보다 밝기(luma)에 8배 민감하다. YUV는 RGB를 “밝기 1축 + 색 2축”으로 분리해서, 색만 해상도를 줄여(서브샘플링) 데이터를 절반 이하로 압축하면서도 인지 품질은 거의 유지한다.


Why — 왜 YUV로 분리하는가

RGB는 디스플레이에 쏘는 형식 — 빨강·녹색·파랑 3채널이 모두 같은 해상도. 그런데 사람의 시각 시스템은:

  • 간상세포(rod): 1억 2천만 개, 밝기에 매우 민감
  • 원추세포(cone): 6백만 개, 색에 민감

→ 비율 약 20:1. 그래서 밝기 정보를 우선 보존하고 색 정보는 뭉개도 우리는 잘 모른다.

YUV(엄밀히는 Y’CbCr)는 이 인지 비대칭을 활용한다:

Y'  = 밝기(luma) — 흑백 TV가 그대로 받아 표시 가능
Cb = 파랑 - 밝기 (color difference)
Cr = 빨강 - 밝기 (color difference)

Y’ 하나만 봐도 흑백 영상으로 보인다 — 이게 1953년 NTSC 컬러TV가 흑백TV와 호환되도록 한 핵심 트릭.


How — 어떻게 동작하는가

RGB → YUV 변환 매트릭스 (BT.709)

Y'  = 0.2126·R + 0.7152·G + 0.0722·B
Cb = (B - Y') / 1.8556      → 정규화 후 [-0.5, 0.5]
Cr = (R - Y') / 1.5748      → 정규화 후 [-0.5, 0.5]

→ 녹색 가중치(0.7152)가 가장 큼 — 사람의 눈이 녹색에 가장 민감.

Color Matrix 표준들

표준공식용도
BT.601Y’=0.299R + 0.587G + 0.114BSD (NTSC/PAL DVD)
BT.709Y’=0.2126R + 0.7152G + 0.0722BHD (1080p)
BT.2020Y’=0.2627R + 0.6780G + 0.0593BUHD/4K/8K, HDR
BT.2100BT.2020 + PQ/HLG transferHDR (05-bit-depth-and-hdr.md)
JPEG (BT.601 full)BT.601 + full range정지 이미지

BT.601 매트릭스로 인코딩한 영상을 BT.709로 디코딩하면 색이 어긋난다 — 가장 흔한 아티팩트:

피부톤이 누렇게 보이거나 / 하늘이 청록빛으로 빠짐.

ffmpeg는 메타데이터로 어느 매트릭스인지를 컨테이너에 박는다 (colormatrix, colorspace 태그).

# 매트릭스 확인
ffprobe -select_streams v:0 -show_entries stream=color_space,color_primaries,color_transfer in.mp4
 
# 강제 명시
ffmpeg -i in.mov -c:v libx264 \
  -colorspace bt709 -color_primaries bt709 -color_trc bt709 \
  -movflags +faststart out.mp4

크로마 서브샘플링 (J:a:b 표기)

J:a:b 는 4픽셀 가로 × 2픽셀 세로 영역 안에서 색 샘플 수를 표기한다.

표기의미데이터 비율 (vs 4:4:4)
4:4:4모든 픽셀에 Y, Cb, Cr 1개씩100% (full chroma)
4:2:2가로 2픽셀당 색 1쌍67%
4:2:02×2 영역당 색 1쌍50%
4:1:1가로 4픽셀당 색 1쌍50%
4:0:0색 없음 (Y만)33% (흑백)

4:2:0의 시각화:

Y'  Y'  Y'  Y'           ← 4 luma 샘플 (가로)
Y'  Y'  Y'  Y'           ← 4 luma 샘플 (다음 줄)

Cb           Cb          ← 2×2 블록당 Cb 1개
Cr           Cr          ← 2×2 블록당 Cr 1개

2×2 영역의 4픽셀이 같은 색 샘플 1쌍을 공유.

서브샘플사용처
4:4:4그래픽·VFX·아카이브·카메라 raw·ProRes 4444
4:2:2방송 마스터·ProRes 422·DNxHD·Canon Cinema RAW
4:2:0모든 H.264/HEVC/AV1 소비자 인코딩 — Web/Streaming/Blu-ray

What — 사양 표와 명령어

Pixel Format 표 (ffmpeg 기준)

pix_fmt비트심도서브샘플비고
yuv420p84:2:0H.264 표준
yuv422p84:2:2방송용
yuv444p84:4:4그래픽용
yuv420p10le104:2:0HEVC Main 10, AV1
yuv422p10le104:2:2ProRes 422 HQ
yuv444p10le104:4:4ProRes 4444
nv1284:2:0 (UV interleaved)GPU 친화
p010le10 (LSB padded)4:2:0HW HEVC
rgb2484:4:4 (RGB)디스플레이용
# 입력 pixel format 확인
ffprobe -select_streams v:0 -show_entries stream=pix_fmt in.mp4
# → pix_fmt=yuv420p
 
# 4:4:4 마스터 → 4:2:0 배포
ffmpeg -i master_444.mov -pix_fmt yuv420p -c:v libx264 deliver.mp4
 
# 10-bit 4:2:0 (HDR/HEVC Main 10)
ffmpeg -i raw.mov -pix_fmt yuv420p10le -c:v libx265 -tag:v hvc1 hevc10.mp4

Range — Limited (TV) vs Full (PC)

RangeY 범위Cb/Cr 범위사용처
Limited (TV/Studio)16-23516-240비디오 표준 (BT.601/709/2020)
Full (PC)0-2550-255JPEG·웹 캡처·게임

Limited 자산을 Full로 디코딩하면: 검은색이 더 검고, 흰색이 빠져나가 콘트라스트가 부풀려진 출력. Full 자산을 Limited로 디코딩하면: 회색빛 washed out — Mac QuickTime이 PC 캡처 영상을 보여줄 때 흔함.

# 명시적 변환 (limited → full)
ffmpeg -i tv.mp4 -vf "scale=in_range=tv:out_range=pc" -c:v libx264 pc.mp4
 
# h264 인코딩 시 range 박기
ffmpeg -i in.mov -c:v libx264 -color_range tv out.mp4

정확한 변환 명령어 (BT.709, full chain)

ffmpeg -i master.mov \
  -c:v libx264 -preset slow -crf 18 \
  -pix_fmt yuv420p \
  -colorspace bt709 -color_primaries bt709 -color_trc bt709 \
  -color_range tv \
  -movflags +faststart \
  out_h264.mp4

What-if — 잘못 다루면 어떻게 깨지는가

❌ 함정 1 — 4:2:0으로 텍스트/UI를 캡처하면 가장자리가 번진다

게임 UI·자막·로고는 고대비 색 경계가 많다 — 4:2:0은 2×2 블록당 색 1개라서 빨강 글자가 어두운 배경 위에서 핑크 흐림처럼 번져 보인다.

예시:

  • 흰 바탕에 빨간 1픽셀 글씨 → 4:2:0 후 글씨가 분홍 흐림.
  • 검은 바탕에 시안 글씨 → 청록 블리딩.

해결책:

  1. 4:2:2 또는 4:4:4 인코딩 (대역폭 높지만 정확) — Apple ProRes 4444가 마스터링 표준.
  2. 고해상도 + 4:2:0 — 1080p 4:2:0 글자가 흐리면 1440p/4K 4:2:0으로 올리면 가장자리가 살아난다.
  3. 별도 그래픽 레이어 — 인코딩 후 클라이언트에서 자막·UI를 따로 그림 (HLS WebVTT 자막).

그래서 Twitch는 1080p 4:2:0이다 — 게이밍 콘솔 H.264 HW 인코더 대부분이 4:2:0만 가속하기 때문. 결과: 채팅·UI 글자가 또렷하지 않다. 1440p/60 partner 등급에서야 텍스트가 살아난다.

❌ 함정 2 — Color Matrix 메타 누락

# 메타 없이 인코딩
ffmpeg -i in.mov -c:v libx264 out.mp4
# → ffprobe에서 color_space=unknown

플레이어는 휴리스틱으로 추측 — 1080p 이상이면 BT.709, 그 미만이면 BT.601 가정. 자산이 SD인데 BT.709로 마스터링됐으면 색이 어긋난다.

해결: 인코딩 시 항상 -colorspace -color_primaries -color_trc 3개를 명시.

❌ 함정 3 — Range 라벨링 실수

iPhone QuickTime은 자주 Full Range (PC) 로 캡처하면서 메타에 라벨을 안 박는다. 편집 NLE에서 limited로 가정 → 색이 빠짐. 반대로 PC 캡처 자산을 limited로 인코딩하면 그림자가 까맣게 깔린다.

# 진단
ffprobe -select_streams v:0 -show_entries stream=color_range in.mp4
 
# range 강제 명시 (limited)
ffmpeg -i in.mov -c:v libx264 -color_range tv -movflags +write_colr out.mp4
 
# 변환
ffmpeg -i full.mp4 -vf "scale=in_range=pc:out_range=tv" -c:v libx264 limited.mp4

❌ 함정 4 — 4:4:4 마스터를 4:2:0 인코딩 후 다시 4:4:4로 복원

크로마 서브샘플링은 비가역. 한 번 잃은 색 디테일은 슈퍼해상도 같은 ML 보강 외에는 못 살린다. 마스터는 언제나 4:4:4 또는 4:2:2 로 보존, 배포만 4:2:0.

❌ 함정 5 — Limited 자산을 sRGB로 가정해 색보정

10bit BT.2020 limited 자산을 sRGB 8bit로 작업하면 첫 단계에서 색·계조가 모두 깎인다. DaVinci Resolve의 Color Space Transform 노드가 표준 — 항상 source → working → display 3-stage.


Insight — 흥미로운 이야기

“4:2:0은 PAL TV의 발명이다”

1980년대 D1 디지털 방송 표준에서 처음 4:2:0이 정의됐다 (정확히는 4:1:1이 NTSC, 4:2:0이 PAL). 그 시대 ASIC가 더 이상 색 데이터를 처리할 여유가 없었다 — 대역폭의 청구서를 색에 떠넘긴 것. 40년 뒤 H.264·HEVC·AV1까지 4:2:0이 소비자 표준의 기본값이고, 모바일 GPU의 비디오 디코더는 4:2:0만 풀가속한다 — 다른 서브샘플링은 SW fallback이 흔하다.

“Twitch가 1440p로 가는 이유는 글자다”

Twitch는 2022년부터 partner에 1440p/60 옵션을 열었다. 이유는 4:2:0이 텍스트에 약한 걸 알기 때문 — 1080p에서 채팅·UI가 흐려지는 걸 해상도 증가로 우회. H.264 4:4:4 인코딩이 정답이지만 GPU 가속이 안 되니 해상도를 1.78배 키워 4:2:0의 색 해상도를 살린 것.

“YouTube의 미스터리 — 같은 영상인데 색이 다르다”

같은 마스터를 YouTube에 올리면 VP9 변환본은 채도가 빠지고 AV1 변환본은 살아있다는 보고가 흔하다. 원인은 YouTube의 BT.601 → BT.709 매트릭스 변환 단계에서 옛 트랜스코더의 휴리스틱 미스 — 코덱이 아니라 매트릭스의 문제.

“ProRes 4444 — 마지막 4가 알파”

Apple ProRes 4444는 4:4:4:4 — Y, Cb, Cr, Alpha 각각 풀 해상도. Hollywood VFX 합성의 표준 마스터링 포맷. 12-bit 4:4:4:4 = 1080p에서 비트레이트 약 330 Mbps. 합성 단계가 끝나면 4:2:0 8bit H.264로 떨어뜨려 배포 — 변환의 끝.


요약 + Mermaid

YUV는 사람 시각의 “밝기 우선” 비대칭을 활용한 압축의 출발점이고, 4:2:0은 그 비대칭을 가장 공격적으로 쓰는 서브샘플링이다. 모든 소비자 H.264/HEVC/AV1 인코딩의 기본값. 텍스트·UI·고대비 그래픽에는 약하다. 마스터는 4:4:4 / 배포는 4:2:0의 일방향 변환. 매트릭스(BT.601/709/2020)와 Range(limited/full)는 메타데이터로 박지 않으면 색이 어긋난다.