05 · JPEG 심층
이 문서가 답하는 질문: JPEG은 어떻게 동작하며, DCT·양자화 테이블·점진적(progressive) 모드는 무엇이고, 주관적 품질은 어떻게 결정되는가?
한 줄 답 (Pyramid Top)
JPEG의 압축은 공간 도메인 → 주파수 도메인(DCT) → 양자화(저주파 보존, 고주파 버림) → 엔트로피 코딩의 4단 파이프라인이고, 양자화 테이블이 어느 주파수를 얼마나 버릴지를 정의한다. 이 양자화 테이블 = JPEG 품질의 본체다.
Why — 왜 JPEG이 30년이 지나도 살아남나
JPEG(1992 ISO/IEC 10918-1)이 30년이 지난 2026년에도 web image traffic의 절반 이상을 차지하는 이유:
- 무료: ISO 표준 + 무료 reference 구현(libjpeg, 1991)
- 모든 OS / 브라우저 / 카메라 / 프린터에 디코더 내장
- 자연사진에 적합한 압축 모델: 사람 눈의 고주파 둔감성 활용
- 점진적 개선 가능: mozjpeg(2014), Guetzli(2017), JPEG XL의 무손실 변환(2021)
JPEG의 핵심 통찰:
- 사람 눈은 밝기(luminance) 변화에 민감하고 색(chrominance) 변화에 둔감 → YCbCr + 4:2:0
- 사람 눈은 저주파(부드러운 영역) 에 민감하고 고주파(미세 질감) 에 둔감 → DCT + 양자화
How — JPEG 인코딩 파이프라인
1) 전체 흐름
2) Step 1 — RGB → YCbCr
Y = 0.299 R + 0.587 G + 0.114 B
Cb = -0.169 R - 0.331 G + 0.500 B + 128
Cr = 0.500 R - 0.419 G - 0.081 B + 128Y는 휘도(밝기), Cb/Cr은 색차(청색/적색 방향).
3) Step 2 — 크로마 서브샘플링
| 표기 | 의미 | 압축률 | 용도 |
|---|---|---|---|
| 4:4:4 | 풀 색해상도 | 1.00× | 무손실 / 그래픽 |
| 4:2:2 | 가로만 1/2 | 0.67× | 방송 / 마스터링 |
| 4:2:0 | 가로 세로 1/2 | 0.50× | 웹 JPEG / 비디오 표준 |
왜 4:2:0이 표준인가: 사람 눈의 색해상도가 휘도 해상도의 절반 수준 → 색 정보를 1/4로 줄여도 거의 못 알아챔.
예외: 빨간 글씨를 4:2:0으로 압축하면 가장자리에 색번짐이 생김. 그래서 디자이너용 JPEG은 4:4:4로.
4) Step 3 — DCT (Discrete Cosine Transform)
8×8 픽셀 블록을 8×8 주파수 계수로 변환.
F(u,v) = (1/4) C(u) C(v) Σ_x Σ_y f(x,y) cos(...) cos(...)좌상단(0,0) = DC = 블록의 평균값. 오른쪽/아래로 갈수록 고주파.
DC →→→ 고주파 가로
↓
↓
↓
고주파 세로이 변환은 손실 없음. 정보를 다시 정렬할 뿐.
5) Step 4 — 양자화 (이게 손실의 본질)
DCT 계수를 양자화 테이블(Q-table)로 나눈 뒤 정수로 반올림.
표준 luminance Q-table (품질 50):
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좌상단(저주파) 작은 수 = 거의 보존, 우하단(고주파) 큰 수 = 거의 0으로.
품질 N → 표준 테이블에 factor를 곱함:
factor = (N < 50) ? (5000 / N) / 100
: (200 - 2N) / 100품질 95: 테이블 거의 그대로 → 손실 적음 품질 50: 표준 테이블 → 균형 품질 10: 테이블 × 5 → 거의 다 0이 됨 → 블록 노이즈
6) Step 5 — Zigzag → Run-length → Huffman
양자화 후 8×8의 우하단은 거의 0. 지그재그 스캔으로 0이 연속되도록 펼친 뒤:
DC | (run-length, value) (run-length, value) ... EOBHuffman 또는 Arithmetic 코딩으로 엔트로피 압축.
7) Baseline vs Progressive
| 모드 | 디코딩 순서 | 용도 |
|---|---|---|
| Baseline (sequential) | 위에서 아래로 한 줄씩 | 빠른 디코딩 |
| Progressive | 저주파 → 고주파 점진적 | 느린 네트워크에서 흐릿한 이미지 먼저 |
Progressive JPEG는 같은 파일이 ~5% 더 작은 경우가 많음 (mozjpeg는 progressive를 기본).
What — 구체 사양
JPEG 파일 구조 (마커)
SOI (FFD8) — Start of Image
APP0 (FFE0) — JFIF 헤더 ("JFIF\0")
APP1 (FFE1) — EXIF (선택)
APP2 (FFE2) — ICC 프로파일 (선택)
DQT (FFDB) — Define Quantization Table
SOF0 (FFC0) — Start of Frame (baseline)
또는 SOF2 (FFC2) — progressive
DHT (FFC4) — Define Huffman Table
SOS (FFDA) — Start of Scan
[compressed data]
EOI (FFD9) — End of Image매직 넘버: FF D8 FF (3 bytes)
품질별 파일 크기 (1920×1080 풍경 사진 예)
| Quality | 파일 크기 | PSNR | SSIM | 시각 평가 |
|---|---|---|---|---|
| 95 | 580 KB | 42 dB | 0.99 | 거의 원본 |
| 85 | 240 KB | 38 dB | 0.97 | 일반인 구분 어려움 |
| 75 | 150 KB | 35 dB | 0.95 | 약간의 모기 노이즈 |
| 60 | 90 KB | 32 dB | 0.92 | 가장자리 노이즈 보임 |
| 30 | 45 KB | 27 dB | 0.85 | 블록 노이즈 명확 |
| 10 | 22 KB | 22 dB | 0.70 | 망가짐 |
실무 가이드: Q=80~85가 대부분의 웹 이미지에 sweet spot.
mozjpeg vs libjpeg 차이
mozjpeg(Mozilla, 2014)는 호환 가능한 JPEG를 만들면서도 515% 작음:
- Trellis quantization (계수를 비트 비용 고려해 최적화)
- DC 예측 개선
- Progressive 기본 활성화
- Adaptive quantization
# 일반 libjpeg
cjpeg -quality 80 input.ppm > out.jpg # 100KB
# mozjpeg
mozcjpeg -quality 80 input.ppm > out.jpg # 88KB (-12%)Guetzli (Google, 2017)
심리시각 모델(Butteraugli)을 사용해 더 작은 JPEG 생성:
- 품질 84 mozjpeg와 비슷한 품질에서 ~30% 작음
- 단점: 인코딩 1MB 이미지에 1분+ — 실시간 불가
What-if — JPEG의 함정
함정 1) JPEG → JPEG 재저장 손실
매번 저장할 때마다 양자화 손실이 누적됨. 100번 저장하면 원본 알아볼 수 없음.
대응: 편집은 PNG/TIFF에서 하고, 마지막에 한 번만 JPEG로 export.
함정 2) 텍스트/UI 스크린샷을 JPEG로 → 글자 가장자리 더러움
증상: UI 스크린샷의 텍스트 가장자리에 색 노이즈.
원인: 8×8 DCT 블록 + 4:2:0 서브샘플링이 sharp edge를 잘 처리 못함.
대응: 스크린샷은 PNG, 사진만 JPEG.
함정 3) Progressive JPEG가 일부 디코더에서 깜빡임
증상: 일부 안드로이드 앱에서 progressive 디코딩 중간 단계가 점멸.
대응: 클라이언트 호환성 우려되면 baseline 사용 (mozjpeg -baseline).
함정 4) 품질 100이 무손실이 아님
JPEG은 양자화 테이블이 1이어도 여전히 손실:
- DCT의 부동소수점 → 정수 반올림
- YCbCr 변환의 손실
- 4:2:0 서브샘플링 (4:4:4로도 여전히 정수 반올림 손실)
진짜 무손실 JPEG은 별도 모드(SOF3 lossless predictive)이지만 거의 미지원.
함정 5) 큰 사진을 progressive baseline 혼합 → 일부 도구에서 망가짐
표준은 둘을 섞을 수 없지만, 일부 라이브러리가 SOF0 + SOS multi-scan으로 만들어버림. exiftool 등으로 검증.
Insight — JPEG의 짧은 역사
“JPEG의 J는 위원회 이름이다”
Joint Photographic Experts Group — 1986년 ISO와 ITU가 합동으로 만든 위원회. Joint는 두 표준화 기구의 공동 작업을 의미. 30년이 지나 위원회는 사라졌지만 이름은 남았다.
“libjpeg의 IJG가 JPEG을 살렸다”
1991년 Independent JPEG Group이 무료 reference 구현 발표. 모든 OS가 같은 코드를 채택 → 디코더 호환성 문제가 사라짐. 그 결과 30년 뒤에도 모든 디바이스가 JPEG을 안다.
“점진적 JPEG의 르네상스 — 2014 mozjpeg”
1990년대 점진적 JPEG는 느린 모뎀을 위해 만들어졌지만, 광케이블 시대에 잊혀짐. 2014년 Mozilla가 mozjpeg에서 progressive를 기본으로 부활시킨 이유:
- 같은 화질에서 ~5% 작음
- 모바일 환경에서 체감 로딩 속도 빠름 (흐릿한 이미지가 먼저 보임) → 페이스북, 핀터레스트, 인스타그램이 일제히 채택.
“JPEG XL은 JPEG의 영혼을 계승한다”
JPEG XL의 가장 영리한 기능: 기존 JPEG 파일을 무손실로 ~20% 더 압축. 픽셀이 아니라 DCT 계수 자체를 더 좋은 엔트로피 코딩으로 재압축. 즉, 모든 기존 JPEG 자산을 변환 손실 없이 작아진 파일로 바꿀 수 있다. 그러나 Chrome이 채택을 거부하면서 보급이 정체된 비극.
한 단락 요약
JPEG =
RGB → YCbCr → 4:2:0 → 8×8 블록 → DCT → 양자화 → Huffman의 7단 파이프라인이고, 손실의 본질은 양자화 테이블이 어느 주파수를 얼마나 버릴지 결정하는 데 있다. 사람 눈의 고주파 둔감성과 색해상도 둔감성을 활용한 30년 전 설계가 여전히 합리적인 이유다. 다음 문서(06-modern-codecs-webp-avif.md)는 같은 원리를 비디오 코덱으로 확장한 WebP/AVIF를 다룬다.