有没有办法更快地运行 OpenCV 的 SIFT?
Posted
技术标签:
【中文标题】有没有办法更快地运行 OpenCV 的 SIFT?【英文标题】:Is there a way to run OpenCV's SIFT faster? 【发布时间】:2020-07-07 01:47:06 【问题描述】:我有一个包含许多无法识别的重复图像的目录。我的目标是识别重复项。由于重复项已被裁剪、调整大小或转换为不同的图像格式,因此无法通过比较它们的哈希值来检测它们。
我编写了一个成功检测重复的脚本,但是有一个主要缺点:脚本很慢。在一个包含 60 个项目的文件夹的试驾中,运行了五个小时(这也可能反映了我越来越多的错误和缓慢的计算机)。由于我的目录中有大约 66,000 张图像,我估计脚本需要 229 天才能完成。
谁能提出解决方案?我的research 透露,您可以通过在循环完成时“释放”存储在变量中的图像来释放内存,但有关如何执行此操作的所有信息似乎都是用 C 语言而不是 python 编写的。我也在考虑尝试使用orb 而不是 sift,但担心它的准确性。有没有人建议这两种选择中的哪一种更好?或者一种重写脚本以减少内存的方法?非常感谢。
from __future__ import division
import cv2
import numpy as np
import glob
import pandas as pd
listOfTitles1 = []
listOfTitles2 = []
listOfSimilarities = []
# Sift and Flann
sift = cv2.xfeatures2d.SIFT_create()
index_params = dict(algorithm=0, trees=5)
search_params = dict()
flann = cv2.FlannBasedMatcher(index_params, search_params)
# Load all the images1
countInner = 0
countOuter = 1
folder = r"/Downloads/images/**/*"
for a in glob.iglob(folder,recursive=True):
for b in glob.iglob(folder,recursive=True):
if not a.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
continue
if not b.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
continue
if b.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
countInner += 1
print(countInner, "", countOuter)
if countInner <= countOuter:
continue
image1 = cv2.imread(a)
kp_1, desc_1 = sift.detectAndCompute(image1, None)
image2 = cv2.imread(b)
kp_2, desc_2 = sift.detectAndCompute(image2, None)
matches = flann.knnMatch(desc_1, desc_2, k=2)
good_points = []
if good_points == 0:
continue
for m, n in matches:
if m.distance < 0.6*n.distance:
good_points.append(m)
number_keypoints = 0
if len(kp_1) >= len(kp_2):
number_keypoints = len(kp_1)
else:
number_keypoints = len(kp_2)
percentage_similarity = float(len(good_points)) / number_keypoints * 100
listOfSimilarities.append(str(int(percentage_similarity)))
listOfTitles2.append(b)
listOfTitles1.append(a)
countInner = 0
if a.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
countOuter += 1
zippedList = list(zip(listOfTitles1,listOfTitles2, listOfSimilarities))
print(zippedList)
dfObj = pd.DataFrame(zippedList, columns = ['Original', 'Title' , 'Similarity'])
dfObj.to_csv(r"/Downloads/images/DuplicateImages3.csv")
【问题讨论】:
您可以在“for b in ...”循环之外提升处理图像 a 的代码。这会有所帮助,但不是您想要的那种性能提升。 您也可以尝试遍历所有图像一次,调用 sift.detectAndCompute 然后缓存结果。现在你要为每张图片多次调用 sift。 最后我怀疑你估计在 66000 张图像上运行这段代码需要 229 天过于乐观。此代码运行时间与您要比较的图像数量的平方成正比。 【参考方案1】:我认为通过简单的更改可以获得显着的性能提升:
-
首先,由于您对比较图像对感兴趣,因此您的循环可能如下所示:
files = ... # preload all file names with glob
for a_idx in range(len(files)):
for b_idx in range(a_idx, len(files)): # notice loop here
image_1 = cv2.imread(files[a_idx])
image_2 = cv2.imread(files[b_idx])
这会考虑所有对而不重复,例如(a, b) && (b, a)
-
其次,在比较每个 b 时,您不需要重新计算 a 的特征
for a_idx in range(len(files)):
image_1 = cv2.imread(files[a_idx])
kp_1, desc_1 = sift.detectAndCompute(image1, None) # never recoompute SIFT!
for b_idx in range(a_idx, len(files)):
image_2 = cv2.imread(files[b_idx])
kp_2, desc_2 = sift.detectAndCompute(image2, None)
-
我还会检查图像尺寸。我的猜测是有一些非常大的东西正在减慢你的内部循环。即使所有 60*60 == 3600 对也不应该花那么长时间。如果图像真的很大,您可以对其进行降采样以提高效率。
【讨论】:
非常感谢您为此所做的工作。我仍在研究如何将您的建议整合到我的脚本中。很快就会回复您。 感谢关于对大图像进行下采样和只计算一次筛选的提示。【参考方案2】:我在我的计算机上运行了您现有的实现,包含 100 张图像。该代码运行了 6 小时 31 分钟。然后我改变了实现,正如我在评论中建议的那样,只为每个图像计算一次 sift.detectAndCompute,缓存结果并在比较中使用缓存的结果。这将我的计算机上同一 100 张图像的执行时间从 6 小时 31 分减少到 6 分 29 秒。我不知道这对于您的所有图像来说是否足够快,但这是一个显着的减少。
在下面查看我修改后的实现。
from __future__ import division
import cv2
import numpy as np
import glob
import pandas as pd
listOfTitles1 = []
listOfTitles2 = []
listOfSimilarities = []
# Sift and Flann
sift = cv2.xfeatures2d.SIFT_create()
index_params = dict(algorithm=0, trees=5)
search_params = dict()
flann = cv2.FlannBasedMatcher(index_params, search_params)
# Load all the images1
countInner = 0
countOuter = 1
folder = r"/Downloads/images/**/*"
folder = "SiftImages/*"
siftOut =
for a in glob.iglob(folder,recursive=True):
if not a.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
continue
image1 = cv2.imread(a)
kp_1, desc_1 = sift.detectAndCompute(image1, None)
siftOut[a]=(kp_1,desc_1)
for a in glob.iglob(folder,recursive=True):
if not a.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
continue
(kp_1,desc_1) = siftOut[a]
for b in glob.iglob(folder,recursive=True):
if not b.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
continue
if b.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
countInner += 1
print(countInner, "", countOuter)
if countInner <= countOuter:
continue
#### image1 = cv2.imread(a)
#### kp_1, desc_1 = sift.detectAndCompute(image1, None)
####
#### image2 = cv2.imread(b)
#### kp_2, desc_2 = sift.detectAndCompute(image2, None)
(kp_2,desc_2) = siftOut[b]
matches = flann.knnMatch(desc_1, desc_2, k=2)
good_points = []
if good_points == 0:
continue
for m, n in matches:
if m.distance < 0.6*n.distance:
good_points.append(m)
number_keypoints = 0
if len(kp_1) >= len(kp_2):
number_keypoints = len(kp_1)
else:
number_keypoints = len(kp_2)
percentage_similarity = float(len(good_points)) / number_keypoints * 100
listOfSimilarities.append(str(int(percentage_similarity)))
listOfTitles2.append(b)
listOfTitles1.append(a)
countInner = 0
if a.lower().endswith(('.jpg','.png','.tif','.tiff','.gif')):
countOuter += 1
zippedList = list(zip(listOfTitles1,listOfTitles2, listOfSimilarities))
print(zippedList)
dfObj = pd.DataFrame(zippedList, columns = ['Original', 'Title' , 'Similarity'])
### dfObj.to_csv(r"/Downloads/images/DuplicateImages3.csv")
dfObj.to_csv(r"DuplicateImages3.2.csv")
【讨论】:
工作速度比原来的要快。以上是关于有没有办法更快地运行 OpenCV 的 SIFT?的主要内容,如果未能解决你的问题,请参考以下文章
尽管设置了标志 DOPENCV_ENABLE_NONFREE=ON,但运行 opencv 获得专利的 SIFT 和 SURF 的问题
如何使用 OpenCV 和 SIFT 查找我的训练图像的多个实例