ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 해리스 코너를 사용한 이미지 매칭(Image feature matching with Harris Corner Detection)
    OpenCV/OpenCV Python 강좌 2019. 5. 29. 11:47




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

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

     

    2019. 5. 29




    두 장의 이미지에서 각각 코너점을 찾은 후, 유사한 코너점끼리 매칭을 해준 결과입니다.

     





    다음 단계를 거쳐 코너점을 매칭합니다.



    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), -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(p1[i,0], p1[i,1],5) for i in range(p1.shape[0])]
    kp2 = [cv2.KeyPoint(p2[i,0], 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

    포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
    댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

    여러분의 응원으로 좋은 컨텐츠가 만들어집니다. 지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

    유튜브 구독하기


    댓글 0

Designed by Tistory.