有没有办法更快地运行 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 查找我的训练图像的多个实例

OpenCV实现SIFT图像拼接源代码

OpenCV 3 中 SIFT 的示例代码?

OpenCV 例程 300篇242. 加速稳健特征检测算法(SURF)

OpenCV 例程 300篇242. 加速稳健特征检测算法(SURF)