2장. 이미지 입출력과 영상 시각화
그림 파일의 형식과 압축
여행 가방을 싸는 세 사람이 있습니다. 첫째는 옷을 반듯하게 개어 빈틈없이 넣습니다. 풀어 보면 옷이 그대로입니다. 둘째는 가방을 작게 하려고 자주 안 입는 옷을 몇 벌 빼고, 나머지를 꾹꾹 눌러 담습니다. 가방은 가볍지만 뺀 옷은 돌아오지 않습니다. 셋째는 개지도 않고 옷을 그냥 쌓아 넣습니다. 가방이 가장 큽니다. 그림 파일의 세 형식이 이 세 사람과 같습니다.
그림의 세 상자가 왼쪽부터 PNG, JPG, BMP 입니다. 여기서 옷을 반듯하게 개는 사람이 PNG(무손실 압축), 옷을 빼고 눌러 담는 사람이 JPG(손실 압축), 그냥 쌓는 사람이 BMP(압축 없음)입니다. 비유와 다른 점도 있습니다. JPG 가 버리는 것은 옷 한 벌처럼 통째가 아니라, 칸마다 숫자를 조금씩 뭉뚱그리는 것입니다.
두 번째 그림은 같은 그림을 세 가지로 저장해 글자 부분을 크게 키운 것입니다. 왼쪽 PNG 는 글자 둘레가 깨끗합니다. JPG 는 품질을 낮출수록 글자 둘레에 얼룩과 네모난 덩어리가 생깁니다. JPG 는 그림을 8×8 칸씩 묶어 그 안의 잔무늬를 덜어 내기 때문입니다.
세 형식 견주기
| 견줄 것 | PNG | JPG (JPEG) | BMP |
|---|---|---|---|
| 압축 | 무손실 | 손실 | 없음 |
| 다시 읽은 숫자 | 원본과 똑같다 | 원본과 조금 다르다 | 원본과 똑같다 |
| 파일 크기 | 중간 (단순한 그림은 아주 작다) | 작다 (품질로 조절) | 가장 크다 |
| 투명도(알파) | 담을 수 있다 | 담지 못한다 | 보통 쓰지 않는다 |
| 알맞은 그림 | 도형, 글자, 화면 캡처, 정답 표시 그림 | 사진, 색이 부드럽게 이어지는 그림 | 아주 오래된 프로그램과 주고받을 때 |
메모리 안에서 크기 견주기
cv2.imencode 는 그림을 파일 꼴의 바이트로 바꿔 주기만 하고 디스크에는 쓰지 않습니다. 돌려받은 바이트의 길이가 곧 그 형식으로 저장했을 때의 파일 크기입니다.
import cv2
img = cv2.imread("ch02_market.png") # 320×240 컬러 그림
print("메모리 속 배열:", img.nbytes, "바이트")
# 확장자만 바꿔 세 형식으로 인코딩한다. ok 는 성공 여부, buf 는 바이트 묶음
for ext in (".bmp", ".png", ".jpg"):
ok, buf = cv2.imencode(ext, img)
print(f"{ext:5} {len(buf):>7,}바이트")BMP 는 배열 크기(230,400바이트)에 머리 정보 54바이트를 붙인 만큼이 되고, PNG 와 JPG 는 그보다 열 배 넘게 작습니다. 이 그림은 넓은 면을 한 가지 색으로 칠한 단순한 그림이라, 무손실인 PNG 도 JPG(옵션을 주지 않으면 품질 95)와 비슷한 크기까지 줄어듭니다.
주의할 점
- JPG 를 열었다가 다시 JPG 로 저장하면 그때마다 조금씩 더 버립니다. 손질하는 중간 결과는 PNG 로 두고, 마지막에 한 번만 JPG 로 저장합니다.
- 확장자를
.png로 바꿔 이름만 고친다고 형식이 바뀌지 않습니다. 형식은 저장할 때(imwrite) 정해집니다.