해리스 코너 디텍터를 사용하여 검출한 코너점을 사용하여 두 장의 이미지를 매칭하는 예제입니다.
코너점의 방향을 기준으로 이미지 패치를 회전시키서 매칭점인지 비교하기 때문에 다른 부분의 코너점인데 매칭될 수 있습니다.
2019. 5. 29 최초작성
2020. 9. 14 최근 OpenCV 버전에서 문제 되는 코드 수정
2021. 8. 1 최근 OpenCV 버전에서 문제 되는 코드 수정
두 장의 이미지에서 각각 코너점을 찾은 후, 유사한 코너점끼리 매칭을 해준 결과입니다.
다음 단계를 거쳐 코너점을 매칭합니다.
1. 해리스 코너 디텍터로 코너점을 찾습니다.
2. 코너점을 기준으로 일정 크기의 이미지를 ROI 합니다.
3. 코너점을 중심점으로 하는 일정크기의 패치를 얻었습니다.
일치하는 점인것 확실 하지만 이미지 방향이 다른 상태입니다.
4. 코너점의 방향을 구하여 이미지를 회전시켜보면 유사한 이미지가 됩니다.
5. 회전된 이미지에서 이미지 중심점을 기준으로 일정 범위를 ROI한 후, 5 x 5 크기의 이미지로 축소합니다.
그리고 1차원 배열로 바꾸어줍니다.
6. 첫번째 이미지 패치를 기준으로 두번째 이미지 패치에서 유사한 것을 찾아 기록합니다.
NCC(Normalized Cross Correlation)를 사용합니다.
7. 두번째 이미지 패치를 기준으로 첫번째 이미지 패치에서 유사한 것을 찾아 기록합니다.
NCC(Normalized Cross Correlation)를 사용합니다.
8. 6번과 7번 매칭 결과 중 공통되는 것만 가지고 매칭 결과 이미지를 만듭니다.
전체 소스코드입니다.
import cv2
import numpy as np
# 해리스 코너를 사용하여 코너점을 찾습니다.
def find_points(image,block_size=5, k=0.05):
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,block_size,3,k)
dst = cv2.dilate(dst,None)
ret, dst = cv2.threshold(dst,0.01*dst.max(),255,0)
dst = np.uint8(dst)
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)
points=[]
for i in range(1, len(corners)):
points.append([int(corners[i,0]),int(corners[i,1])])
points = np.asarray(points)
return points
# 코너점의 방향을 계산합니다.
def ori_points(image, points):
orientation = np.zeros((points.shape[0], 1))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
sobel = np.array([[-1.0, 0.0, 1.0],[-2.0, 0.0, 2.0],[-1.0, 0.0, 1.0]])
ori_x = cv2.filter2D(gray, -1, sobel)
ori_y = cv2.filter2D(gray, -1, sobel.T)
for i in range(points.shape[0]):
x = points[i, 0]
y = points[i, 1]
angle = np.arctan2(-ori_y[y, x], ori_x[y, x]) * 180/np.pi
if angle < 0.0:
orientation[i,:]=(angle+360.0)
else:
orientation[i,:]=(angle)
return orientation
# 이미지 패치를 회전합니다.
def rotation(image, angle):
(h, w) = image.shape
(cX, cY) = (w // 2, h // 2)
rotation_matrix = cv2.getRotationMatrix2D((cX, cY), float(-angle), 1.0)
cos = np.abs(rotation_matrix[0, 0])
sin = np.abs(rotation_matrix[0, 1])
newWidth = int((h * sin) + (w * cos))
newHeight = int((h * cos) + (w * sin))
rotation_matrix[0, 2] += (newWidth / 2) - cX
rotation_matrix[1, 2] += (newHeight / 2) - cY
dst=cv2.warpAffine(image, rotation_matrix, (newWidth, newHeight))
return dst
# 코너점을 중심으로한 descriptor를 구합니다.
def points_descriptor(image,points,orientation,num):
descriptor=np.zeros((points.shape[0],25))
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
gray=cv2.copyMakeBorder(gray,100,100,100,100, borderType=cv2.BORDER_REPLICATE)
for i in range(points.shape[0]):
x = points[i, 0] + 100
y = points[i, 1] + 100
patch = gray[y-40:y+40, x-40:x+40]
new_patch = rotation(patch, orientation[i,:])
cY,cX = int(new_patch.shape[0]/2), int(new_patch.shape[1]/2)
new_patch = new_patch[cY-20:cY+20,cX-20:cX+20]
new_patch = cv2.resize(new_patch,(5,5),interpolation=cv2.INTER_AREA)
a=new_patch.flatten()
if a.all() == False:
descriptor[i,:] = np.zeros(25)
else:
descriptor[i,:]= a
return descriptor
def feature_match(des1, des2):
match1=[]
match2=[]
for i in range(des1.shape[0]):
temp = []
for j in range(des2.shape[0]):
if des1[i,:].all() != False and des2[j,:].all() != False:
p1 = des1[i,:]
p2 = des2[j,:]
d = np.sum((p1 - np.mean(p1))*(p2 - np.mean(p2))/(np.std(p1)*np.std(p2)))
d = d / 25
temp.append(d)
else:
temp.append(-1)
d = np.argmax(temp)
if temp[d] > 0.5:
match1.append((i, d))
for j in range(des2.shape[0]):
temp = []
for i in range(des1.shape[0]):
if des1[i,:].all() != False and des2[j,:].all() != False:
p1 = des1[i,:]
p2 = des2[j,:]
d = np.sum((p1 - np.mean(p1))*(p2 - np.mean(p2))/(np.std(p1)*np.std(p2)))
d = d / 25
temp.append(d)
else:
temp.append(-1)
d = np.argmax(temp)
if temp[d] > 0.5:
match2.append((d, j))
match = list(set(match1).intersection(set(match2)))
return match
f1 = cv2.imread("s1.jpg")
f2 = cv2.imread("s2.jpg")
# 코너점을 찾습니다.
p1= find_points(f1,block_size=5, k=0.04)
p2= find_points(f2,block_size=5, k=0.04)
# 코너점 방향을 구합니다.
ori1 = ori_points(f1, p1)
ori2 = ori_points(f2, p2)
# 코너점을 이미지로 저장합니다.
a1 = f1.copy()
a2 = f2.copy()
for i in range(p1.shape[0]):
cv2.circle(a1, (p1[i, 0], p1[i, 1]), 3, (0,255,0), -1)
cv2.imwrite("point1.jpg", a1)
for i in range(p2.shape[0]):
cv2.circle(a2, (p2[i, 0], p2[i, 1]), 3, (0,255,0), -1)
cv2.imwrite("point2.jpg", a2)
# descriptor를 구합니다.
des1 = points_descriptor(f1,p1,ori1,1)
des2 = points_descriptor(f2,p2,ori2,2)
# 매칭을 구합니다.
match_result= feature_match(des1, des2)
print(len(match_result))
# 매칭을 그리기 위한 작업입니다.
kp1 = [cv2.KeyPoint(int(p1[i,0]), int(p1[i,1]),5) for i in range(p1.shape[0])]
kp2 = [cv2.KeyPoint(int(p2[i,0]), int(p2[i,1]),5) for i in range(p2.shape[0])]
matches = [cv2.DMatch(i[0], i[1], 1) for i in match_result]
draw_params = dict(matchColor = (0, 255, 0), # 매칭된 점은 초록색
singlePointColor = (255, 0, 0), # 매칭 안된 점은 파란색
flags = 0)
matching = cv2.drawMatches(f1, kp1, f2, kp2, matches, None,**draw_params)
cv2.imshow("result", matching)
cv2.waitKey(0)
참고
'OpenCV > OpenCV 강좌' 카테고리의 다른 글
Python OpenCV에서 이미지 크기 (width, height) 가져오기 (2) | 2021.11.14 |
---|---|
OpenCV - MSER과 IOU를 사용하여 사각형 검출 (0) | 2021.10.04 |
OpenPose를 사용하여 손가락 인식하는 OpenCV 예제 (0) | 2021.02.02 |
char 배열과 Mat간 변환하는 OpenCV 예제 (0) | 2020.12.10 |
흑백 사진을 컬러 사진으로 변환하는 방법(colorization) (0) | 2020.11.27 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!