The Compression Pipeline
압축 파이프라인 — 예측·변환·양자화·엔트로피
모든 현대 비디오 코덱(H.264·H.265·AV1)이 공유하는 4단계 파이프라인. 이 그림이 머릿속에 있으면 어느 코덱의 디테일도 자기 자리를 찾는다.
Overview
비디오 코덱은 마법의 블랙박스가 아니다. 모든 현대 코덱이 같은 파이프라인을 공유한다. H.264, H.265, AV1, VP9, VVC — 디테일은 다르지만 큰 흐름은 동일하다. 이번 장은 그 큰 그림 그 자체.
그 그림: 예측(Prediction) → 변환(Transform) → 양자화(Quantization) → 엔트로피 부호화(Entropy coding). 이걸 "PTQE"로 외워두면, 4·5·7·8장에서 나올 모든 디테일이 "아, 이건 PTQE의 어느 단계에서 무슨 일을 하는 거구나"로 자기 자리를 찾는다.
- 현대 코덱의 4단계 파이프라인을 정확한 순서로 외운다
- 각 단계가 어떤 중복(공간/시간/시각)을 활용하는지 매핑한다
- 양자화가 손실의 유일한 단계임을 이해한다
- 엔트로피 코딩이 무손실 마무리임을 안다
- in-loop filter가 왜 끝이 아니라 루프 안에 있는지 설명할 수 있다
Sections
3.1 1단계 — 예측 (Prediction): 비슷한 부분 추측
압축의 첫 번째 진짜 일은 '예측'이다. 원본 블록을 그대로 저장하지 않고, 다른 데이터로부터 추측한 다음 차이만 저장한다.
두 가지 예측이 있다.
인트라 예측 (Intra): 같은 프레임 안의 옆 블록 픽셀로부터 추측. '이 블록은 위쪽 픽셀들의 평균이겠지' 또는 '왼쪽 픽셀들을 오른쪽으로 복사하면 비슷하겠지'. H.264는 9방향, H.265는 35방향, AV1은 56방향.
인터 예측 (Inter): 다른 프레임의 비슷한 블록으로부터 추측. 모션 벡터로 "이전 프레임의 (x+5, y-2) 위치에서 블록을 가져오면 거의 맞다"고 알려준다.
예측 결과: 잔차(residual) = 원본 - 예측. 예측이 잘 맞을수록 잔차가 작아진다. 다음 단계들은 이 잔차에만 적용된다.
예측의 위력: 잘 맞는 예측은 잔차를 거의 0에 가깝게 만든다. 0이 많은 데이터는 압축이 폭증한다. 코덱 성능의 절반 이상이 이 단계의 정확도에 달려 있다.
3.2 2단계 — 변환 (Transform): 주파수 도메인으로
잔차를 그대로 저장하지 않고 변환한다. 8×8 또는 16×16 블록을 DCT(Discrete Cosine Transform)로 변환하면 주파수 도메인으로 옮겨진다.
직관: 한 블록 안의 픽셀 변화를 '저주파(부드러운 그라데이션) + 고주파(날카로운 디테일)'의 조합으로 분해. DCT 결과는 한 코너에 저주파, 다른 코너에 고주파가 모인 행렬.
왜 변환을 하나? - 에너지 압축(energy compaction): 자연 이미지의 에너지가 저주파 몇 개 계수에 집중됨 → 고주파는 0에 가까움 - 양자화에 유리: 다음 단계인 양자화에서 "저주파는 정밀하게, 고주파는 거칠게" 다룰 수 있게 됨 - 시각적 중복 활용: 인간 눈이 고주파에 둔감하다는 사실을 이용 가능
DCT 자체는 가역적이다(역DCT로 완벽 복원). 손실은 다음 단계인 양자화에서만 일어난다. 자세한 수학은 챕터 4.
3.3 3단계 — 양자화 (Quantization): 손실의 유일한 자리
여기가 손실 압축의 모든 손실이 일어나는 단계. 이 한 단계만 lossy다. 나머지는 다 가역적이다.
양자화는 DCT 계수를 양자화 매트릭스로 나눈 다음 정수로 반올림. 예: 계수 47을 양자화 스텝 8로 나누면 5.875 → 6. 복원할 때 6 × 8 = 48이 되어 1만큼 손실.
핵심: 양자화 매트릭스는 균일하지 않다. 저주파 계수는 작은 스텝(정밀하게), 고주파 계수는 큰 스텝(거칠게). 이 비대칭이 "사람 눈이 못 인지하는 고주파를 더 많이 버린다"는 시각적 중복의 활용이다.
양자화 스텝의 강도를 결정하는 게 QP(Quantization Parameter) 또는 CRF(Constant Rate Factor). 둘 다 '얼마나 많이 버리느냐'를 조절. 낮을수록 정밀(고화질·고용량), 높을수록 거칠(저화질·저용량).
엔지니어가 일상에서 가장 많이 만지는 옵션이 이 QP/CRF. ffmpeg -crf 23 같은 게 바로 이 값.
3.4 4단계 — 엔트로피 부호화 (Entropy coding): 무손실 마무리
양자화 후 데이터에는 0이 잔뜩 섞여 있고, 작은 정수들이 반복된다. 이걸 무손실로 최대한 압축하는 게 엔트로피 부호화.
원리: 자주 나오는 값에 짧은 비트열, 드물게 나오는 값에 긴 비트열을 할당. Shannon이 1948년에 증명한 한계까지 압축 가능.
H.264에서 두 종류: - CAVLC (Context-Adaptive Variable Length Coding): 빠르지만 효율 낮음. Baseline profile. - CABAC (Context-Adaptive Binary Arithmetic Coding): 느리지만 10% 더 효율. Main/High profile.
H.265는 CABAC만, AV1은 SBAC(Symbol-Based Arithmetic Coding) 으로 발전.
엔트로피 부호화는 가역적이라 손실 없음. 디코더가 이 단계부터 거꾸로 복원해 나간다 — 엔트로피 디코딩 → 역양자화 → 역DCT → 예측 + 잔차 = 복원된 블록.
3.5 In-Loop Filter — 파이프라인 안에 숨은 5번째 단계
파이프라인을 4단계로 단순화했지만, 현대 코덱에는 또 하나 결정적 단계가 있다 — In-Loop Filter.
왜 필요한가? 양자화에서 손실이 생기면 블록 경계가 어색하게 보이는 블록 노이즈(blocky artifact)가 생긴다. 8×8 블록 단위로 따로 처리됐기 때문에 경계가 튀어 보임. 이걸 부드럽게 만드는 게 deblocking filter.
더 중요한 점: 이 필터가 디코더 후처리가 아니라 인코더 루프 안에 있다는 것. 왜? 다음 P/B 프레임을 예측할 때 참조 프레임으로 쓰는 게 "필터 거친 결과"여야 한다. 안 그러면 인코더(필터 거친 것 참조)와 디코더(필터 거친 것 참조) 사이에 차이가 생겨 점점 누적된다 = drift.
H.265 이후는 deblocking + SAO(Sample Adaptive Offset)까지 추가. AV1은 거기에 CDEF + Loop Restoration까지 3중 필터.
이 필터들의 누적이 최신 코덱이 같은 비트레이트에서 더 좋아 보이는 큰 이유 중 하나.
맥도날드의 햄버거가 어떻게 배달되는지 보자. (1) 주문 예측: AI가 "이 시간에 빅맥 100개 나갈 거다"로 미리 만들기 시작. (2) 냉동 변환: 패티를 압축·냉동해서 부피 줄임. (3) 양자화: 정확히 100g이 아니라 95~105g 사이로 그냥 둠 (사람이 알아챌 한계 안에서 손실 허용). (4) 포장 압축: 같은 모양 박스에 빈틈없이 쟁여서 트럭 한 대에 더 많이 실음.
비디오 코덱의 PTQE 파이프라인이 정확히 이렇다. 예측(주문 예측), 변환(냉동 압축), 양자화(허용 오차 안에서 손실), 엔트로피 부호화(빈틈없는 포장).
그리고 In-Loop Filter는 "배달 트럭이 흔들려서 햄버거가 살짝 어그러진 걸 매장에서 다시 정리하는 직원". 이 직원이 없으면 다음 배달 때 어그러진 햄버거를 기준으로 또 만들어서 점점 더 어그러진다 = drift.
8×8 블록 하나에 파이프라인 전체(예측 → DCT → 양자화 → 엔트로피)를 적용해 보자. 실제 H.264·H.265 인코더의 한 블록 처리 흐름을 단순화한 데모.
import numpy as np
from scipy.fftpack import dct, idct
# 8x8 블록 (그라데이션 + 약간 노이즈)
block = np.array([[i + j + np.random.randint(-3, 4)
for j in range(8)] for i in range(8)], dtype=float)
print('원본 블록:'); print(block.astype(int))
# 1) PREDICT: 단순 DC 예측 (블록 평균값)
predicted = np.full_like(block, block.mean())
residual = block - predicted
print('\n잔차(평균):', residual.mean())
# 2) TRANSFORM: 2D DCT
def dct2(x): return dct(dct(x.T, norm='ortho').T, norm='ortho')
def idct2(x): return idct(idct(x.T, norm='ortho').T, norm='ortho')
coeffs = dct2(residual)
print('\nDCT 계수 (저주파↖, 고주파↘):'); print(coeffs.round(1))
# 3) QUANTIZE: 양자화 매트릭스 (저주파는 정밀, 고주파는 거칠게)
Q = np.array([
[16, 11, 10, 16, 24, 40, 51, 61],
[12, 12, 14, 19, 26, 58, 60, 55],
[14, 13, 16, 24, 40, 57, 69, 56],
[14, 17, 22, 29, 51, 87, 80, 62],
[18, 22, 37, 56, 68, 109,103, 77],
[24, 35, 55, 64, 81, 104,113, 92],
[49, 64, 78, 87, 103,121,120,101],
[72, 92, 95, 98, 112,100,103, 99]]) # JPEG 표준 매트릭스
quantized = np.round(coeffs / Q).astype(int)
print('\n양자화 결과 (0이 잔뜩):'); print(quantized)
# 4) ENTROPY: 0의 비율
zeros = (quantized == 0).sum()
print(f'\n64개 계수 중 {zeros}개가 0 — 엔트로피 부호화의 자원')
# 디코더: 역과정
restored = idct2(quantized * Q) + predicted
print(f'\n복원 후 평균 오차: {np.abs(restored - block).mean():.2f}')
이 8×8 블록 하나에 PTQE 파이프라인이 다 들어 있다. (1) DC 예측으로 잔차 생성, (2) DCT로 주파수 도메인 변환, (3) 양자화 매트릭스로 나누어 정수화 — 여기서 64개 계수 중 절반 이상이 0이 된다. (4) 0이 많아진 데이터는 엔트로피 부호화로 압축 폭증. 디코더는 역양자화 → 역DCT → 잔차 + 예측 = 복원. 사람이 인지하기 어려운 손실(평균 오차 1~2)만 남는다.
✅ 시니어가 보는 것
- 4단계를 순서대로 즉답할 수 있는지
- 양자화가 손실의 유일한 자리임을 정확히 안다
- DCT가 손실이 아닌 변환임을 안다
- in-loop filter가 왜 루프 안에 있어야 하는지(drift 방지) 설명 가능
⚠️ 레드 플래그
- DCT가 압축이라고 생각
- 예측을 안 거치고 바로 변환·양자화한다고 생각
- 인코더만 알고 디코더의 역과정을 설명 못 함
🎤 예상 인터뷰 질문
- 비디오 코덱의 압축 파이프라인을 4단계로 설명해 주세요
- 양자화가 손실의 유일한 단계라는 게 무슨 뜻인가요?
- In-loop filter가 왜 디코더 후처리가 아니라 인코더 루프 안에 있어야 하나요?
Key Takeaways
PTQE 파이프라인
Predict → Transform → Quantize → Entropy. 모든 코덱이 공유.
예측이 가장 중요
잔차를 작게 만드는 게 압축의 절반.
Intra vs Inter
같은 프레임 안 vs 다른 프레임 참조.
DCT는 손실 없음
변환은 가역적. 손실은 다음 단계만.
양자화 = 유일한 손실
여기서만 사람 눈 인지 한계 활용.
엔트로피 = 무손실 마무리
Shannon 한계까지 압축. CABAC, SBAC.
In-Loop Filter
Drift 방지를 위해 루프 안에 위치.
코덱은 같은 그림
디테일이 다를 뿐 큰 흐름은 H.264~AV1 동일.