3장. 디지털 영상과 색공간 변환
비트 깊이와 데이터형
시험 점수를 떠올려 봅시다. 어떤 시험은 255점 만점이고, 어떤 시험은 100점 만점입니다. 두 점수를 함께 계산하려면 "만점의 몇 %인가"처럼 비율로 바꾸는 편이 편합니다. 그림의 숫자도 0~255 의 정수로 저장하지만, 계산할 때는 255 로 나눠 0.0~1.0 의 비율로 바꿔 쓰는 일이 많습니다.
그림의 세 상자는 숫자를 담는 그릇 세 가지입니다. 보통 그림 파일은 왼쪽의 uint8 로 읽히고, 밝기를 곱하거나 나누는 계산은 가운데의 float32 로 바꿔서 합니다. 여기서 255점 만점 점수가 uint8 이고, 비율(0.0~1.0)이 float32 입니다.
세 가지 데이터형
| 데이터형 | 숫자 범위 | 한 칸의 크기 | 언제 쓰나 |
|---|---|---|---|
uint8 | 0~255 정수 | 1바이트(8비트) | 보통의 png, jpg 그림 |
float32 | 소수(그림에서는 보통 0.0~1.0) | 4바이트(32비트) | 곱하기, 나누기 같은 계산, 딥러닝 입력 |
uint16 | 0~65,535 정수 | 2바이트(16비트) | 의료·과학용 카메라, 깊이 카메라 |
16비트는 2를 16번 곱한 65,536단계입니다. 8비트(256단계)보다 밝기를 훨씬 잘게 나눠 적습니다. 16비트 png 를 그대로 읽는 읽기 모드는 2장에서 보았고, 이 과정의 실습 그림은 모두 8비트입니다.
바꾸고 되돌리기
uint8 을 float32 로 바꿀 때는 astype(np.float32) 로 그릇을 바꾼 뒤 255 로 나눕니다. 되돌릴 때는 255 를 곱하고, 0~255 밖의 값을 np.clip 으로 자른 다음 astype(np.uint8) 로 담습니다.
import numpy as np
px = np.array([0, 51, 255], dtype=np.uint8)
# 그릇을 float32 로 바꾸고 255 로 나눈다: 0~255 → 0.0~1.0
f = px.astype(np.float32) / 255
print(f) # 결과: [0. 0.2 1. ] → 51 ÷ 255 = 0.2
# 1.5배 밝게 하면 1.0 을 넘는 값이 생긴다
brighter = f * 1.5
print(brighter) # 결과: [0. 0.3 1.5]
# np.clip(배열, 가장 작은 값, 가장 큰 값): 범위 밖의 값을 끝 값으로 자른다
safe = np.clip(brighter, 0, 1)
# 255 를 곱하고 반올림(round)한 뒤 uint8 그릇에 담는다
back = (safe * 255).round().astype(np.uint8)
print(back) # 결과: [ 0 76 255] → 0.3 × 255 = 76.5 → 반올림 76마지막 줄에서 76.5 가 77 이 아니라 76 이 된 까닭은, NumPy 의 round 가 끝자리 .5 를 가까운 짝수 쪽으로 보내기 때문입니다. 1 차이라 눈으로는 구별되지 않습니다.
주의할 점
px / 255처럼 uint8 배열을 그냥 나누면 결과가 float64(8바이트)가 됩니다. 메모리를 아끼려면px.astype(np.float32) / 255처럼 먼저 float32 로 바꿉니다.- 0~255 를 벗어난 소수를
clip없이astype(np.uint8)로 담으면 엉뚱한 값이 나옵니다. 그 값은 컴퓨터마다 다를 수 있습니다. 실습 2에서 직접 봅니다. astype(np.uint8)은 소수점 아래를 버립니다(127.9 → 127). 반올림하려면.round()를 먼저 붙입니다.