logo

이미지 변형

실습 준비

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가 적합하며, 자세한 내용은 아래 보간 절에서 다룸.

확대·축소 비율 지정

dsizeNone으로 두고 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)는 이미지의 너비와 높이의 비율임.

aspect ratio=heightwidth

너비만 정하고 원본과 같은 비율을 유지하려면 같은 배율로 높이를 계산함.

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로 나누어 되돌림.

보간

  • 크기가 달라지면 출력 픽셀의 위치가 원본 픽셀과 정확히 일치하지 않을 수 있음.
  • 보간(interpolation)은 주변 픽셀을 이용해 출력 픽셀값을 계산하는 방법.
보간 방법 특징 주요 용도
cv.INTER_NEAREST 가장 가까운 픽셀 사용, 빠르지만 계단 현상이 잘 보임 픽셀값을 그대로 유지해야 하는 마스크
cv.INTER_LINEAR 주변 2×2 픽셀 사용, OpenCV 기본값 일반적인 확대
cv.INTER_CUBIC 주변 4×4 픽셀 사용, 비교적 부드러움 고품질 확대
cv.INTER_LANCZOS4 주변 8×8 픽셀 사용, 계산량이 큼 고품질 확대
cv.INTER_AREA 픽셀 영역 관계를 반영 일반적인 축소
  • cv.INTER_AREA는 여러 원본 픽셀이 출력 픽셀 하나에 대응하는 축소 상황을 전제.
  • 확대에 사용하면 cv.INTER_NEAREST와 비슷하게 동작하여 계단 현상이 나타남. 확대에는 cv.INTER_LINEARcv.INTER_CUBIC을 사용함.

보간 방법 비교

  • 원본의 가로와 세로를 각각 1/32로 줄인 뒤 원본 크기로 확대하여 보간 방법을 비교.
  • 낮아진 해상도를 크게 확대하면 픽셀 경계와 보간 방식의 차이가 더 뚜렷하게 나타남.
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는 픽셀 경계가 뚜렷하고, 더 넓은 이웃을 사용하는 방법은 경계가 상대적으로 부드러움. 보간으로 확대해도 원본에 없던 실제 세부 정보가 복원되는 것은 아님.

왼쪽부터 최근접, 선형, 큐빅, Lanczos 보간 결과

이미지 뒤집기

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)

시계 방향으로 90도 회전한 이미지

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

퀴즈

문제 1 / 7맞음: 0힌트: 0틀림: 0채점중: 0남음: 7

cv.resize(image, dsize)dsize에 전달하는 크기 순서는?

  • (width, height)
  • (height, width)
  • (height, width, channels)
  • (channels, width)

퀴즈를 풀려면 대화형 기능을 불러와야 합니다.

Previous
이미지 비교와 차영상