OpenCV 비전 실무14장 · 영상 기하학 변환과 Affine, Perspective6 / 21← 이전목차다음 →MetaContents

14장. 영상 기하학 변환과 Affine, Perspective

자르기와 크기 맞추기

액자 가게에서 가로로 긴 사진을 정사각형 액자에 넣는다고 해 봅시다. 방법은 세 가지입니다. 사진을 억지로 눌러 넣거나(찌그러짐), 양옆을 잘라 내거나(가장자리를 잃음), 사진을 줄여 넣고 남는 곳에 흰 종이를 덧댑니다(여백이 생김). 딥러닝 모델은 보통 128×128 처럼 정해진 크기의 사진만 받으므로, 크기가 제각각인 사진을 이 세 가지 가운데 하나로 맞춥니다.

억지로 늘리기, 가운데 자르기, 여백 채우기의 세 가지 크기 맞추는 법과 각각 잃는 것을 견준 비교 그림

그림의 세 상자가 세 가지 방법입니다. 여기서 눌러 넣기가 비율을 무시한 cv2.resize, 양옆 잘라 내기가 가운데 자르기, 흰 종이 덧대기가 레터박스입니다. 어느 방법이든 무엇인가를 잃습니다. 모양, 가장자리, 칸 수 가운데 무엇을 잃어도 되는지에 따라 고릅니다.

가로로 긴 시계 사진을 늘리기, 가운데 자르기, 레터박스로 128×128 에 맞춘 세 결과

320×180 시계 사진을 세 방법으로 128×128 에 맞췄습니다. 늘리기는 동그란 시계가 위아래로 길쭉해졌습니다. 가운데 자르기는 시계 모양은 그대로지만 양 끝의 빨강·파랑 네모가 잘려 나갔습니다. 레터박스는 모든 것이 남았지만 위아래에 회색 띠가 생겼습니다.

자르기는 슬라이싱으로

2장에서 본 것처럼 잘라 내기는 img[y 시작:y 끝, x 시작:x 끝] 한 줄입니다. 13장에서는 관심 영역(ROI)을 골라 잘라 냅니다. 이 장에서는 크기를 맞추려고 가운데를 자릅니다.

단계320×180 사진의 계산
① 짧은 변의 길이min(320, 180) = 180
② 가로에서 버릴 칸320 − 180 = 140, 양쪽에 70칸씩
③ 자르기img[0:180, 70:250] → 180×180
④ 크기 바꾸기180×180 → 128×128, 줄이므로 INTER_AREA

레터박스 계산

단계320×180 사진의 계산
① 배율긴 변에 맞춘다: 128 ÷ 320 = 0.4
② 줄인 크기320 × 0.4 = 128, 180 × 0.4 = 72 → 128×72
③ 모자라는 칸세로 128 − 72 = 56, 위 28칸, 아래 28칸
④ 덧대기cv2.copyMakeBorder(작은 그림, 28, 28, 0, 0, ...)

cv2.copyMakeBorder 는 copy and make border(복사하며 테두리 만들기)라는 뜻입니다. 인자는 그림, 위, 아래, 왼쪽, 오른쪽 칸 수, 채우는 방식, 색 순서입니다. 6장에서 본 BORDER_REPLICATE, BORDER_REFLECT 도 여기서 쓸 수 있습니다.

짧은 예시

Python
import cv2

img = cv2.imread("ch14_photos/wide.png")
h, w = img.shape[:2]
print(w, h)                                  # 결과: 320 180

# 가운데 자르기: 짧은 변(180)에 맞춰 양쪽을 똑같이 잘라 낸다
side = min(h, w)
x0, y0 = (w - side) // 2, (h - side) // 2    # 버릴 칸의 절반: x0 = 70, y0 = 0
crop = img[y0:y0 + side, x0:x0 + side]
print(crop.shape)                            # 결과: (180, 180, 3)

# 레터박스: 긴 변을 128 에 맞추고, 모자란 세로를 위아래로 나눠 덧댄다
scale = 128 / max(h, w)
small = cv2.resize(img, (round(w * scale), round(h * scale)), interpolation=cv2.INTER_AREA)
top = (128 - small.shape[0]) // 2
bottom = 128 - small.shape[0] - top
box = cv2.copyMakeBorder(small, top, bottom, 0, 0,          # 위, 아래, 왼쪽, 오른쪽 칸 수
                         cv2.BORDER_CONSTANT,               # 정한 색 하나로 채운다
                         value=(128, 128, 128))             # 채울 색: 회색
print(small.shape, box.shape)                # 결과: (72, 128, 3) (128, 128, 3)

// 는 나눈 몫만 남기는 나눗셈입니다. 모자란 칸이 홀수면 위와 아래가 한 칸 다르므로, 아래는 128 - 높이 - top 으로 남은 칸을 모두 채웁니다.

주의할 점

  • 가운데 자르기는 물건이 가운데에 있을 때만 알맞습니다. 물건이 가장자리에 있으면 물건이 잘려 나갑니다.
  • 레터박스의 여백 색은 학습 데이터 전체에서 같게 씁니다. 사진마다 다르면 모델이 여백 색까지 배울 수 있습니다.
  • 물건 탐지(17장부터 다루는 YOLO)처럼 상자 좌표가 있는 데이터는 자르거나 여백을 붙인 만큼 상자 좌표도 함께 옮겨야 합니다.
← 이전 절14장 · 보간법다음 절 →14장 · 뒤집기와 90도 돌리기