카테고리 없음

[OpenCV] 객체 탐지와 두 사진 비교하기

콜라드리블 2024. 5. 2. 22:09
작성자 김명원
일 시 2024. 5. 2  (목) 18:00 ~ 21:00
장 소 복지관 b128-1호
참가자 명단 임혜진, 이재영, 성창민, 김명원, 장원준
 사 진

 

OpenCV와 matiplotilb 라이브러리 임포트하기

import cv2
import numpy as np
from matplotlib import pyplot as plt

 

사진 2장을 불러오고 시각화하기

car_img = cv2.imread('/content/1.png')
license_img = cv2.imread('/content/2.png')
#plt.imshow()함수를 사용하여 이미지를 출력했다. 다음과 같이 원본과 색상이 다르게 출력된다.
#그 이유는 cv2.imread()함수를 사용하면 픽셀을 RGB 순서가 아니라 BGR 순서로 읽게 되는데 이미지 데이터의 형식이나 범위가 다를 경우 이렇게 출력될 수 있다.
plt.subplot(1,2,1)
plt.imshow(car_img)
plt.subplot(1,2,2)
plt.imshow(license_img)
plt.show()

제공한 차 사진을 template 삼아 차사진에서 번호판(top_left) 출력하기

#객체를 탐지하기 위해 차와 번호판을 흑백으로 가져온다.
car_img = cv2.imread('/content/1.png', cv2.IMREAD_GRAYSCALE)
img_1 = car_img.copy()
template = cv2.imread('/content/2.png', cv2.IMREAD_GRAYSCALE)
#번호판 이미지(행,열,채널)을 (채널,열,행) 순으로 바꾼 뒤 w, h에 각각 너비와 높이를 저장한다.
w, h = template.shape[::-1]
#이미지를 매칭할 때 OpenCV에서 제공하는 여러 메소드를 사용한다.
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
#각 메소드 별로 matchTemplate함수를 사용해 번호판을 찾은 이미지를 출력한다.
for meth in methods:
img = img_1.copy()
method = eval(meth) #문자열로 표현된 메소드를 실행 가능한 코드로 변환시킨다.
res = cv2.matchTemplate(car_img,template,method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]: #cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED은 제곱의 '차이'를 매칭하는 방법으로 매칭된 결과가 낮을 수록 좋은 것이다.
top_left = min_loc
else: #나머지 메소드는 상관 관계 매칭, 상관 매칭 방법을 사용하기 때문에 매칭 결과의 최대를 저장한다.
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)

cv2.rectangle(img,top_left, bottom_right, 255, 2) #차 이미지에 탐지한 번호판 위치에 흑백의 정사각형을 그린다.

plt.subplot(121),plt.imshow(res,cmap = 'gray')
plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img,cmap = 'gray')
plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
plt.suptitle(meth)

plt.show()
print("표지판 위치: ", top_left)

제공된 차 사진에 번호판 위치 사각형으로 표시하기

car_img = cv2.imread('/content/1.png')
img_gray = cv2.cvtColor(car_img, cv2.COLOR_BGR2GRAY)
template = cv2.imread('/content/2.png', cv2.IMREAD_GRAYSCALE)
w, h = template.shape[::-1]
#위 방법처럼 cv2.TM_CCOEFF_NORMED 매칭 메소드를 사용해 번호판을 찾는다.
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
#번호판과 비슷하게 생긴 여러 사물을 탐지할 수도 있으므로 0.8 이상의 정확도를 가진 객체만 탐지하도록 한다.
threshold = 0.8
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(car_img, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)

cv2.imwrite('res.png',car_img)

res_img = cv2.imread('/content/res.png')
plt.imshow(res_img)

plt.show()

SIFT 서술자 추출기 생성, 각 사진에 대해 키 포인트와 서술자 추출

image_1 = cv2.imread('/content/3.png')
image_2 = cv2.imread('/content/4.png')
image_1 = cv2.cvtColor(image_1, cv2.COLOR_BGR2RGB)
image_2 = cv2.cvtColor(image_2, cv2.COLOR_BGR2RGB)
gray_1 = cv2.cvtColor(image_1, cv2.COLOR_BGR2GRAY)
gray_2 = cv2.cvtColor(image_2, cv2.COLOR_BGR2GRAY)
# ORB 서술자 추출기 생성
detector = cv2.ORB_create()


# 각 영상에 대해 키 포인트와 서술자 추출
#각 이미지의 특징점(뚜렷한 부분이나 관심 영역)을 검출하고 서술자(특징점 주변에 대한 정보)를 저장한다.
key_point1, desc1 = detector.detectAndCompute(gray_1,None)
key_point2, desc2 = detector.detectAndCompute(gray_2,None)

 

BFMatcher 생성, Hamming 거리 상호체크 후 매칭 계산

# BFMatcher 생성, Hamming 거리, 상호 체크
#BFMatcher를 생성하는데 cv2.NORM_HAMMING(해밍 거리 측정 방법)를 사용해 두 서술자 집합을 매칭한다.
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(desc1,desc2)

matches = sorted(matches, key = lambda x:x.distance)
#이미지 1에서 검출된 특징점 1과 이에 일치하는 이미지 2의 특징점 2를 매칭한 뒤, 모든 매치를 두 사진 위에 선을 그어 시각적으로 표현한다.
res = cv2.drawMatches(image_1,key_point1,image_2,key_point2,matches[::],None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.figure(figsize=(18,18))
plt.imshow(res)
plt.show()

FlannBasedMatcher 사용

# FLANN 매처 파라미터 설정
# FLANN 기반 매처 생성
#sift(이미지 내의 특징을 잘 표현할 수 있는 특징점을 찾는 알고리즘)객체를 생성한다.
sift = cv2.SIFT_create()

key_point1, desc1 = sift.detectAndCompute(gray_1,None)
key_point2, desc2 = sift.detectAndCompute(gray_2,None)
#FLANN의 알고리즘 중 FLANN_INDEX_KDTREE를 사용했다.
#trees는 Kmeans와 같은 트리의 개수이다. 트리의 개수가 많아질수록 정확도는 높아지지만 더 많은 메모리와 계산량이 필요하다.
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
#매칭 중 수행되는 트리 탐색 수이다. checks 값이 커지만 속도가 빨라지고, 작아지면 정확도가 높아진다.
search_params = dict(checks=50)
#이제 cv2.FlannBasedMatcher함수를 사용해 위에 설정한 값을 토대로 flann 객체를 생성한다.
flann = cv2.FlannBasedMatcher(index_params,search_params)
#그 뒤, 같은 것을 의미하는 서술자끼리 매칭시키고 짝을 지어 리스트에 정보를 저장한다.
matches = flann.knnMatch(desc1,desc2,k=2)
matchesMask = [[0,0] for i in range(len(matches))]
#매칭된 각 서술자들을 비교하며 거리가 0.55 이하인 경우만 선택한다.
for i,(m,n) in enumerate(matches):
if m.distance < 0.55*n.distance:
matchesMask[i]=[1,0]
#매칭된 점들 초록색 선으로 연결하고 한쪽에서만 검출된 특징점들은 빨간 점으로 나태낸다.
draw_params = dict(matchColor = (0,255,0), singlePointColor = (255,0,0), matchesMask = matchesMask, flags = cv2.DrawMatchesFlags_DEFAULT)
res = cv2.drawMatchesKnn(image_1,key_point1,image_2,key_point2,matches,None,**draw_params)

plt.figure(figsize=(18,18))
plt.imshow(res)
plt.show()

OpenCV를 더욱 다양한 분야에 사용해보면서 
다음 프로젝트에서도 유용하게 잘 사용할 수 있으면 좋겠다.