OpenCV Review 05

2400·2025년 3월 10일
post-thumbnail

컨볼루션 적용(가우시안 스무딩, 엠보싱)

import cv2 as cv  
import numpy as np

img = cv.imread("./.data/seulgi4.jpg")

img = cv.resize(img, dsize=(0, 0), fx=0.5, fy=0.5)

gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

cv.putText(gray, 'seulgi', (10, 20), cv.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)

cv.imshow('Original', gray)

# 다양한 크기의 가우시안 블러 적용 후 결과를 수평으로 연결
# 왼쪽부터 5x5, 9x9, 15x15 커널 크기 순서
smooth = np.hstack((cv.GaussianBlur(gray, (5, 5), 0.0),
                    cv.GaussianBlur(gray, (9, 9), 0.0),
                    cv.GaussianBlur(gray, (15, 15), 0.0)))

# 블러 처리된 이미지들을 가로로 연결하여 표시
cv.imshow('smooth', smooth)

# 엠보싱 필터 정의 (좌상단에서 우하단 방향으로 양각 효과)
femboss = np.array([[-1.0, 0.0, 0.0],
                    [0.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0]])

# 그레이스케일 이미지를 16비트 정수형으로 변환 (음수 값 처리를 위해)
gray16 = np.int16(gray)

# 엠보싱 필터 적용 (올바른 방법)
# filter2D 결과에 128을 더해 음수 값을 양수 범위로 이동시키고, 0-255 범위로 클리핑
emboss = np.uint8(np.clip(cv.filter2D(gray16, -1, femboss) + 128, 0, 255))

# 엠보싱 필터 적용 (문제 있는 방법 1)
# 클리핑 없이 128 더하기 - 오버플로우 발생 가능
emboss_bad = np.uint8(cv.filter2D(gray16, -1, femboss) + 128)

# 엠보싱 필터 적용 (문제 있는 방법 2)
# 8비트 이미지에 직접 적용 - 음수 값이 래핑되어 의도하지 않은 결과 생성
emboss_worse = cv.filter2D(gray, -1, femboss)

# 세 가지 방법으로 처리된 엠보싱 결과 이미지 표시
cv.imshow('Emboss', emboss)  # 올바른 방법
cv.imshow('Emboss_bad', emboss_bad)  # 문제 있는 방법 1
cv.imshow('Emboss_worse', emboss_worse)  # 문제 있는 방법 2

# 키 입력 대기 (아무 키나 누를 때까지 창 유지)
cv.waitKey()

# 모든 OpenCV 창 닫기
cv.destroyAllWindows()

기하연산

동차 좌표(Homogeneous Coordinates)

  • 일반 2D 좌표 (x,y)를 (x,y,w)로 표현하는 방식으로, 보통 w=1로 설정
  • 장점: 이동, 회전, 크기 변환 등을 하나의 행렬 곱셈으로 표현 가능
  • 3x3 변환 행렬로 다양한 기하 변환을 일관되게 처리

동차 행렬(Homogeneous Matrix)

  • 동차 좌표 시스템에서 변환을 나타내는 행렬
  • 하나의 행렬로 여러 변환(이동, 회전, 크기 등)을 표현 가능

3가지 기본 기하 변환

  1. 이동 변환(Translation)

    • 좌표 이동: (x,y) → (x+tx, y+ty)
    • 동차 행렬: [1 0 tx; 0 1 ty; 0 0 1]
  2. 회전 변환(Rotation)

    • 각도 θ만큼 원점 중심으로 회전
    • 동차 행렬: [cosθ -sinθ 0; sinθ cosθ 0; 0 0 1]
  3. 크기 변환(Scaling)

    • x, y 방향으로 크기 변경
    • 동차 행렬: [sx 0 0; 0 sy 0; 0 0 1]

영상의 기하 변환

  • 픽셀 단위의 이미지에 기하 변환 적용
  • 변환 후 화소 위치가 정수가 아닌 경우가 발생
  • 입력 영상과 출력 영상의 화소 간 대응 관계를 찾기 위해 영상 보간 필요

영상 보간(Image Interpolation)

  • 변환 후 비정수 좌표의 화소값을 추정하는 방법
  • 주요 보간 방법:
    1. 최근접 이웃 보간(Nearest Neighbor): 가장 가까운 화소값 사용
    2. 쌍선형 보간(Bilinear): 주변 4개 화소의 가중 평균
    3. 쌍입방 보간(Bicubic): 주변 16개 화소를 사용한 3차 다항식 보간

영상 기하 변환은 컴퓨터 비전에서 이미지 정렬, 왜곡 보정, 특징점 추적 등 다양한 응용에 활용된다.

보간을 이용한 영상 변환

import cv2 as cv

img = cv.imread("./.data/frieren.png")

# 올바른 슬라이싱 문법: img[y1:y2, x1:x2]
patch = img[250:350, 170:270]  # y좌표(250~350), x좌표(170~270) 영역 추출

# 원본 이미지에 사각형 그리기
img = cv.rectangle(img, (170, 250), (270, 350), (255, 0, 0), 3)

# 다양한 보간법으로 패치 크기 5배 확대
patch1 = cv.resize(patch, dsize=(0, 0), fx=5, fy=5, interpolation=cv.INTER_NEAREST)
patch2 = cv.resize(patch, dsize=(0, 0), fx=5, fy=5, interpolation=cv.INTER_LINEAR)
patch3 = cv.resize(patch, dsize=(0, 0), fx=5, fy=5, interpolation=cv.INTER_CUBIC)

# 결과 표시
cv.imshow('Original', img)
cv.imshow('Resize nearest', patch1)     # 최근접 이웃 보간법
cv.imshow('Resize bilinear', patch2)    # 쌍선형 보간법
cv.imshow('Resize bicubic', patch3)     # 쌍입방 보간법

cv.waitKey()
cv.destroyAllWindows()
  1. 이미지에서 직사각형 영역(패치)을 추출
  2. 원본 이미지에 추출한 영역을 파란색 사각형으로 표시
  3. 추출한 패치를 세 가지 다른 보간법(최근접 이웃, 쌍선형, 쌍입방)을 사용하여 5배 확대
  4. 각각의 결과를 별도의 창에 표시

각 보간법의 차이점:

  • 최근접 이웃(INTER_NEAREST): 가장 빠르지만 계단 현상(pixelation)이 두드러짐
  • 쌍선형(INTER_LINEAR): 속도와 품질의 균형이 좋음, 약간의 블러 효과
  • 쌍입방(INTER_CUBIC): 품질이 가장 좋지만 계산 비용이 더 큼, 더 부드러운 결과
profile
시즌 2의 공부기록 - Artificial Intelligence & AeroSpace

0개의 댓글