반응형

해리스 코너 디텍터를 사용하여 검출한 코너점을 사용하여 두 장의 이미지를 매칭하는 예제입니다.

코너점의 방향을 기준으로 이미지 패치를 회전시키서 매칭점인지 비교하기 때문에 다른 부분의 코너점인데 매칭될 수 있습니다.

 

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)


참고

[1] https://github.com/ImXman/Computer_Visison_Feature_Matching/blob/b6b7f245e3b1f30b2f8bca5e1aff5c2ddb435fdf/feature_matching.py

 

[2] https://stackoverflow.com/questions/50984205/how-to-find-corners-points-of-a-shape-in-an-image-in-opencv

 

[3] https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_feature2d/py_features_harris/py_features_harris.html

 

[4] https://yeslab.tistory.com/26

반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts