我一直在尝试使用关键点和单词技术包在Python OpenCV 3.2.0中创建一个图像分类器。经过一番阅读后,我发现我可以按照以下方式执行此操作
我设法做了第1步和第2步,但却陷入了第3步和第4步。
我通过使用k-means聚类返回的标签成功生成了直方图(我认为)。但是,当我想使用未用于生成字典的新测试数据时,我得到了一些意想不到的结果。我尝试使用像tutorial这样的FLANN匹配器,但是从标签数据生成直方图得到的结果与FLANN匹配返回的数据不匹配。
我加载图片:
dictionary_size = 512
# Loading images
imgs_data = []
# imreads returns a list of all images in that directory
imgs = imreads(imgs_path)
for i in xrange(len(imgs)):
# create a numpy to hold the histogram for each image
imgs_data.insert(i, np.zeros((dictionary_size, 1)))
然后我创建了一个描述符数组(desc):
def get_descriptors(img, detector):
# returns descriptors of an image
return detector.detectAndCompute(img, None)[1]
# Extracting descriptors
detector = cv2.AKAZE_create()
desc = np.array([])
# desc_src_img is a list which says which image a descriptor belongs to
desc_src_img = []
for i in xrange(len(imgs)):
img = imgs[i]
descriptors = get_descriptors(img, detector)
if len(desc) == 0:
desc = np.array(descriptors)
else:
desc = np.vstack((desc, descriptors))
# Keep track of which image a descriptor belongs to
for j in range(len(descriptors)):
desc_src_img.append(i)
# important, cv2.kmeans only accepts type32 descriptors
desc = np.float32(desc)
然后使用k-means对描述符进行聚类:
# Clustering
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 0.01)
flags = cv2.KMEANS_PP_CENTERS
# desc is a type32 numpy array of vstacked descriptors
compactness, labels, dictionary = cv2.kmeans(desc, dictionary_size, None, criteria, 1, flags)
然后我使用从k-means返回的标签为每个图像创建直方图:
# Getting histograms from labels
size = labels.shape[0] * labels.shape[1]
for i in xrange(size):
label = labels[i]
# Get this descriptors image id
img_id = desc_src_img[i]
# imgs_data is a list of the same size as the number of images
data = imgs_data[img_id]
# data is a numpy array of size (dictionary_size, 1) filled with zeros
data[label] += 1
ax = plt.subplot(311)
ax.set_title("Histogram from labels")
ax.set_xlabel("Visual words")
ax.set_ylabel("Frequency")
ax.plot(imgs_data[0].ravel())
然后我尝试在同一图像上做同样的事情,但使用FLANN:
matcher = cv2.FlannBasedMatcher_create()
matcher.add(dictionary)
matcher.train()
descriptors = get_descriptors(imgs[0], detector)
result = np.zeros((dictionary_size, 1), np.float32)
# flan matcher needs descriptors to be type32
matches = matcher.match(np.float32(descriptors))
for match in matches:
visual_word = match.trainIdx
result[visual_word] += 1
ax = plt.subplot(313)
ax.set_title("Histogram from FLANN")
ax.set_xlabel("Visual words")
ax.set_ylabel("Frequency")
ax.plot(result.ravel())
这会输出像这样的直方图,它的分布非常不均匀,与第一个直方图不匹配。
您可以在GitHub上查看完整的代码和图片。改变" imgs_path" (第20行)在运行之前带有图像的目录。
我哪里错了?为什么直方图如此不同?如何使用字典生成新数据的直方图?
作为附注,我尝试使用OpenCV BOW实现,但发现了另一个问题,它给出了错误:" _queryDescriptors.type()== trainDescType in function cv :: BFMatcher :: knnMatchImpl "这就是为什么我自己试图实现它。如果有人可以使用Python OpenCV BOW和AKAZE提供一个工作示例那么那就好了。
答案 0 :(得分:1)
似乎你无法使用字典训练FlannBasedMatcher,如下图所示:
matcher = cv2.FlannBasedMatcher_create()
matcher.add(dictionary)
matcher.train()
但是,您可以在匹配时传递dictionary
:
matcher = cv2.FlannBasedMatcher_create()
...
matches = matcher.match(np.float32(descriptors), dictionary)
我不完全确定为什么会这样。或许train
方法只是由match
方法使用,如post中暗示的那样。
同样根据opencv docs,match
的参数为:
- queryDescriptors - 查询描述符集。
- trainDescriptors - 训练描述符集。此集不会添加到存储在类对象中的train描述符集合中。
- 匹配 - 匹配。如果在掩码中屏蔽了查询描述符,则不会为此描述符添加匹配项。因此,匹配大小可能小于查询描述符计数。
所以我猜你应该将dictionary
作为trainDescriptors
传递,因为它就是这样。
如果有人能够对此有所了解,我们将不胜感激。
以下是使用上述方法后的结果:
您可以看到完整更新的代码here。