#python #opencv #computer-vision #template-matching #orb
#python #opencv #компьютерное зрение #соответствие шаблону #сфера
Вопрос:
Я пытаюсь реализовать выравнивание изображения сферы Python (3.7) OpenCV (3.4.3). Обычно я выполняю большую часть своей обработки с помощью ImageMagick. Но мне нужно выполнить выравнивание изображения, и я пытаюсь использовать Python OpenCV ORB. Мой сценарий основан на одном из учебников Сатьи Маллика по изучению OpenCV наhttps://www.learnopencv.com/image-alignment-feature-based-using-opencv-c-python /.
Однако я пытаюсь изменить его, чтобы использовать жесткое выравнивание, а не соответствие перспективы, и фильтровать точки с помощью маски, чтобы ограничить разницу в значениях y, поскольку изображения уже почти выровнены.
Подход с маской был взят из кода выравнивания ФЛАННА в последнем примере на https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_feature2d/py_matcher/py_matcher.html .
Мой скрипт работает нормально, если я удалю matchesMask, который должен обеспечивать фильтрацию точек. (У меня есть два других рабочих скрипта. Один похож, но просто фильтрует точки и игнорирует маску. Другая основана на алгоритме ECC.)
Тем не менее, я хотел бы понять, почему мой приведенный ниже код не работает.
Возможно, структура моей маски неверна в текущих версиях Python Opencv?
Ошибка, которую я получаю, заключается в:
Traceback (most recent call last):
File "warp_orb_rigid2_filter.py", line 92, in <module>
imReg, m = alignImages(im, imReference)
File "warp_orb_rigid2_filter.py", line 62, in alignImages
imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None, **draw_params)
SystemError: <built-in function drawMatches> returned NULL without setting an error
Вот мой код. Первая стрелка показывает, где создается маска. Вторая стрелка показывает строку, которую я должен удалить, чтобы заставить скрипт работать. Но тогда это игнорирует мою фильтрацию точек.
#!/bin/python3.7
import cv2
import numpy as np
MAX_FEATURES = 500
GOOD_MATCH_PERCENT = 0.15
def alignImages(im1, im2):
# Convert images to grayscale
im1Gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY)
im2Gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY)
# Detect ORB features and compute descriptors.
orb = cv2.ORB_create(MAX_FEATURES)
keypoints1, descriptors1 = orb.detectAndCompute(im1Gray, None)
keypoints2, descriptors2 = orb.detectAndCompute(im2Gray, None)
# Match features.
matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING)
matches = matcher.match(descriptors1, descriptors2, None)
# Sort matches by score
matches.sort(key=lambda x: x.distance, reverse=False)
# Remove not so good matches
numGoodMatches = int(len(matches) * GOOD_MATCH_PERCENT)
matches = matches[:numGoodMatches]
# Extract location of good matches and filter by diffy
points1 = np.zeros((len(matches), 2), dtype=np.float32)
points2 = np.zeros((len(matches), 2), dtype=np.float32)
for i, match in enumerate(matches):
points1[i, :] = keypoints1[match.queryIdx].pt
points2[i, :] = keypoints2[match.trainIdx].pt
# initialize empty arrays for newpoints1 and newpoints2 and mask
newpoints1 = np.empty(shape=[0, 2])
newpoints2 = np.empty(shape=[0, 2])
matches_Mask = [0] * len(matches)
# filter points by using mask
for i in range(len(matches)):
pt1 = points1[i]
pt2 = points2[i]
pt1x, pt1y = zip(*[pt1])
pt2x, pt2y = zip(*[pt2])
diffy = np.float32( np.float32(pt2y) - np.float32(pt1y) )
print(diffy)
if abs(diffy) < 10.0:
newpoints1 = np.append(newpoints1, [pt1], axis=0)
newpoints2 = np.append(newpoints2, [pt2], axis=0)
matches_Mask[i]=[1,0] #<--- mask created
print(matches_Mask)
draw_params = dict(matchColor = (255,0,),
singlePointColor = (255,255,0),
matchesMask = matches_Mask, #<---- remove mask here
flags = 0)
# Draw top matches
imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None, **draw_params)
cv2.imwrite("/Users/fred/desktop/lena_matches.png", imMatches)
# Find Affine Transformation
# true means full affine, false means rigid (SRT)
m = cv2.estimateRigidTransform(newpoints1,newpoints2,False)
# Use affine transform to warp im1 to match im2
height, width, channels = im2.shape
im1Reg = cv2.warpAffine(im1, m, (width, height))
return im1Reg, m
if __name__ == '__main__':
# Read reference image
refFilename = "/Users/fred/desktop/lena.png"
print("Reading reference image : ", refFilename)
imReference = cv2.imread(refFilename, cv2.IMREAD_COLOR)
# Read image to be aligned
imFilename = "/Users/fred/desktop/lena_r1.png"
print("Reading image to align : ", imFilename);
im = cv2.imread(imFilename, cv2.IMREAD_COLOR)
print("Aligning images ...")
# Registered image will be stored in imReg.
# The estimated transform will be stored in m.
imReg, m = alignImages(im, imReference)
# Write aligned image to disk.
outFilename = "/Users/fred/desktop/lena_r1_aligned.jpg"
print("Saving aligned image : ", outFilename);
cv2.imwrite(outFilename, imReg)
# Print estimated homography
print("Estimated Affine Transform : n", m)
Вот мои два изображения: lena и lena, повернутые на 1 градус. Обратите внимание, что это не мои реальные изображения. У этих изображений нет различий в значениях > 10, но у моих реальных изображений есть.
Я пытаюсь выровнять и деформировать повернутое изображение, чтобы оно соответствовало исходному изображению lena.
Ответ №1:
Способ, которым вы создаете маску, неверен. Это должен быть только список с одиночными номерами, где каждый номер сообщал бы вам, хотите ли вы использовать это конкретное соответствие функций.
Поэтому замените эту строку:
matches_Mask = [[0,0] for i in range(len(matches))]
С помощью этого:
matches_Mask = [0] * len(matches)
… итак:
# matches_Mask = [[0,0] for i in range(len(matches))]
matches_Mask = [0] * len(matches)
Это создает список из 0, длина которого равна количеству совпадений. Наконец, вам нужно изменить запись в маску с одним значением:
if abs(diffy) < 10.0:
#matches_Mask[i]=[1,0] #<--- mask created
matches_Mask[i] = 1
Я, наконец, понял это:
Estimated Affine Transform :
[[ 1.00001187 0.01598318 -5.05963793]
[-0.01598318 1.00001187 -0.86121051]]
Обратите внимание, что формат маски отличается в зависимости от того, какой сопоставитель вы используете. В этом случае вы используете сопоставление методом грубой силы, поэтому маска должна быть в формате, который я только что описал.
Если вы использовали, например, FLANN’s knnMatch
, то это будет вложенный список списков, где каждый элемент представляет собой k
длинный список. Например, если у вас было k=3
и пять ключевых точек, это будет список длиной в пять элементов, причем каждый элемент представляет собой список из трех элементов. Каждый элемент во вложенном списке определяет, какое соответствие вы хотите использовать для рисования.
Комментарии:
1.
@rayryeng
Большое спасибо. Но, пожалуйста, объясните, почему маска не [1,0], как в другой ссылке. Связано ли это с разной структурой маски между моим описанием matcher_create () и другим ссылочным использованием flann.knnMatch()? Есть ли какая-нибудь ссылка, объясняющая структуру маски для разных сопоставителей? Также в приведенном выше примере я использовал два простых изображения вместо моих реальных изображений, которые я не могу показать здесь, но для которых у него были разные значения> 10. Я отредактирую свой пост, чтобы лучше объяснить это.2. Да, это именно так. Перебор и kNN имеют разные структуры. При использовании kNN потребуется использовать вашу оригинальную реализацию. У меня также нет ссылки на структуру маски…. Это приходит из опыта.
3. Я также не знал, что вы используете FLANN. В вашем коде используется сопоставление методом грубой силы, поэтому маска представляет собой просто один список. Для КНН соответствуя, если вы используете
k=2
, то это будет вложенный список списков, где каждый элемент списка,k
долго. Например, если у вас былоk=5
и три совпадения, это будет список длиной в три элемента, причем каждый элемент представляет собой список из 5 элементов. Каждый элемент во вложенном списке определяет, какое соответствие вы хотите использовать для рисования.4.
@rayryeng
. На самом деле я не использовал FLANN, а просто попытался эмулировать структуру маскировки, выполненную в примере FLANN. Таким образом, структура маски действительно меняется при сопоставлении (потенциально, и, по крайней мере, FLANN отличается). Спасибо за это разъяснение. Я изменил свой скрипт, как вы написали, и теперь он работает должным образом даже для моих реальных изображений. из-за фильтрации diffy выбрасывается 13 баллов, и маска теперь хорошо с этим справляется. Начинающий пользователь Python / OpenCV высоко ценит вашу помощь и опыт.5. @fmw42 Я понял. Хорошо, итак, если вы решите использовать FLANNВ следующий раз, вы знаете, как структурировать маску! Рад, что помог, и, пожалуйста, дайте мне знать, если у вас возникнут еще какие-либо вопросы или вам понадобится дополнительная помощь!