딥러닝

딥러닝 - OpenCV_012_템플릿매칭_허프변환

인생진리 2023. 3. 9. 12:43

[12차시] 학습목표
○ 탬플릿 매칭에 대해 학습한다.
○ 허프 변환을 이용하여 직선 및 원을 찾는 방법을 학습한다.

1  템플릿 매칭

1.1  where() 함수 테스트

  • 해당 조건에 만족하는 값이 있는 인덱스들을 반환
import numpy as np

arr1 = np.arange(2, 10)
loc = np.where(arr1 > 5)
loc

이 코드는 NumPy 모듈을 이용하여 다음과 같은 작업을 수행합니다.

  1. 2부터 9까지의 숫자가 들어있는 NumPy 배열 arr1을 생성합니다.
  2. np.where() 함수를 사용하여 arr1 배열에서 5보다 큰 요소의 인덱스를 추출합니다.
  3. loc 변수에 추출된 인덱스를 저장합니다.

위 코드에서는 np.where() 함수를 사용하여 조건을 만족하는 요소의 인덱스를 추출하는 방법을 보여줍니다. 이때, np.where() 함수는 첫 번째 인수로 조건식을 받아 해당 조건식이 참(True)인 요소의 인덱스를 반환합니다.

추출된 인덱스는 loc 변수에 저장됩니다

arr2 = np.array([[3, 5, 7, 4],
                 [6, 7, 2, 3],
                 [2, 4, 8, 5]])

# 값이 5보다 큰 값들의 행과 열인덱스를 튜플로 반환 
loc = np.where(arr2 >= 5)

print("행 인덱스") 
print(loc[0]) 
print("열 인덱스") 
print(loc[1])

이 코드는 2차원 NumPy 배열 arr2에서 값이 5보다 큰 요소들의 행과 열 인덱스를 찾아 출력하는 코드입니다.

np.where() 함수를 사용하여 arr2 배열에서 값이 5보다 큰 요소들의 인덱스를 추출합니다.

np.where() 함수는 조건식을 만족하는 요소의 인덱스를 반환하며, 이때 반환값은 튜플로 구성됩니다.

튜플의 첫 번째 요소는 행 인덱스를, 두 번째 요소는 열 인덱스를 나타내게 됩니다.

 

그리고 행 인덱스와 열 인덱스를 각각 loc[0], loc[1]으로 추출하여 출력합니다.

이때, loc[0]은 행 인덱스를 나타내고, loc[1]은 열 인덱스를 나타냅니다

 

.

1.2  zip() 함수 테스트

  • 동일한 개수를 가진 리스트나 튜플을 같은 위치의 요소들끼리 묶어서 튜플로 만들고, 이를 요소로 하는 리스트를 만듬

a = zip([0, 1, 2, 3], [4, 5, 6, 7])

for pt in a:
    print(pt)

이 코드는 두 개의 리스트를 묶어서 zip 객체를 생성하고, for 문을 사용하여 zip 객체를 순회하면서 각 요소를 출력하는 코드입니다.

zip() 함수는 두 개 이상의 iterable한 객체를 묶어서 각 객체에서 같은 인덱스에 위치한 요소들을 순서대로 추출할 수 있는 객체를 만듭니다. 이때, zip 객체는 generator 형태로 반환되며, for문과 같은 반복문에서 사용할 수 있습니다.

위 코드에서는 두 개의 리스트 [0, 1, 2, 3]과 [4, 5, 6, 7]를 zip() 함수를 사용하여 묶어서 zip 객체 a를 생성합니다. 그리고 for문에서 zip 객체 a를 순회하면서 각 요소를 출력합니다.

# arr[A:B:C]의 의미 -> index A 부터 index B 까지 C의 간격으로 배열을 생성
# C가 양수라면 마지막 index까지, C가 음수라면 첫 index까지
arr = range(10) 

print(arr[::2]) # 처음부터 끝까지 두 칸 간격으로
print(arr[1::2]) # index 1 부터 끝까지 두 칸 간격으로 
print(arr[::-1]) # 처음부터 끝까지 -1칸 간격으로 ( == 역순으로) 
print(arr[::-2]) # 처음부터 끝까지 -2칸 간격으로 ( == 역순, 두 칸 간격으로) 
print(arr[3::-1]) # index 3 부터 끝까지 -1칸 간격으로 ( == 역순으로)
print(arr[1:6:2]) # index 1 부터 index 6 까지 두 칸 간격으로

이 코드는 range() 함수를 사용하여 0부터 9까지의 숫자가 저장된 리스트를 생성하고,

리스트 슬라이싱을 사용하여 해당 리스트의 요소들을 출력하는 코드입니다.

리스트 슬라이싱을 사용할 때, arr[A:B:C]의 형태로 인덱스 A부터 B까지의 요소를 C 간격으로 추출할 수 있습니다.

이때 C가 양수라면 끝 인덱스까지 추출하며, C가 음수라면 시작 인덱스까지 추출합니다.

위 코드에서는 리스트 arr에서 다양한 방법으로 슬라이싱을 수행하며,

그 결과를 출력합니다. 예를 들어, arr[::2]는 리스트의 처음부터 끝까지 두 칸 간격으로 요소를 추출하여 출력하고,

arr[::-1]는 리스트를 역순으로 출력합니다.

 

# zip(*loc[::-1]) 테스트
import numpy as np

loc = (np.array([0, 1, 2, 3]), np.array([4, 5, 6, 7]))

# 동일한 개수를 가진 리스트나 튜플을 같은 위치의 멤버들끼리 묶어서 
# 튜플로 만든 다음 이를 멤버로 하는 리스트를 만듬
# *loc[::-1] : -1은 loc의 순서를 거꾸로 하여 튜플로 묶고 리스트로 만듬 
#              (x, y 좌표의 순서가 바뀌므로), *는 가변매개변수
for pt in zip(*loc[::-1]):
    print(pt)

이 코드는 NumPy 모듈을 사용하여 생성된 loc 변수에 대해 zip() 함수를 사용하여 연산하는 코드입니다.

위 코드에서 loc 변수는 2개의 1차원 NumPy 배열을 원소로 갖는 튜플입니다. loc[0]은 x 좌표, loc[1]은 y 좌표를 나타내는 배열입니다.

 

for문에서 zip(*loc[::-1])을 사용하여, loc[0], loc[1] 배열을 역순으로 뒤집고

zip() 함수로 묶어서 각각의 요소를 순회하며 출력합니다.

여기서 *loc[::-1]은 loc[0], loc[1] 배열을 묶은 튜플을 가변 인자 형태로 언패킹하는 것을 의미합니다.

1.3  템플릿 매칭 수행하기

  • Templete Matching : 어떤 이미지에서 템플릿 이미지와 매칭되는 부분이 있는지 검색하는 방법
  • 동작 방법
    • 템플릿 이미지를 입력 이미지의 좌측 상단에서 시작해서 전체 영역을 스캔
    • 템플릿 이미지와 입력 이미지 간의 유사도 분석을 통해 유사도를 반환

  • cv2.matchTemplate(원본이미지, 템플릿이미지, 매칭 방법)
    • 전체 이미지의 블록별로 매칭된 이미지 객체의 매칭도를 반환
    • 매칭 방법
      • cv2.TM_SQDIFF : 템플릿 영상에서 같은 위치에 있는 입력 영상의 부분 영상의 픽셀를 뺀 것을 제곱하여 다 더해서 유사도 비교
        • 유사하면 0(검정), 유사하지 않으면 255(흰색)을 반환
      • cv2.TM_SQDIFF_NORMED : cv2.TM_SQDIFF를 [0, 1]로 정규화한 것
      • cv2.TM_CCORR : 유사하면 255, 유사하지 않으면 0을 반환
      • cv2.TM_CCORR_NORMED : cv2.TM_CCORR를 정규화한 것
      • cv2.TM_CCOEFF : 밝기 보정 후에 cv2.TM_CCORR 방법을 적용한 것
      • cv2.TM_CCOEFF_NORMED : cv2.TM_CCOEFF를 정규화 한 것 (가장 성능이 우수, 속도가 느림), 완전히 일치하면 1, 역일치하면 -1, 상호연관성이 없으면 0을 반환
#실습
import cv2
import matplotlib.pyplot as plt

img = cv2.imread("./images/game.png")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

img_cut = cv2.imread("./images/game_cut.png")
img_cut = cv2.cvtColor(img_cut,cv2.COLOR_BGR2RGB)

plt.imshow(img)
plt.axis("off")
plt.show()

이 코드는 OpenCV를 사용하여 이미지 파일을 읽어들이고,

Matplotlib를 사용하여 이미지를 출력하는 코드입니다.

 

먼저, cv2.imread() 함수를 사용하여 "./images/game.png" 파일을 읽어들입니다.

이때, 이미지의 색상 순서는 BGR로 저장됩니다.

따라서, cv2.cvtColor() 함수를 사용하여 BGR 이미지를 RGB 이미지로 변환합니다.

 

그리고 cv2.imread() 함수를 사용하여 "./images/game_cut.png" 파일도 읽어들입니다.

이때도 이미지의 색상 순서는 BGR이므로, cv2.cvtColor() 함수를 사용하여 RGB 이미지로 변환합니다.

마지막으로, plt.imshow() 함수를 사용하여 img 이미지를 출력합니다.

이때, plt.axis("off")를 사용하여 축을 없애고, plt.show() 함수를 사용하여 이미지를 화면에 출력합니다.

따라서, 위 코드를 실행하면 "./images/game.png" 파일이 RGB 이미지로 변환되어 화면에 출력됩니다.

img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img_cut_gray = cv2.cvtColor(img_cut, cv2.COLOR_RGB2GRAY)

# 매칭도와 좌표가 반환
res= cv2.matchTemplate(img_gray, img_cut_gray, cv2.TM_CCOEFF_NORMED)

plt.imshow(res, cmap='gray')
plt.axis('off')

이 코드는 OpenCV를 사용하여 이미지 매칭을 수행하는 코드입니다.

 

먼저, cv2.cvtColor() 함수를 사용하여 img와 img_cut 이미지를 각각 RGB 이미지에서 그레이스케일 이미지로 변환합니다. 그레이스케일 이미지는 색상 정보가 없이 밝기 정보만을 가지는 이미지입니다.

 

그리고 cv2.matchTemplate() 함수를 사용하여 img_gray와 img_cut_gray 이미지를 비교하여 매칭도와 좌표를 반환합니다.

 

이때, cv2.TM_CCOEFF_NORMED 매칭 알고리즘을 사용합니다.

이 알고리즘은 매칭되는 영역의 픽셀 값이 서로 얼마나 비슷한지를 나타내는 매칭 점수를 반환합니다.

마지막으로, plt.imshow() 함수를 사용하여 res 이미지를 출력합니다.

 

cmap='gray'를 사용하여 흑백 이미지로 출력하고, plt.axis('off')를 사용하여 축을 없앱니다.

 

따라서, 위 코드를 실행하면 img_gray와 img_cut_gray 이미지를 비교하여 매칭도와 좌표를 구한 후,

그 결과를 출력합니다.

#실습
import numpy as np


# 매칭도가 0.7 이상인 값들의 행과 열의 인덱스를 반환
loc = np.where(res >= 0.7 )

h, w = img_cut_gray.shape

for pt in zip(*loc[::-1]) :
    cv2.rectangle(img, pt, (pt[0]+w, pt[1]+h),(0, 255, 0),2)

plt.imshow(img)
plt.axis("off")

이 코드는 OpenCV를 사용하여 이미지 매칭 결과에서 일정 매칭 점수 이상인 값을 찾아내고,

그 좌표값을 기준으로 이미지 상에 사각형을 그리는 코드입니다.

 

먼저, np.where() 함수를 사용하여 res 이미지에서 매칭도가 0.7 이상인 값들의 행과 열의 인덱스를 반환합니다.

그리고 img_cut_gray 이미지의 크기인 h, w를 구합니다.

 

마지막으로, for문과 zip() 함수를 사용하여 매칭 결과에서 추출한 좌표값을

하나씩 꺼내어 cv2.rectangle() 함수로 이미지 상에 사각형을 그립니다.

 

이때, pt는 매칭 결과에서 추출한 좌표값을 나타내며, (pt[0]+w, pt[1]+h)는

사각형의 우측 하단 꼭지점의 좌표를 나타냅니다.

 

마지막으로, plt.imshow() 함수를 사용하여 img 이미지를 출력합니다.

 

따라서, 위 코드를 실행하면 img와 img_cut 이미지의 매칭 결과에서 일정 매칭 점수 이상인 값을 찾아내어,

해당 좌표값을 기준으로 사각형을 그린 결과가 출력됩니다.

 

실습문제

○ 다른 이미지를 이용하여 템플릿 매칭 수행하기

2  허프 변환을 이용한 도형 찾기

2.1  개념

  • 허프 변환 (Hough Transform)
    • 이미지에서 모양을 찾는 가장 유명한 방법
    • 이미지의 형태를 찾거나, 누락되거나 깨진 영역을 복원할 수 있음
  • 기본적으로 허프변환은 직선의 방정식을 이용
  • 하나의 점을 지나는 무수한 직선의 방적식은 y=ax+b로 표현할 수 있으며, 이것을 삼각함수를 이용하여 변형하면 r = 𝑥 cos 𝜃 + 𝑦 sin 𝜃 으로 표현
  • r, 𝜃 평면에서 선들의 교점은 x, y 평면에서 하나의 직선을 의미
  • 따라서, r, 𝜃 평면에서 많은 선이 만나는 점을 찾으면 x, y 평면의 직선을 검출할 수 있음

2.2  직선 찾기

  • cv2.HoughLines(image, rho, theta, threshold)
    • image : single-channel binary image, canny edge를 적용한 이미지
    • rho : r 값의 범위 (0 ~ 1 실수)
    • theta : 𝜃 값의 범위(0 ~ 180 정수)
    • threshold : 만나는 점의 기준, 숫자가 작으면 많은 선이 검출되지만 정확도가 떨어지고 숫자가 크면 정확도가 올라감
    •  
#실습

2.3  동영상에서 직선 찾기

2.4  확률 허프 변환

  • cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)
    • 허프변환은 모든 점에 대해서 계산을 하기 때문에 시간이 많이 소요
    • 확률 허프 변환 : 모든 픽셀 값을 처리하지 않고 적당히 필요한 만큼 확률적으로 픽셀들을 선택해 연산
      • image : single-channel binary image, canny edge를 적용한 이미지
      • rho : r 값의 범위 (0 ~ 1 실수)
      • theta : 𝜃 값의 범위(0 ~ 180 정수)
      • threshold : 만나는 점의 기준, 숫자가 작으면 많은 선이 검출되지만 정확도가 떨어지고 숫자가 크면 정확도가 올라감
      • minLineLength : 선의 최소 길이. 이 값보다 작으면 선이 아니라고 인식
      • maxLineGap : 선과 선사이의 최대 허용간격. 이 값보다 작으면 하나의 선으로 인식
#실습

2.5  원 찾기

  • cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, minDis, param1, param2, minRadius, maxRadius)
    • cv2.HOUGH_GRADIENT : 원을 찾는 방법
    • 1 : 원이미지와 허프변환 이미지 간의비율 (1로 설정)
    • minDist : 원 중심들 간의 최소 거리 (0보다 커야 함)
    • param1 : Canny 에지 추출자의 maxVal 값
    • parma2 : 허프변환 카운팅 값 (너무 작으면 원하지 않는 많은 원들이 검출됨)
    • minRadius : 원의 최소 반지름
    • maxRadius : 원의 최대 반지름
  • 원리
    • 이미지에서 에지를 계산
    • 에지에 법선을 긋고 (기울기) 직각인 선을 그림
      • 설정한 반지름의 범위에 따라 여러 개의 선을 그림
    • 가장 선이 많이 만나는 점에 해당하는 반지름 크기의 원을 찾음

#실습

실습문제

○ 포도송이 위치 분석하기

[12차시] 정리하기
○ Templete Matching : 어떤 이미지에서 템플릿 이미지와 매칭되는 부분이 있는지 검색하는 방법
  • 동작 방법
    • 템플릿 이미지를 입력 이미지의 좌측 상단에서 시작해서 전체 영역을 스캔
    • 템플릿 이미지와 입력 이미지 간의 유사도 분석을 통해 유사도를 반환
    • cv2.matchTemplate(원본이미지, 템플릿이미지, 매칭 방법)
○ 허프 변환 (Hough Transform)
  • 하나의 점을 지나는 무수한 직선의 방적식은 y=mx+c로 표현할 수 있으며, 이것을 삼각함수를 이용하여 변형하면 r = 𝑥 cos 𝜃 + 𝑦 sin 𝜃 으로 표현
  • r, 𝜃 평면에서 선들의 교점은 x, y 평면에서 하나의 직선을 의미
  • 따라서, r, 𝜃 평면에서 많은 선이 만나는 점을 찾으면 x, y 평면의 직선을 검출할 수 있음
  • cv2.HoughLines(image, rho, theta, threshold) : 직선 검출
  • cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap) : 확률 허프 변환
    • 모든 픽셀 값을 처리하지 않고 적당히 필요한 만큼 확률적으로 픽셀들을 선택해 연산
  • cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, minDis, param1, param2, minRadius, maxRadius) : 원 찾기

파일 다운 경로 :https://github.com/yoonhyochang/Deep_learning