检查是否有与输入图像完全相同的图像

Div*_*ent 4 python compare subtraction

我想知道如何在海量数据中查找图像(文件夹中有很多图像)并且我想查找与输入图像完全相同的图像(假设来自另一个文件夹中的输入图像不在数据中)文件夹)并将输入图像与所有海量数据进行比较,如果找到完全相同的图像,则显示其名称作为输出(文件夹中相同图像的名称,而不是输入名称)(例如:dafs.jpg

使用蟒蛇

我正在考虑比较 RGB 像素的确切值并从文件夹中的每个图像中减去输入图像的像素

但我不知道如何在 python 中做到这一点

Har*_*wis 5

比较 RGB 像素值

您可以使用枕头模块来访问特定图像的像素数据。请记住,pillow支持这些图像格式

如果我们根据您的描述对 2 个图像相同意味着什么做出一些假设,则两个图像必须:

  • 具有相同的尺寸(高度和宽度)
  • 具有相同的RGB像素值(输入图像中像素[x,y]的RGB值必须与输出图像中像素[x,y]的RGB值相同)
  • 具有相同的方向(与之前的假设相关,与旋转 90 度的同一图像相比,图像被认为不相同)

那么如果我们有 2 张使用该pillow模块的图像

from PIL import Image

original = Image.open("input.jpg")
possible_duplicate = Image.open("output.jpg")
Run Code Online (Sandbox Code Playgroud)

以下代码将能够比较 2 个图像以查看它们是否相同

def compare_images(input_image, output_image):
  # compare image dimensions (assumption 1)
  if input_image.size != output_image.size:
    return False

  rows, cols = input_image.size

  # compare image pixels (assumption 2 and 3)
  for row in range(rows):
    for col in range(cols):
      input_pixel = input_image.getpixel((row, col))
      output_pixel = output_image.getpixel((row, col))
      if input_pixel != output_pixel:
        return False

  return True
Run Code Online (Sandbox Code Playgroud)

通过调用

compare_images(original, possible_duplicate)
Run Code Online (Sandbox Code Playgroud)

使用这个函数,我们可以浏览一组图像

from PIL import Image

def find_duplicate_image(input_image, output_images):
  # only open the input image once
  input_image = Image.open(input_image)

  for image in output_images:
    if compare_images(input_image, Image.open(image)):
      return image
Run Code Online (Sandbox Code Playgroud)

把它们放在一起,我们可以简单地调用

original = "input.jpg"
possible_duplicates = ["output.jpg", "output2.jpg", ...]

duplicate = find_duplicate_image(original, possible_duplicates)
Run Code Online (Sandbox Code Playgroud)

请注意,上面的实现只会找到第一个重复项,并返回它。如果没有发现重复,None将被退回。

要记住的一件事是,像这样对每个像素进行比较可能代价高昂。我使用此图像compare_images使用timeit模块将其用作输入和输出 100 次,并取所有这些运行的平均值

num_trials = 100
trials = timeit.repeat(
    repeat=num_trials,
    number=1,
    stmt="compare_images(Image.open('input.jpg'), Image.open('input.jpg'))",
    setup="from __main__ import compare_images; from PIL import Image"
)
avg = sum(trials) / num_trials

print("Average time taken per comparison was:", avg, "seconds")

# Average time taken per comparison was 1.3337286046380177 seconds
Run Code Online (Sandbox Code Playgroud)

请注意,这是在只有 600 x 600 像素的图像上完成的。如果您使用一组“大量”可能的重复图像进行此操作,我将“大量”表示至少有 100 万张相似尺寸的图像,这可能需要大约 15 天(1,000,000 * 1.28 秒/60 秒/60 分钟) / 24 小时)来遍历并将每个输出图像与输入进行比较,这并不理想。

另请记住,这些指标会因您使用的机器和操作系统而异。我提供的数字更多是为了说明目的。

替代实现

虽然我自己还没有完全探索过这个实现,但您可以尝试的一种方法是使用哈希函数预先计算集合中每个图像的像素数据的哈希值。如果您将这些存储在数据库中,每个散列都包含指向原始图像或图像名称的链接,那么您所要做的就是使用相同的散列函数计算输入图像的散列,然后比较散列。这将花费大量的计算时间,并且会产生更有效的算法。

这篇博文描述了一种实现此目的的实现。

更新 - 2018-08-06

根据OP的要求,如果您获得了可能重复图像的目录而不是显式图像路径本身,那么您可以像这样使用osntpath模块

import ntpath
import os

def get_all_images(directory):
  image_paths = []

  for filename in os.listdir(directory):
    # to be as careful as possible, you might check to make sure that
    # the file is in fact an image, for instance using
    # filename.endswith(".jpg") to check for .jpg files for instance
    image_paths.append("{}/{}".format(directory, filename))

  return image_paths

def get_filename(path):
  return ntpath.basename(path)
Run Code Online (Sandbox Code Playgroud)

使用这些函数,更新后的程序可能看起来像

possible_duplicates = get_all_images("/path/to/images")
duplicate_path = find_duplicate_image("/path/to/input.jpg", possible_duplicates)
if duplicate_path:
  print(get_filename(duplicate_path))
Run Code Online (Sandbox Code Playgroud)

如果有重复,上面只会打印重复图像的名称,否则不会打印任何内容。