영상처리

Part 11 : 기하학적 변환 (Geometric Transformation)

dainj 2026. 2. 27. 12:15

 

기하학적 변환은 영상의 픽셀 밝기 값을 바꾸는 것이 아니라, 픽셀의 물리적인 위치(좌표)를 수학적인 행렬 연산을 통해 이동시키는 작업이다.

의료 영상에서는 환자의 자세가 틀어져 비스듬하게 촬영된 방사선 영상을 정방향으로 보정하거나, 분석이 필요한 특정 장기 영역(ROI)을 정밀하게 확대 및 축소할 때 필수적으로 사용되는 전처리 기법이다.

1. 크기 변환(Scaling)과 보간법(Interpolation)

영상의 해상도를 높여서 확대하거나 반대로 축소할 때 가장 큰 문제는 '원래 없던 빈 공간의 픽셀 값을 어떻게 채워 넣을 것인가'이다. 이 빈자리를 주변 픽셀들의 정보를 바탕으로 합리적으로 추정하여 채워 넣는 수학적 기법을 보간법(Interpolation)이라 한다.

어떤 보간법을 선택하느냐에 따라 의료 영상의 미세한 디테일(미세 혈관, 작은 종양 등)이 유지될 수도 있고 뭉개질 수도 있다.

  • cv2.INTER_NEAREST (최근방 이웃 보간법): 새로 만들어진 픽셀 위치에서 가장 가까운 원본 픽셀의 값을 그대로 가져와서 복사한다. 연산 속도는 가장 빠르지만, 영상의 경계선이 톱니바퀴처럼 계단 현상(Aliasing)이 생기며 거칠어지는 단점이 있다.
  • cv2.INTER_LINEAR (양선형 보간법): 새로 채워야 할 픽셀을 기준으로 가장 인접한 4개의 픽셀 값을 찾고, 거리의 비율에 따라 가중 평균을 내어 값을 결정한다. 속도와 영상 품질의 밸런스가 가장 좋아 OpenCV의 기본값으로 널리 쓰인다.
  • cv2.INTER_CUBIC (3차 회선 보간법): 인접한 4개가 아니라 16개의 주변 픽셀을 넓게 참조하여 3차 함수 곡선으로 값을 추정한다. 연산량은 많아 속도는 느리지만, 의료 영상처럼 해상도와 디테일이 생명인 분야에서 훨씬 부드럽고 자연스러운 고품질 확대 영상을 얻을 수 있다.
  • cv2.INTER_AREA (영역 보간법): 주로 영상을 축소할 때 사용한다. 축소 시 픽셀 정보들이 겹쳐서 생기는 물결무늬(무아레 현상)를 방지하고 디테일이 깨지는 것을 막아준다.

 


2. 어파인 변환 (Affine Transformation)

어파인 변환은 영상의 이동(Translation), 확대/축소(Scaling), 회전(Rotation), 그리고 한쪽으로 미는 듯한 기울기(Shearing) 변환을 모두 포함하는 2차원 기하학적 변환이다.

  • 핵심 원리: 어파인 변환의 가장 중요한 기하학적 특징은 '변환 전에 서로 평행했던 선들은 변환 후에도 반드시 평행을 유지한다'는 것이다. 정사각형을 마름모나 평행사변형으로 바꿀 수는 있지만, 평행하지 않은 일반 사다리꼴로 찌그러뜨릴 수는 없다.
  • 적용 방법: 이 변환 행렬(2 X 3 행렬)을 수학적으로 계산해 내기 위해서는, 변환 전 원본 영상에서의 좌표점 3개와 이 점들이 이동해서 가야 할 변환 후의 매칭 좌표점 3개가 필요하다.

[실습 코드: 어파인 변환 구현]

Python
img = cv2.imread("chessboard.png")
rows, cols, ch = img.shape

# 1. 변환 전 원본 기준 좌표 3개 (좌상단, 우상단, 좌하단 부근)
pts1 = np.float32([[40,15],[200,15],[40,35]])
# 2. 변환 후 매핑될 새로운 좌표 3개 (x, y 값이 약간씩 뒤틀림)
pts2 = np.float32([[40,25],[210,15],[50,35]])

# 시각적 확인을 위해 원본 이미지의 기준 좌표에 색상 점 그리기
cv2.circle(img, (40,15), 10, (255,0,0),-1)  # 파란점
cv2.circle(img, (200,15), 10, (0,255,0),-1) # 초록점
cv2.circle(img, (40,35), 10, (0,0,255),-1)  # 빨간점

# 3. 3개의 매칭 점을 이용해 2x3 어파인 변환 행렬(M) 계산
M = cv2.getAffineTransform(pts1,pts2)
# 4. 행렬을 적용하여 실제 영상을 변형
dst = cv2.warpAffine(img, M, (cols, rows))

plt.subplot(121),plt.imshow(img),plt.title('image')
plt.subplot(122),plt.imshow(dst),plt.title('Affine')
plt.show()


3. 퍼스펙티브 변환 (Perspective Transformation, 원근 변환)

퍼스펙티브 변환은 어파인 변환보다 한 차원 더 높은 자유도를 가진 변환이다. 3차원 공간에서 대상을 비스듬히 바라볼 때 발생하는 원근감(가까운 것은 크게 보이고 먼 것은 작게 보이는 왜곡 현상)을 2차원 평면상에서 조작하거나 보정할 때 사용한다.

  • 핵심 원리: 어파인 변환과 달리, 원근감이 들어가기 때문에 '평행했던 선들이 변환 후에는 더 이상 평행하게 유지되지 않을 수 있다'는 것이 가장 큰 차이점이다.
  • 적용 방법: 비스듬하게 찌그러진 사각형을 반듯한 정면 뷰의 직사각형으로 펼치기 위해, 변환 전후로 총 4개의 매칭 좌표점(보통 대상의 네 모서리)이 필요하며 이를 통해 3 X 3 변환 행렬을 도출한다.
  • 의료 적용: X-ray 필름이나 스캔된 의료 문서가 비스듬하게 찍혔을 때, 이를 스캐너로 정면에서 반듯하게 뜬 것처럼 직사각형 형태로 쫙 펴서 보정할 때 아주 유용하다.

[실습 코드: 원근 변환(Perspective) 적용]

Python
img = cv2.imread("my_card.jpg")
# OpenCV의 BGR 색상계를 Matplotlib 출력을 위해 RGB로 변환
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) 

# 1. 변환 전 삐딱한 객체의 네 꼭짓점 좌표 (좌상->좌하->우상->우하 순서)
pts1 = np.float32([[1250,1250],[650,2750],[2200,1500],[1750,3150]])

# 2. 반듯하게 펴질 새로운 목표 좌표 (1000x1200 크기의 반듯한 직사각형 형태)
pts2 = np.float32([[10,10],[10,1200],[1000,10],[1000,1200]])

# 변환할 기준이 되는 4개의 좌표점 시각화
cv2.circle(img, (1250,1250), 20, (255,0,0),-1)
cv2.circle(img, (650,2750), 20, (0,255,0),-1)
cv2.circle(img, (2200,1500), 20, (0,0,255),-1)
cv2.circle(img, (1750,3150), 20, (0,0,0),-1)

# 3. 4개의 매칭 점을 이용해 3x3 퍼스펙티브 변환 행렬(M) 계산
M = cv2.getPerspectiveTransform(pts1,pts2)
# 4. 행렬을 적용하여 가로 1000, 세로 1200 사이즈로 원근 왜곡을 보정하여 출력
dst = cv2.warpPerspective(img, M, (1000,1200))

plt.subplot(121),plt.imshow(img),plt.title('image')
plt.subplot(122),plt.imshow(dst),plt.title('Perspective')
plt.show()

카드로 실습