I wanna make a function that match keypoints of two of images by use Python. And here is important thing: I don't wanna use built-in function except what's included in mine. I used two of same images, but second one is rotated 90 degree.
...
def mySIFT(src):
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY).astype(np.float32)
dst = cv2.cornerHarris(gray, 3, 3, 0.04)
dst[dst < 0.01 * dst.max()] = 0
dst = find_local_maxima(dst, 21)
dst = dst / dst.max()
y, x = np.nonzero(dst)
keypoints = []
for i in range(len(x)):
pt_x = int(x[i])
pt_y = int(y[i])
response = dst[y[i], x[i]]
keypoints.append(KeyPoint(pt_x, pt_y, None, -1, response, 0, -1))
Ix, Iy = calc_derivatives(gray)
magnitude = np.sqrt(Ix ** 2 + Iy ** 2)
angle = np.rad2deg(np.arctan2(Iy, Ix))
angle = (angle + 360) % 360
num = 0
for i in range(len(keypoints)):
x, y = keypoints[i].pt
orient_hist = np.zeros(36, )
for row in range(-8, 8):
for col in range(-8, 8):
p_y = int(y + row)
p_x = int(x + col)
if p_y < 0 or p_y > src.shape[0] - 1 or p_x < 0 or p_x > src.shape[1] - 1:
continue
gaussian_weight = np.exp((-1 / 16) * (row ** 2 + col ** 2))
orient_hist[int(angle[p_y, p_x] // 10)] += magnitude[p_y, p_x] * gaussian_weight
keypoints[i].angle = np.max(orient_hist)
for ori in range(len(orient_hist)):
if orient_hist[ori] > 0.75 * keypoints[i].angle:
if ori != np.argmax(orient_hist):
keypoints.append(KeyPoint (pt_x, pt_y, size, orient_hist[ori], keypoints[i].response, 0, -1))
descriptors = np.zeros((len(keypoints), 128))
for i in range(len(keypoints)):
x, y = keypoints[i].pt
theta = np.deg2rad(keypoints[i].angle)
cos_angle = np.cos(theta)
sin_angle = np.sin(theta)
for row in range(-8, 8):
for col in range(-8, 8):
row_rot = np.round((cos_angle * col) + (sin_angle * row))
col_rot = np.round((cos_angle * col) - (sin_angle * row))
p_y = int(y + row_rot)
p_x = int(x + col_rot)
if p_y < 0 or p_y > (src.shape[0] - 1) or p_x < 0 or p_x > (src.shape[1] - 1):
continue
descriptor_angle = angle[p_y, p_x] - keypoints[i].angle
while descriptor_angle < 0.0:
descriptor_angle += 360.0
while descriptor_angle >= 360.0:
descriptor_angle -= 360.0
descriptor_angle = math.floor(descriptor_angle / 45)
new_row = 2 + math.floor(row / 4)
new_col = 2 + math.floor(col / 4)
new_num = 8 * (new_col * 4 + new_row)
descriptors[i, new_num + descriptor_angle] += magnitude[p_y, p_x] * gaussian_weight
return keypoints, descriptors
def main():
src = cv2.imread("font.png")
src_rotation = cv2.rotate(src, cv2.ROTATE_90_CLOCKWISE)
kp1, des1 = mySIFT(src)
kp2, des2 = mySIFT(src_rotation)
bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True)
des1 = des1.astype(np.uint8)
des2 = des2.astype(np.uint8)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
result = cv2.drawMatches(src, kp1, src_rotation, kp2, matches[:20], outImg=None, flags=2)
cv2.imshow('sift', result)
cv2.waitKey()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
Specifically, it can find all of keypoints correctly, but it match wrong keypoints. When I use two perfectly same images(rotations are same), my program will work. However, if one of image is rotated, it cannot match.
There are two line that I think there are error:
keypoints.append(KeyPoint (pt_x, pt_y, size, orient_hist[ori], keypoints[i].response, 0, -1))
This line will try to append new keypoints in array, because I wanna add all of weights that is greater than 80% of most huge weight. I thought my problems are wrong parameters, so I checked it, but I could not solve.
descriptors[i, new_num + descriptor_angle] += magnitude[p_y, p_x] * gaussian_weight
This one will save data of magnitude reflected by gaussian weight. I thought I wrote wrong formula, but I don't know what is the right one(including mine).
I cannot find the problem, so is there anyone can help me?