본문 바로가기
데이터사이언스/컴퓨터 비전 입문

CH01 이미지는 숫자 배열이다: 픽셀·채널·비트깊이

by 미스터탁 2026. 8. 30.

CH 01 컴퓨터 비전 입문 · Part 1 이미지라는 데이터

이미지는 숫자 배열이다

컴퓨터에게 사진은 숫자가 격자로 놓인 것일 뿐이다. 이 장에서는 그 격자를 직접 열어 보고, 몇 개의 숫자가 필요한지를 실제로 잰다 — 비트깊이를 줄이면 무엇을 잃는지, 해상도를 줄이면 언제 못 알아보는지, 그리고 왜 사진은 무작위 숫자와 다른지. 마지막 질문의 답이 이 시리즈 전체를 떠받친다.

  • 01 직관
  • 02 수식 읽는 법
  • 03 손으로 풀기
  • 04 코드
  • 05 시각화

약어 및 기호 정의

픽셀
pixel (picture element) — 이미지를 이루는 가장 작은 칸 하나
채널
channel — 한 픽셀이 갖는 숫자의 개수. 흑백은 1개, 컬러는 R·G·B 3개
비트깊이
bit depth — 한 채널을 몇 비트로 저장하는가. 8비트면 28=256단계
양자화
quantization — 연속적인 밝기를 정해진 단계로 반올림하는 것
휘도
luminance — 사람 눈이 느끼는 밝기. Y = 0.2126R + 0.7152G + 0.0722B (Rec.709)
RMSE
Root Mean Square Error — 원본과의 차이를 제곱해 평균 내고 제곱근. 작을수록 원본에 가깝다
PSNR
Peak Signal-to-Noise Ratio — 20·log10(255/RMSE). dB 단위, 클수록 좋다
베이어 배열
Bayer filter — 카메라 센서가 R:G:B를 1:2:1로 까는 배치
STEP 01

직관: 사진을 열면 무엇이 있나

사진 파일을 열어 안을 들여다보면 숫자밖에 없다. 512×512 흑백 사진이라면 숫자 262,144개가 격자로 놓여 있고, 각 숫자는 0부터 255 사이다. 0이 검정, 255가 흰색이다.

컬러라면 픽셀 하나마다 숫자가 세 개다. 그래서 같은 크기의 컬러 사진은 786,432개 — 정확히 3배다.

여기서 두 가지 질문이 갈라져 나온다

① 몇 개의 숫자가 필요한가? 256단계가 꼭 필요할까? 16단계면 안 되나? 512×512가 필요할까? 이것이 압축의 질문이다.

② 이 숫자들은 서로 어떤 관계인가? 무작위로 흩어져 있나, 아니면 규칙이 있나? 이것이 컴퓨터 비전 전체의 질문이다.

두 질문의 답은 사실 같다. 이웃한 픽셀이 서로 비슷하기 때문에 압축이 되고, 같은 이유로 합성곱이 작동한다. 4절에서 이 "비슷함"을 상관계수로 잰다.

1-2. 왜 이걸 먼저 다루나

딥러닝 이야기부터 시작하면 모든 것이 마법처럼 보인다. "CNN이 특징을 자동으로 배운다"는 문장은 사진이 메모리에서 어떤 모양인지 모르는 사람에게는 아무것도 설명하지 않는다.

이 장의 목표는 하나다 — 사진에 대해 궁금한 것이 생기면 직접 재 볼 수 있게 되는 것.

STEP 02

수식 읽는 법: 세 개뿐이다

2-1. 휘도 — 흑백으로 바꾸는 식

Y = 0.2126·R + 0.7152·G + 0.0722·B

읽는 법 — "빨강의 21%, 초록의 72%, 파랑의 7%를 더해서 밝기를 만든다." 세 계수를 더하면 1.0이 된다(0.2126+0.7152+0.0722 = 1.0).

왜 초록이 압도적인가? 사람 눈의 원추세포가 초록 근처에서 가장 민감하기 때문이다. 이 식은 물리가 아니라 생물학이다 — 흑백 변환 한 줄에도 "사람"이라는 모델이 들어 있다.

2-2. 양자화 — 단계를 줄이는 식

q(x) = ⌊ x / 256 × L ⌋ , L = 2b

읽는 법b비트로 저장한다는 것은 L = 2b개의 칸에 나눠 담는다는 뜻이다. ⌊·⌋버림이다. b=8이면 256칸이라 원본 그대로지만, b=4면 16칸에 몰아넣는다.

2-3. RMSE — 얼마나 망가졌는지 재는 식

RMSE = √( (1/N) · Σi (xi − x̂i)² )

읽는 법 — 픽셀마다 원본 x와 복원값 의 차를 제곱해서(부호를 없애고 큰 오차에 벌점을 더 준다) 평균 내고, 마지막에 제곱근을 씌워 원래 단위(0~255)로 되돌린다.

RMSE 5는 "픽셀당 평균 5만큼 틀렸다"로 읽으면 된다. 다만 사람이 느끼는 화질과 정확히 같지는 않다 — 같은 RMSE라도 얼굴에 몰린 오차가 하늘에 흩어진 오차보다 훨씬 거슬린다. 24장에서 지표의 이런 함정을 따로 다룬다.

STEP 03

손으로 풀기

손풀이 A — 픽셀 하나를 흑백으로

astronaut 사진의 (100, 100) 픽셀은 R=187, G=176, B=169였다.

단순 평균 (187 + 176 + 169) / 3 = 177.3333
휘도 가중 0.2126×187 + 0.7152×176 + 0.0722×169 = 177.8332

거의 같다. 회색에 가까운 색이라 어느 쪽으로 재도 비슷하기 때문이다. 두 방식이 갈라지는 것은 순수한 색에서다.

RGB 단순 평균 휘도 가중 차이
빨강 (255,0,0) 85.00 54.21 −30.79
초록 (0,255,0) 85.00 182.38 +97.38
파랑 (0,0,255) 85.00 18.41 −66.59
노랑 (255,255,0) 170.00 236.59 +66.59

단순 평균은 빨강·초록·파랑을 전부 85로 만든다. 셋이 구분되지 않는다. 휘도로 재면 54 / 182 / 18로 갈라진다 — 초록이 파랑의 10배다.

손풀이 B — 4비트로 줄이면 픽셀 하나가 어떻게 되나

원본 값 187을 4비트(16단계)로 양자화한다.

⌊ 187 / 256 × 16 ⌋ = ⌊ 11.6875 ⌋ = 11 — 즉 11번 칸에 들어간다.
복원할 때는 칸의 가운데 값을 쓴다: (11 + 0.5) × (256/16) = 184.

오차 3. 한 칸의 폭이 16이므로 오차는 최대 ±8이고, 값이 고르게 퍼져 있다면 평균 제곱오차는 16²/12 = 21.3, 즉 RMSE ≈ 4.6이다.

4절에서 실제로 재면 4.5572가 나온다. 손계산 4.6과 맞는다 — 이론이 실제와 맞는지 확인하는 것이 이 시리즈의 방식이다.

손풀이 C — 사진 한 장은 몇 바이트인가

512 × 512 × 1채널 × 8비트 = 2,097,152비트 = 262,144바이트 = 256KB

4K 컬러 사진이라면 3840 × 2160 × 3 × 8비트 = 약 23.7MB. 그런데 실제 JPEG는 3~5MB다. 차이가 압축이고, 압축이 가능한 이유가 4절의 마지막 실험이다.

STEP 04

코드: 직접 재 본다

scikit-image에 들어 있는 camera(512×512 흑백)와 astronaut(512×512 컬러)를 쓴다. 외부 다운로드 없이 패키지에 그대로 들어 있는 진짜 사진이다.

ch01_code.pynumpy 양자화 · 다운샘플 · 상관
def quantize(a, bits):
    L = 2 ** bits
    q = np.floor(a / 256.0 * L)          # 어느 칸인가
    q = np.clip(q, 0, L - 1)
    return (q + 0.5) * (256.0 / L)      # 칸의 가운데로 복원
def down(a, k):                          # k×k 블록 평균으로 축소
    h, w = a.shape[0]//k*k, a.shape[1]//k*k
    return a[:h, :w].reshape(h//k, k, w//k, k).mean((1, 3))
# 이웃 픽셀이 얼마나 비슷한가 — 거리 d만큼 밀어서 상관계수
for d in [1, 2, 4, 8, 16, 32, 64]:
    r = np.corrcoef(cam[:, :-d].ravel(), cam[:, d:].ravel())[0, 1]
비트깊이를 낮추면 무엇을 잃는가 비트 단계 수 이론 용량(KB) PNG(KB) RMSE PSNR(dB) 8 256 256.0 136.2 0.5000 54.15 6 64 192.0 96.1 1.2303 46.33 5 32 160.0 71.7 2.3240 40.81 4 16 128.0 52.2 4.5572 34.96 3 8 96.0 35.4 9.3650 28.70 2 4 64.0 14.5 16.7940 23.63 1 2 32.0 9.9 35.0602 17.23 해상도를 줄이면 크기 픽셀 수 원본 대비 복원 RMSE 512px 262144 1.0000 0.0000 256px 65536 0.2500 9.3808 128px 16384 0.0625 14.0655 64px 4096 0.0156 19.3529 32px 1024 0.0039 24.3745 16px 256 0.0010 30.1145 이웃 픽셀은 실제로 얼마나 비슷한가 (상관계수) 1픽셀 떨어진 이웃 r = 0.9781 2픽셀 떨어진 이웃 r = 0.9554 4픽셀 떨어진 이웃 r = 0.9189 8픽셀 떨어진 이웃 r = 0.8878 16픽셀 떨어진 이웃 r = 0.8296 32픽셀 떨어진 이웃 r = 0.7431 64픽셀 떨어진 이웃 r = 0.5953 컬러 채널을 하나씩 지우면 남긴 채널 평균 밝기 그레이와의 RMSE R만 141.56 46.3693 G만 105.76 11.2858 B만 96.48 30.8264

4-1. 비트깊이 — 절반을 버려도 거의 안 보인다

8비트에서 4비트로 가도 RMSE는 4.56

저장 공간을 정확히 절반으로 줄였는데 픽셀당 평균 오차는 4.56이다. 255단계 중 4.56이니 약 1.8%다. 대부분의 사진에서 눈에 띄지 않는다.

그런데 3비트에서 9.37, 2비트에서 16.79로 급격히 커진다. 곡선이 4비트 부근에서 꺾인다.

왜 꺾이나? 양자화 오차는 칸의 폭에 비례하고, 칸의 폭은 비트가 하나 줄 때마다 두 배가 되기 때문이다. RMSE는 비트에 대해 지수적으로 증가한다 — 표에서 4.56 → 9.37 → 16.79로 대략 두 배씩 커지는 것이 그것이다.

8비트의 RMSE가 0이 아니라 0.5000인 것은 복원할 때 칸의 가운데를 쓰기 때문이다. 정수를 x.5로 되돌리니 항상 0.5씩 어긋난다. 양자화 자체의 손실은 0이다.

PNG 용량은 이론값의 절반 — 여기가 이 장의 핵심으로 가는 문이다

8비트 사진의 이론 용량은 256KB인데 실제 PNG는 136.2KB다. 거의 절반이다.

PNG는 무손실 압축이다. 정보를 하나도 버리지 않고 절반으로 줄였다. 이것이 가능하려면 원본에 중복이 있어야 한다.

어떤 중복인가? 다음 실험이 그 답이다.

4-2. 이웃 픽셀의 상관 — 이 시리즈에서 가장 중요한 숫자

바로 옆 픽셀과의 상관계수가 0.9781

1이면 완전히 같고 0이면 아무 관계가 없다. 0.9781은 "거의 같다"에 해당한다.

거리를 벌리면 0.9554 → 0.9189 → 0.8878 → … → 0.5953으로 천천히 풀린다. 64픽셀이나 떨어져도 여전히 0.6이다.

이것이 "이미지는 무작위 숫자가 아니다"의 정확한 뜻이다. 262,144개의 숫자가 자유롭게 흩어져 있는 것이 아니라, 서로를 강하게 예측한다.

여기서 세 가지가 한꺼번에 따라 나온다.

① 압축이 된다 — 옆 픽셀을 알면 이 픽셀을 거의 맞힐 수 있으니, 차이만 저장하면 된다.
② 다운샘플이 견딘다 — 512px을 256px로 줄여도 RMSE 9.38이다. 버린 픽셀은 이웃과 거의 같았다.
③ 합성곱이 말이 된다이웃끼리만 보면 되는 연산이 의미를 갖는 이유가 바로 이 상관이다. 4장과 14장이 이 위에 세워진다.

4-3. 채널 — G 하나가 흑백에 가장 가깝다

G만 남겼을 때 RMSE 11.29, R만 남겼을 때 46.37

휘도식의 계수(0.2126 / 0.7152 / 0.0722)가 그대로 나타난다. G의 몫이 72%이니 G 하나만 봐도 흑백에 가깝다.

그래서 카메라 센서는 초록 픽셀을 빨강·파랑의 두 배로 깐다(베이어 배열, R:G:B = 1:2:1). 사람 눈이 초록에서 해상도를 가장 크게 느끼기 때문이다.

여기서 "R만"의 평균 밝기가 141.56으로 가장 높은 것에 속으면 안 된다. 밝다는 것과 흑백에 가깝다는 것은 다르다. 우주인 사진에는 붉은 톤이 많아 R 채널의 평균이 높지만, 그 분포는 휘도와 어긋나 있어 RMSE가 가장 크다.

STEP 05

시각화

ch01-pixels
그림 1. 사진 → 숫자. 왼쪽은 camera 512×512, 가운데는 빨간 상자 안의 8×8을 확대한 것, 오른쪽은 같은 8×8을 숫자로 찍은 것이다. 오른쪽 격자가 컴퓨터가 보는 전부다. 눈여겨볼 것은 이웃한 숫자들이 서로 가깝다는 사실이다 — 옆칸으로 갈 때 값이 몇씩만 움직인다. 이 그림의 오른쪽이 이 장 전체의 주제다: 사진은 숫자지만 아무 숫자나가 아니다.
ch01-channels
그림 2. 컬러 사진을 채널로 분해하고 두 가지로 흑백을 만들었다. R·G·B 세 장을 보면 같은 장면인데 밝기 분포가 전혀 다르다. 오른쪽 두 장이 이 장의 측정이다 — 단순 평균휘도 가중은 이 사진에서는 비슷해 보이지만, 순수한 색에서는 크게 갈린다: 초록 (0,255,0)이 평균으로는 85, 휘도로는 182.38이다. 흑백 변환은 산술이 아니라 사람 눈의 모델이다. 그리고 G 채널 하나만으로 흑백을 만들면 RMSE가 11.29로, R만(46.37)이나 B만(30.83)보다 압도적으로 작다 — 카메라 센서가 초록 픽셀을 두 배로 까는 이유가 이 숫자다.
ch01-bits
그림 3. 위쪽 — 비트깊이를 8 → 4 → 2 → 1로 줄인 실제 사진. 4비트(16단계)까지는 거의 원본이다(RMSE 4.56). 저장 공간을 절반으로 줄였는데 눈에 띄지 않는다. 2비트부터 하늘에 계단 모양의 띠(밴딩)가 생기고 얼굴이 뭉개진다. 왼쪽 아래 — RMSE는 비트가 하나 줄 때마다 대략 두 배씩 커진다(4.56 → 9.37 → 16.79). 칸의 폭이 두 배가 되기 때문이다. 오른쪽 아래 — 이론 용량실제 PNG. 8비트에서 이론은 256KB인데 PNG는 136.2KB다. 무손실인데 절반이 된다 — 사진 안에 그만큼의 중복이 있다는 증거이고, 그 중복의 정체가 그림 5다.
ch01-res
그림 4. 해상도를 절반씩 줄였다. 512 → 256으로 픽셀을 1/4로 버렸는데 복원 RMSE는 9.38에 그친다. 사람은 사실상 구분하지 못한다. 128px(1/16)에서도 사진의 내용은 그대로 읽힌다. 32px(1/256)에 가서야 무너진다. 아래 곡선이 완만한 것에 주목하라 — 픽셀 수를 256배 줄이는 동안 RMSE는 3배도 안 커졌다. 왜 이렇게 잘 버티나? 버린 픽셀이 남긴 픽셀과 거의 같았기 때문이다. 사진의 정보는 픽셀 개수에 비례하지 않는다.
ch01-corr
그림 5. 이 장에서 가장 중요한 그림. 왼쪽 — 픽셀 값(가로)과 그 바로 옆 픽셀 값(세로)을 흩뿌린 것. 점들이 대각선에 딱 붙어 있다(r = 0.9781). 옆 픽셀은 이 픽셀과 거의 같다는 뜻이다. 64픽셀 떨어진 쌍은 훨씬 퍼져 있지만 그래도 r = 0.5953으로 여전히 상당하다. 오른쪽 — 거리에 따라 상관이 천천히 풀린다. 만약 사진이 무작위 잡음이라면 이 곡선은 d=1에서 곧장 0으로 떨어졌을 것이다. 실제로는 그렇지 않다. 이 한 장의 그림에서 이 시리즈의 절반이 나온다 — 압축이 되는 이유(그림 3), 다운샘플이 견디는 이유(그림 4), 그리고 이웃만 보는 연산인 합성곱이 왜 이미지에서 통하는지(4장·14장).

이것만 기억하자

  1. 이미지는 0~255의 숫자 격자다. 512×512 흑백은 숫자 262,144개, 컬러는 그 3배.
  2. 8비트에서 4비트로 절반을 버려도 RMSE는 4.56, 픽셀을 1/4로 줄여도 9.38이다. 사진에는 버려도 되는 것이 많다.
  3. 버려도 되는 이유는 하나다 — 이웃 픽셀의 상관계수가 0.9781이다. 사진은 무작위 숫자가 아니다. 이 사실이 압축·다운샘플·합성곱을 모두 떠받친다.

흔한 오해

  1. "픽셀이 많을수록 정보가 많다" — 픽셀 수를 256배 줄이는 동안 RMSE는 3배도 안 늘었다. 이웃이 서로 비슷하므로 픽셀 수와 정보량은 비례하지 않는다.
  2. "흑백 변환은 그냥 세 값의 평균이다" — 평균으로는 빨강·초록·파랑이 전부 85로 같아진다. 휘도로 재면 54 / 182 / 18이다. 계수 안에 사람 눈이 들어 있다.
  3. "채널이 밝을수록 흑백에 가깝다" — R 채널이 평균 141.56으로 가장 밝지만 흑백과의 RMSE는 46.37로 가장 나쁘다. G는 평균 105.76인데 RMSE 11.29다.
  4. "RMSE가 낮으면 사람 눈에도 좋다" — 대체로 맞지만 항상은 아니다. 같은 RMSE라도 얼굴에 몰린 오차가 하늘에 흩어진 오차보다 훨씬 거슬린다. 지표의 이런 함정은 24장에서 따로 다룬다.

댓글