이미지 변형
실습 준비
import cv2 as cv
from yeom import to_pil
파일 불러오기
image = cv.imread("balls.png")
이미지 크기 변경
cv.resize()는 이미지를 지정한 크기나 비율로 확대·축소함. OpenCV 배열의 shape는 (높이, 너비, 채널) 순서.
height, width = image.shape[:2]
height, width
(816, 1456)
결과 크기 지정
dsize에는 배열의 shape와 반대로 (너비, 높이)를 전달.
half_by_size = cv.resize(
image,
(width // 2, height // 2), # dsize는 (너비, 높이) 순서
interpolation=cv.INTER_AREA,
)
half_by_size.shape
(408, 728, 3)
interpolation은 크기가 달라질 때 픽셀값을 계산하는 방법.- 축소에는
cv.INTER_AREA가 적합하며, 자세한 내용은 아래 보간 절에서 다룸.
확대·축소 비율 지정
dsize를 None으로 두고 fx, fy로 가로와 세로의 배율을 지정 가능.
half_by_ratio = cv.resize(
image,
None,
fx=0.5,
fy=0.5,
interpolation=cv.INTER_AREA,
)
half_by_ratio.shape
(408, 728, 3)
-
fx=0.5,fy=0.5는 가로와 세로를 각각 절반으로 줄임. -
채널 수는 변하지 않음. 크기를 직접 지정한 방식과 결과가 같음.
-
cv.resize()는 원본 배열을 바꾸지 않고 새 배열을 반환. -
원본이 필요하면 그대로 두고 반환값을 다른 이름에 저장.
종횡비 유지
종횡비(aspect ratio)는 이미지의 너비와 높이의 비율임.
너비만 정하고 원본과 같은 비율을 유지하려면 같은 배율로 높이를 계산함.
target_width = 480
scale = target_width / width
target_height = round(height * scale) # 같은 배율을 높이에도 적용
resized = cv.resize(
image,
(target_width, target_height),
interpolation=cv.INTER_AREA,
)
resized.shape
(269, 480, 3)
축소한 이미지에서 찾은 좌표는 원본 좌표와 다름. 원본 기준 위치가 필요하면 좌표를 scale로 나누어 되돌림.
가로와 세로의 비율을 다르게 지정하면 형태가 달라짐
fx와fy를 다르게 지정하면 이미지가 한쪽 방향으로 늘어나거나 눌림.- 원래 형태를 유지하려면 두 배율을 같게 하거나 한쪽 크기에서 나머지 크기를 계산.
보간
- 크기가 달라지면 출력 픽셀의 위치가 원본 픽셀과 정확히 일치하지 않을 수 있음.
- 보간(interpolation)은 주변 픽셀을 이용해 출력 픽셀값을 계산하는 방법.
| 보간 방법 | 특징 | 주요 용도 |
|---|---|---|
cv.INTER_NEAREST | 가장 가까운 픽셀 사용, 빠르지만 계단 현상이 잘 보임 | 픽셀값을 그대로 유지해야 하는 마스크 |
cv.INTER_LINEAR | 주변 픽셀 사용, OpenCV 기본값 | 일반적인 확대 |
cv.INTER_CUBIC | 주변 픽셀 사용, 비교적 부드러움 | 고품질 확대 |
cv.INTER_LANCZOS4 | 주변 픽셀 사용, 계산량이 큼 | 고품질 확대 |
cv.INTER_AREA | 픽셀 영역 관계를 반영 | 일반적인 축소 |
cv.INTER_AREA는 여러 원본 픽셀이 출력 픽셀 하나에 대응하는 축소 상황을 전제.- 확대에 사용하면
cv.INTER_NEAREST와 비슷하게 동작하여 계단 현상이 나타남. 확대에는cv.INTER_LINEAR나cv.INTER_CUBIC을 사용함.
보간 방법 비교
- 원본의 가로와 세로를 각각 로 줄인 뒤 원본 크기로 확대하여 보간 방법을 비교.
- 낮아진 해상도를 크게 확대하면 픽셀 경계와 보간 방식의 차이가 더 뚜렷하게 나타남.
small = cv.resize(
image,
None,
fx=1/32, # 해상도를 크게 낮춰 보간 차이를 부각
fy=1/32,
interpolation=cv.INTER_AREA,
)
nearest = cv.resize(
small,
(width, height),
interpolation=cv.INTER_NEAREST,
)
linear = cv.resize(
small,
(width, height),
interpolation=cv.INTER_LINEAR,
)
cubic = cv.resize(
small,
(width, height),
interpolation=cv.INTER_CUBIC,
)
lanczos = cv.resize(
small,
(width, height),
interpolation=cv.INTER_LANCZOS4,
)
to_pil(cv.hconcat([nearest, linear, cubic, lanczos]))
INTER_NEAREST는 픽셀 경계가 뚜렷하고, 더 넓은 이웃을 사용하는 방법은 경계가 상대적으로 부드러움. 보간으로 확대해도 원본에 없던 실제 세부 정보가 복원되는 것은 아님.

이미지 뒤집기
cv.flip()은 지정한 축을 기준으로 이미지를 반전.
flipCode | 결과 |
|---|---|
0 | 상하 반전 |
| 양수 | 좌우 반전 |
| 음수 | 상하좌우 반전 |
vertical = cv.flip(image, 0)
horizontal = cv.flip(image, 1)
both = cv.flip(image, -1)
to_pil(cv.hconcat([vertical, horizontal, both]))

- 좌우 반전은 글자나 좌우가 구분되는 대상을 거울에 비친 것처럼 바꿈.
- 예제 이미지에서도 왼쪽의 빨간 공과 오른쪽의 파란 공이 자리를 바꿈.
- 데이터 증강에 사용할 때는 좌우가 바뀌어도 의미가 유지되는지 확인해야 함.
90도 단위 회전
cv.rotate()는 90도 단위로 이미지를 회전.
시계방향 90도 회전:
clockwise = cv.rotate(
image,
cv.ROTATE_90_CLOCKWISE,
)
clockwise.shape
(1456, 816, 3)
180도 회전:
rotated_180 = cv.rotate(
image,
cv.ROTATE_180,
)
rotated_180.shape
(816, 1456, 3)
반시계방향 90도 회전:
counterclockwise = cv.rotate(
image,
cv.ROTATE_90_COUNTERCLOCKWISE,
)
counterclockwise.shape
(1456, 816, 3)
90도 회전하면 너비와 높이가 서로 바뀌고, 180도 회전하면 원래 크기가 유지됨.
to_pil(clockwise)

cv.rotate()는 90도 단위 회전에 적합함. 30도나 45도처럼 임의 각도로 회전하려면 회전 행렬과 cv.warpAffine()이 필요.