我希望创建一个图像基表,然后将任何新图像与之进行比较,以确定新图像是否与基表完全相同(或接近)。

例如:如果你想减少100次相同图像的存储,你可以存储它的一个副本,并提供它的参考链接。当输入一个新图像时,你想要与现有的图像进行比较,以确保它不是重复的…想法吗?

我的一个想法是缩小到一个小缩略图,然后随机选择100个像素位置并进行比较。


当前回答

如果您有大量的图像,请查看Bloom过滤器,它使用多个散列来获得概率高但效率高的结果。如果图像的数量不是很大,那么像md5这样的加密散列应该足够了。

其他回答

我认为值得在此基础上添加我构建的phash解决方案,我们已经使用了一段时间:Image:: phash。它是一个Perl模块,但主要部分是用c语言编写的。它比phash.org快几倍,并且为基于dct的phash提供了一些额外的特性。

我们已经在MySQL数据库上建立了数以千万计的图像索引,所以我想要一些快速的东西,也想要一种使用MySQL索引的方法(这与汉明距离不工作),这导致我使用“减少”哈希进行直接匹配,模块文档讨论了这一点。

使用起来很简单:

use Image::PHash;

my $iph1 = Image::PHash->new('file1.jpg');
my $p1   = $iph1->pHash();

my $iph2 = Image::PHash->new('file2.jpg');
my $p2   = $iph2->pHash();

my $diff = Image::PHash::diff($p1, $p2);

我们笼统地称之为副本的东西,算法很难识别。 你的副本可以是:

确切的副本 接近精确重复。(图像的轻微编辑等) 重复(相同的内容,但不同的视角,相机等)

第一个和第二个更容易解决。3号。是非常主观的,仍然是一个研究课题。 我可以提供1号和2号的解决方案。 这两个解决方案都使用了优秀的图像哈希-哈希库:https://github.com/JohannesBuchner/imagehash

确切的副本 使用感知哈希度量可以找到精确的重复项。 phash库在这方面做得很好。我经常用它来清洁 训练数据。 用法(来自github网站)简单如:

from PIL import Image
import imagehash

# image_fns : List of training image files
img_hashes = {}

for img_fn in sorted(image_fns):
    hash = imagehash.average_hash(Image.open(image_fn))
    if hash in img_hashes:
        print( '{} duplicate of {}'.format(image_fn, img_hashes[hash]) )
    else:
        img_hashes[hash] = image_fn

接近精确复制 在这种情况下,您必须设置一个阈值,并比较它们之间距离的哈希值 其他。这必须通过对图像内容的反复试验来完成。

from PIL import Image
import imagehash

# image_fns : List of training image files
img_hashes = {}
epsilon = 50

for img_fn1, img_fn2 in zip(image_fns, image_fns[::-1]):
    if image_fn1 == image_fn2:
        continue

    hash1 = imagehash.average_hash(Image.open(image_fn1))
    hash2 = imagehash.average_hash(Image.open(image_fn2))
    if hash1 - hash2 < epsilon:
        print( '{} is near duplicate of {}'.format(image_fn1, image_fn2) )

下面是解决这个问题的三种方法(还有很多其他方法)。

The first is a standard approach in computer vision, keypoint matching. This may require some background knowledge to implement, and can be slow. The second method uses only elementary image processing, and is potentially faster than the first approach, and is straightforward to implement. However, what it gains in understandability, it lacks in robustness -- matching fails on scaled, rotated, or discolored images. The third method is both fast and robust, but is potentially the hardest to implement.

关键点匹配

Better than picking 100 random points is picking 100 important points. Certain parts of an image have more information than others (particularly at edges and corners), and these are the ones you'll want to use for smart image matching. Google "keypoint extraction" and "keypoint matching" and you'll find quite a few academic papers on the subject. These days, SIFT keypoints are arguably the most popular, since they can match images under different scales, rotations, and lighting. Some SIFT implementations can be found here.

关键点匹配的一个缺点是简单实现的运行时间:O(n^2m),其中n是每张图像中的关键点数量,m是数据库中的图像数量。一些聪明的算法可能会更快地找到最接近的匹配,比如四叉树或二进制空间分区。


备选方案:直方图法

Another less robust but potentially faster solution is to build feature histograms for each image, and choose the image with the histogram closest to the input image's histogram. I implemented this as an undergrad, and we used 3 color histograms (red, green, and blue), and two texture histograms, direction and scale. I'll give the details below, but I should note that this only worked well for matching images VERY similar to the database images. Re-scaled, rotated, or discolored images can fail with this method, but small changes like cropping won't break the algorithm

Computing the color histograms is straightforward -- just pick the range for your histogram buckets, and for each range, tally the number of pixels with a color in that range. For example, consider the "green" histogram, and suppose we choose 4 buckets for our histogram: 0-63, 64-127, 128-191, and 192-255. Then for each pixel, we look at the green value, and add a tally to the appropriate bucket. When we're done tallying, we divide each bucket total by the number of pixels in the entire image to get a normalized histogram for the green channel.

For the texture direction histogram, we started by performing edge detection on the image. Each edge point has a normal vector pointing in the direction perpendicular to the edge. We quantized the normal vector's angle into one of 6 buckets between 0 and PI (since edges have 180-degree symmetry, we converted angles between -PI and 0 to be between 0 and PI). After tallying up the number of edge points in each direction, we have an un-normalized histogram representing texture direction, which we normalized by dividing each bucket by the total number of edge points in the image.

为了计算纹理尺度直方图,对于每个边缘点,我们测量到具有相同方向的下一个最近边缘点的距离。例如,如果边缘点A的方向是45度,算法就会沿着这个方向走,直到找到另一个方向为45度的边缘点(或在合理偏差范围内)。在计算每个边缘点的距离后,我们将这些值转储到直方图中,并通过除以边缘点的总数来归一化。

现在每张图像有5个直方图。要比较两张图像,需要取每个直方图桶之间差值的绝对值,然后将这些值相加。例如,为了比较图像A和B,我们将计算

|A.green_histogram.bucket_1 - B.green_histogram.bucket_1| 

对于绿色直方图中的每个桶,并对其他直方图重复,然后将所有结果相加。结果越小,匹配越好。对数据库中的所有图像重复此操作,结果最小的匹配者获胜。您可能希望有一个阈值,超过这个阈值,算法就会得出没有找到匹配的结论。


第三个选择-关键点+决策树

A third approach that is probably much faster than the other two is using semantic texton forests (PDF). This involves extracting simple keypoints and using a collection decision trees to classify the image. This is faster than simple SIFT keypoint matching, because it avoids the costly matching process, and keypoints are much simpler than SIFT, so keypoint extraction is much faster. However, it preserves the SIFT method's invariance to rotation, scale, and lighting, an important feature that the histogram method lacked.

更新:

我的错误——语义德克顿森林论文并不是专门关于图像匹配的,而是关于区域标记的。关于匹配的原始论文是:使用随机树的关键点识别。此外,下面的论文继续发展的想法,并代表了艺术的状态(c. 2010):

快速关键点识别使用随机蕨类-更快,更可扩展比Lepetit 06 概要:二进制健壮的独立基本特征-不太健壮但非常快-我认为这里的目标是在智能手机和其他手持设备上进行实时匹配

我的公司每个月有大约2400万张来自制造商的图片。我正在寻找一个快速的解决方案,以确保我们上传到我们的目录的图像是新的图像。

I want to say that I have searched the internet far and wide to attempt to find an ideal solution. I even developed my own edge detection algorithm. I have evaluated speed and accuracy of multiple models. My images, which have white backgrounds, work extremely well with phashing. Like redcalx said, I recommend phash or ahash. DO NOT use MD5 Hashing or anyother cryptographic hashes. Unless, you want only EXACT image matches. Any resizing or manipulation that occurs between images will yield a different hash.

对于phash/ahash,查看这个:imagehash

我想通过发布我的代码和准确性来扩展*redcalx的帖子。

工作内容:

from PIL import Image
from PIL import ImageFilter
import imagehash

img1=Image.open(r"C:\yourlocation")
img2=Image.open(r"C:\yourlocation")
if img1.width<img2.width:
    img2=img2.resize((img1.width,img1.height))
else:
    img1=img1.resize((img2.width,img2.height))
img1=img1.filter(ImageFilter.BoxBlur(radius=3))
img2=img2.filter(ImageFilter.BoxBlur(radius=3))
phashvalue=imagehash.phash(img1)-imagehash.phash(img2)
ahashvalue=imagehash.average_hash(img1)-imagehash.average_hash(img2)
totalaccuracy=phashvalue+ahashvalue

以下是我的一些结果:

item1  item2  totalsimilarity
desk1  desk1       3
desk1  phone1     22
chair1 desk1      17
phone1 chair1     34

希望这能有所帮助!

这篇文章是我解决方案的起点,这里有很多好主意,所以我想分享我的结果。主要的见解是,我已经找到了一种方法,通过利用phash的速度来解决基于关键点的图像匹配的缓慢问题。

对于一般的解决方案,最好采用几种策略。每种算法都最适合于某些类型的图像转换,您可以利用这一点。

最上面是最快的算法;底部最慢(虽然更准确)。如果在更快的级别上找到了一个很好的匹配,您可能会跳过慢的级别。

基于文件哈希(md5,sha1等)的精确副本 用于缩放图像的感知哈希(phash) 用于修改图像的基于特征的(SIFT)

我的phash治疗效果很好。该方法对缩放后的图像具有较好的精度。它不适用于(感知上)修改过的图像(裁剪、旋转、镜像等)。为了处理散列速度,我们必须使用磁盘缓存/数据库来维护干草堆的散列。

phash真正的好处是,一旦你建立了哈希数据库(对我来说大约是1000张图片/秒),搜索可以非常非常快,特别是当你可以把整个哈希数据库保存在内存中时。这是相当实用的,因为哈希只有8个字节。

例如,如果您有100万张图像,则需要100万64位哈希值(8 MB)的数组。在某些cpu上,这适用于L2/L3缓存!在实际使用中,我看到corei7的速度超过1千兆哈姆/秒,这只是CPU内存带宽的问题。一个10亿张图片的数据库在64位CPU(需要8GB内存)上是可行的,搜索不会超过1秒!

For modified/cropped images it would seem a transform-invariant feature/keypoint detector like SIFT is the way to go. SIFT will produce good keypoints that will detect crop/rotate/mirror etc. However the descriptor compare is very slow compared to hamming distance used by phash. This is a major limitation. There are a lot of compares to do, since there are maximum IxJxK descriptor compares to lookup one image (I=num haystack images, J=target keypoints per haystack image, K=target keypoints per needle image).

为了解决速度问题,我尝试在每个找到的关键点周围使用phash,使用特征大小/半径来确定子矩形。使此工作良好的技巧是增加/缩小半径以生成不同的子矩形水平(在针图像上)。通常情况下,第一个级别(未缩放)将匹配,但通常需要更多。我不是100%确定为什么这是有效的,但我可以想象它可以实现对phash来说太小的功能(phash将图像缩小到32x32)。

另一个问题是SIFT不能最优地分配关键点。如果图像中有一个区域有很多边缘,那么关键点就会聚集在那里,而在其他区域则不会出现任何边缘。我在OpenCV中使用GridAdaptedFeatureDetector来改进分发。不知道什么网格大小是最好的,我使用一个小网格(1x3或3x1取决于图像方向)。

你可能想要在特征检测之前将所有的草堆图像(和针)缩放到更小的尺寸(我在最大尺寸上使用210px)。这将减少图像中的噪声(一直是计算机视觉算法的一个问题),也将探测器聚焦在更突出的特征上。

对于人物图像,您可以尝试面部检测并使用它来确定要缩放的图像大小和网格大小(例如最大的人脸缩放为100px)。特征检测器考虑多个等级(使用金字塔),但它将使用多少等级是有限制的(当然这是可调的)。

关键点检测器可能在返回的特性数量少于您想要的特性数量时工作得最好。例如,如果你要求400,得到300,那很好。如果你每次都能拿回400块,那么一些好的功能就会被忽略掉。

针状图像可以比干草堆图像拥有更少的关键点,但仍然可以获得良好的结果。增加更多并不一定能让你获得巨大的收益,例如当J=400和K=40时,我的命中率约为92%。当J=400和K=400时,命中率只能上升到96%。

我们可以利用汉明函数的极快速度来解决缩放、旋转、镜像等问题。可以使用多通道技术。在每次迭代中,转换子矩形,重新散列,并再次运行搜索函数。