Посмотрев на постановку задачи, я смог вычислить довольно неплохие результаты.
Здесь мы использовали функции KAZE, новый многомасштабный алгоритм обнаружения и описания объектов 2D в нелинейных масштабных пространствах. Предыдущие подходы обнаруживают и описывают объекты на разных уровнях масштаба путем построения или аппроксимации гауссова масштаба масштаба изображения.
Однако размытие по Гауссу не учитывает естественные границы объектов и в равной степени сглаживает как детали, так и шумы, снижая точность локализации и различимость. Напротив, мы обнаруживаем и описываем 2D-объекты в нелинейном масштабном пространстве с помощью нелинейной диффузионной фильтрации. Таким образом, мы можем сделать размытие локально адаптивным к данным изображения, уменьшая шум, но сохраняя границы объекта, получая превосходную точность локализации и различимость.
Нелинейное масштабное пространство построено с использованием эффективных методов аддитивного разделения операторов (AOS) и диффузии с переменной проводимостью. Мы представляем обширную оценку эталонных наборов данных и практическое применение сопоставления на деформируемых поверхностях. Несмотря на то, что наши функции вычисляются несколько дороже, чем SURF из-за конструкции пространства с нелинейным масштабом, но сопоставимы с SIFT, наши результаты показывают шаг вперед в производительности как по обнаружению, так и по сравнению с предыдущими современными методами. .
Вы можете найти больше ссылок в исследовательской работе здесь .
import os, cv2, random
import numpy as np
import matplotlib.pyplot as plt
#show image
def displayImage(input_img, display_title=None):
im_shape = input_img.shape
c = 3
if len(im_shape) >= 3:
c = im_shape[2]
if len(im_shape) == 2:
c = 1
if c == 3:
rgb_img = cv2.cvtColor(input_img.copy(), cv2.COLOR_BGR2RGB)
plt.imshow(rgb_img)
if c == 1:
plt.imshow(input_img,cmap='gray')
plt.axis('off')
plt.grid(False)
if not display_title is None:
plt.title(display_title)
plt.show()
def featureExtractor(image, fd):
kpts, desc = fd.detectAndCompute(image, None)
return kpts, desc
def featureMatching(kpts1, desc1, kpts2, desc2, fd='kaze'):
if desc1 is None and desc_2 is None:
print('Empty descriptor')
return
if fd == 'akaze':
# create BFMatcher object
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# Match descriptors.
matches = bf.match(desc1, desc2)
# Sort them in the order of their distance.
matches = sorted(matches, key = lambda x:x.distance)
# good_matches = matches[:10]
return matches
else:
# Matching descriptor vectors with a FLANN based matcher
matcher = cv2.DescriptorMatcher_create(cv2.DescriptorMatcher_FLANNBASED)
knn_matches = matcher.knnMatch(desc1, desc2, 2)
# Filter matches using the Lowe's ratio test
good_matches = []
for m,n in knn_matches:
ratio_thresh = 0.7
if m.distance < ratio_thresh * n.distance:
good_matches.append(m)
return good_matches
def reprojectionError(matches, kpts1, kpts2, M):
ptsA = np.float32([ kpts1[m.queryIdx].pt for m in good_matches ])
ptsA_ = ptsA.reshape(-1,1,2)
ptsB = np.float32([ kpts2[m.trainIdx].pt for m in good_matches ])
ptsB_ = cv2.perspectiveTransform(ptsA_, M)
ptsB_ = ptsB_.reshape(ptsB.shape)
reproj_err = 0.
for i in range(len(ptsB)):
delx = ptsB[i][0] - ptsB_[i][0]
delx *= delx
dely = ptsB[i][1] - ptsB_[i][1]
dely *= dely
reproj_err += delx + dely
reproj_err = np.sqrt(reproj_err)
# print 'reprojection error:', reproj_err
reproj_err /= float(len(ptsB))
return reproj_err
def drawMatches(img1, img2, good_matches, kpts1, desc1, kpts2, desc2):
src_pts = np.float32([ kpts1[m.queryIdx].pt for m in good_matches ]).reshape(-1,1,2)
dst_pts = np.float32([ kpts2[m.trainIdx].pt for m in good_matches ]).reshape(-1,1,2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
if M is not None:
matchesMask = mask.ravel().tolist()
h,w = img1.shape[:2]
pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
dst = cv2.perspectiveTransform(pts,M)
dst += (w, 0) # adding offset
draw_params = dict(matchColor = (0,0,255), # draw matches in green color
singlePointColor = None,
matchesMask = matchesMask, # draw only inliers
flags = 2)
result = cv2.drawMatches(img1, kpts1, img2, kpts2, good_matches, None,**draw_params)
# Draw bounding box in Red
cv2.polylines(result, [np.int32(dst)], True, (0,0,255),3, cv2.LINE_AA)
displayImage(result, 'result')
return M
fd = {
'kaze': cv2.KAZE_create(),
'akaze': cv2.AKAZE_create()
}
key = 'akaze'
detect = 'path/to/cropped/template/of/the/object/to/be/detected'
target = 'path/to/target/image/where/the/object/to/be/detected'
template = cv2.imread(detect)
scene = cv2.imread(target)
# extract features form the template image
kpts1, desc1 = featureExtractor(template, fd[key])
# extract features form the scene image
kpts2, desc2 = featureExtractor(scene, fd[key])
good_matches = featureMatching(kpts1, desc1, kpts2, desc2, key)
if good_matches is not None:
M = drawMatches(scene, template, good_matches, kpts1, desc1, kpts2, desc2)
reprojection_error = reprojectionError(good_matches, kpts1, kpts2, M)
print(f'Reprojection error: {reprojection_error}')
На основании подходящих ключевых точек вы можете найти ограничивающий многоугольник объекта в изображение. Результат может быть дополнительно улучшен с помощью некоторой предварительной обработки изображений, такой как выравнивание гистограммы.
Результаты: