有没有更好的算法来比较每个图像与文件夹中的其他图像?

Posted

技术标签:

【中文标题】有没有更好的算法来比较每个图像与文件夹中的其他图像?【英文标题】:Is there any better algorithms to compare each image, with other images within the folder? 【发布时间】:2021-04-26 01:38:36 【问题描述】:

我正在使用 Python 3.8.1 创建工具来组织我的图像。其中一种工具是检测和分离相似的图像。因此,我需要一种算法来将每个图像与该文件夹中的所有其他图像进行比较,更准确地说,我需要一个更好的算法来将每个图像与该文件夹中的所有其他图像进行比较。 我解决这个问题的方法是这样的: 让我们以 6 张图像为例。我们将图像命名为 1、2、3、4、5 和 6。这是每个图像与该文件夹中所有图像之间所有可能的比较: 1 -> 1   2 -> 1   3 -> 1   4 -> 1   5 -> 1   6 -> 1 1 -> 2   2 -> 2   3 -> 2   4 -> 2   5 -> 2   6 -> 2 1 -> 3   2 -> 3   3 -> 3   4 -> 3   5 -> 3   6 -> 3 1 -> 4   2 -> 4   3 -> 4   4 -> 4   5 -> 4   6 -> 4 1 -> 5   2 -> 5   3 -> 5   4 -> 5   5 -> 5   6 -> 5 1 -> 6   2 -> 6   3 -> 6   4 -> 6   5 -> 6   6 -> 6 有 6 * 6 = 36 次比较。 接下来,因为我们正在比较每张图像以找到相似的图像,所以排除自身之间的比较是合乎逻辑的,因此我们需要删除比较 1 -> 1、2 -> 2 等。我们还需要排除两次比较图像,例如比较 1 -> 2,然后再比较 2 -> 1。从逻辑上讲,比较“我”和“你”,比较“你”和“我”有什么区别。如果“我”真的和“你”不一样,那你就不用再比较了。 因此,其余的比较是: 1 -> 2   1 -> 3   2 -> 3   1 -> 4   2 -> 4   3 -> 4   1 -> 5   2 -> 5   3 -> 5   4 -> 5 1 -> 6   2 -> 6   3 -> 6   4 -> 6   5 -> 6 这将要比较的图像总数减少到 5 + 4 + 3 + 2 + 1 = 15 次比较,不到原始图像的一半。 我使用一种方法来实现这一点,该方法获取该文件夹中所有图像的列表,然后根据上述逻辑返回两个图像对的列表。方法如下:

def get_cmpr_pairs_list( self ):
    cmprPairsList = []
    for i in range(0, self.imgCount):
        for j in range(i+1, self.imgCount):
            cmprPairsList.append( [self.filenames[i], self.filenames[j]] )
    return cmprPairsList

看到这还不够,我使用multiprocessing 模块将这些图像对与我所有的 CPU 内核(即 8 核 CPU)进行比较的任务分开。这是我创建的用于比较文件夹中所有图像的方法:

def find_similars_all( self ):
    print("Finding similar images...\n")

    similarImg = []         # a list that stores similar images
    cmprPairsList = self.get_cmpr_pairs_list()       # get the comparison pairs list
    self.cmprPairsCount = len(cmprPairsList)

    # create a multiprocess pool
    with multiprocessing.get_context("spawn").Pool() as p :
        print("Total images to compare :  images\n".format(self.cmprPairsCount))
        # find all similar image and add it to similarImg
        similarImg = p.map(self.compare_img, cmprPairsList)
        # remove all None from the list
        similarImg = list(filter(None, similarImg))
        # melt all the list result into a single list
        # from [[], [], ...] into [ ... ]
        similarImg = itertools.chain.from_iterable(similarImg)
        # remove duplicates
        similarImg = list(dict.fromkeys(similarImg))

    # if we have found some similar images...
    if similarImg:
        self.move_filenames(similarImg, "Similars")
    else :
        print("\nNo similar images found.")

    print("\nDone searching for similar images!")
    self.update_filenames()

最后一个,这是每个进程将运行以比较每个图像的方法。如果图像对相似,它将返回这对图像:

def compare_img( self, imagePair ):
    # get the pair of image filename that we want to compare
    imgA, imgB = imagePair[0], imagePair[1]
    imgA = os.path.join(self.dirname, imgA)
    imgB = os.path.join(self.dirname, imgB)
    
    # ------ just making a counter for the image comparisons ------
    global counter
    counter.increment()
    print("/ images compared...".format(counter.value(), self.cmprPairsCount), end="\r")
    # ------ just making a counter for the image comparisons ------

    # ---- comparison algorithm starts here ----
    # add the threshold
    threshold = 1 - self.similarity_percentage/100
    diff_limit = int(threshold*(self.hash_size**2))
    
    # create the average hash of the image
    with Image.open(imgA) as img:
        hash1 = imagehash.average_hash(img, self.hash_size).hash
    
    with Image.open(imgB) as img:
        hash2 = imagehash.average_hash(img, self.hash_size).hash
    
    result = np.count_nonzero(hash1 != hash2) <= diff_limit
    # ---- comparison algorithm stops here ----

    # this part will conclude whether the two image is similar or not
    if result:
        print(" image found % similar to ".format(imgA, self.similarity_percentage, imgB))
        return imagePair

我使用 750 张图像测试了这个算法,总共产生了 280,875 次比较,我认为总时间为 30 分钟到 1 小时。我的 CPU 使用率一直是 100%,这让我觉得这个东西可以作为我的 CPU 的压力测试大声笑。此外,我通过将图像文件存储在 RAMDisk 上来严格限制此测试,因此 Python 在打开文件时不会出现问题。我认为,唯一的瓶颈是比较总数。 回到我的主要问题,有什么办法可以改善吗?比如用一些特殊的算法减少比较的次数,或者更快地比较图像,或者任何可以进一步改进的东西。但是,我更感兴趣的是找到可以将每个图像与文件夹中的其他图像进行比较的其他算法。 我将非常感谢您的帮助。谢谢!

【问题讨论】:

你能告诉更多关于这条线在做什么 `hash1 = imagehash.average_hash(img, self.hash_size).hash` 吗? 为什么不先计算所有哈希,然后检查不同哈希的数量? @Surt - 关于哈希或比较算法,我从网站上复制了它,所以我不知道它如何得出图像相似与否的基本知识。但我记得它的工作原理是取图像的平均哈希值,然后对这些值进行 xor-ing(?)(我不记得了)。 @Abhinav Mathur,我想我可以尝试通过散列所有图像,将这些散列存储在图像对列表中,然后让 cpu 计算每对散列之间的差异。 你不需要比较,因为字典将只存储一个计算哈希的图像 id 【参考方案1】:

不要为每次比较计算哈希值。打开每个文件,计算哈希,存储它。然后比较所有组合的存储哈希。

【讨论】:

是的,我想我可以试试。计算所有哈希值,成对存储,然后让 CPU 计算所有这些哈希值对。我会让你知道结果如何 感谢您的建议!操作从 1 小时到使用同一组图像的瞬间。我从没想过它会这么快 LOL。 不客气。这是优化时要做的第一件事 - 搜索已完成的重复工作并思考如何只做一次。

以上是关于有没有更好的算法来比较每个图像与文件夹中的其他图像?的主要内容,如果未能解决你的问题,请参考以下文章

将一个图像中的 SURF 描述符与其他图像中的描述符列表进行比较

从图像中解析字符以进行 OCR 的算法

二维码图像矫正增强基于MATLAB的二维码图像矫正增强处理仿真

图像配准的恶魔算法(用于假人)

在 C# 中比较两个图像的算法

图像分析用 OpenCV 与 Skimage,哪一个更好?