查找近似重复和伪造图片。

18

我正在使用感知哈希技术来查找近似重复和完全重复的图像。该代码可完美地用于查找完全重复的图像。但是,查找近似重复和略微修改过的图像似乎很困难。因为它们的哈希之间的差异得分通常与完全不同的随机图像的哈希差异相似。

为了解决这个问题,我尝试将近似重复的图像像素化到50x50像素并使它们变成黑白,但我仍然没有得到我需要的(小差异得分)。

这是一对近似重复图像的样本:

图像1(a1.jpg):

enter image description here

图像2(b1.jpg):

enter image description here

这些图像的哈希得分之间的差异为:24

当像素化(50x50像素)时,它们看起来像这样:

enter image description here

rs_a1.jpg

enter image description here

rs_b1.jpg

像素化图像的哈希差异得分甚至更大!:26

如@ann zen所要求的,以下是另外两个近似重复的图像对的示例:

图像对1

enter image description here

图像对2

enter image description here

我用于缩小图像大小的代码如下:

from PIL import Image    
with Image.open(image_path) as image:
            reduced_image = image.resize((50, 50)).convert('RGB').convert("1")

比较两个图像哈希的代码:

from PIL import Image
import imagehash        
with Image.open(image1_path) as img1:
            hashing1 =  imagehash.phash(img1)
with Image.open(image2_path) as img2:
            hashing2 =  imagehash.phash(img2)           
print('difference :  ', hashing1-hashing2)

通常这样的任务是使用深度学习模型完成的。您是否有使用这种“统计”方法的任何理由或限制? - Abhinav Mathur
@AbhinavMathur 我需要在一组1000万张图片中找到被编辑/伪造/调整过的图片。使用哈希算法(如phash)很容易找到完全重复的图片。但我找不到一种方法来找到近似重复/编辑过的图片。 - Youcef
或许可以计算两个几乎相同图像之间的交叉相关性,这应该比逐像素哈希更具有鲁棒性。 - stateMachine
@Youcef,你从哪里获取到这些相似的图像对?有什么仓库可以提供吗? - nathancy
@nathancy 不是的。我只是在谷歌上搜索了一些样本。 - Youcef
基于像素的方法过于简单化,因为你所讨论的是对象相似性,而不是像素相似性。你需要深度学习特征值,而不是像素值。我收集了一组研究论文来解释图像相似性以及嵌入是什么:https://vitali-fedulov.github.io/similar.pictures/research.html - Similar pictures
3个回答

30
这里提供了一种使用sentence-transformers库的定量方法,用于确定重复和相似度较高的图像。我们可以使用OpenAI对比语言-图像预训练(CLIP)模型,该神经网络已经针对各种(图像、文本)对进行了训练。为了找到图像的副本和近似副本,我们将所有图像编码为向量空间,然后找到对应于图像相似的高密度区域。

当比较两个图像时,它们被赋予介于01.00之间的分数。我们可以使用阈值参数来识别两个图像是否相似或不同。通过降低阈值,您将获得包含少量相似图像的较大聚类。重复的图像将具有1.00的分数,表示这两个图像完全相同。为了找到近似副本图像,我们可以将阈值设置为任意值,例如0.9。例如,如果两个图像之间的得分大于0.9,则我们可以得出它们是相似的图像。


示例:

enter image description here

该数据集有5个图像,注意cat #1有副本,而其他图像则不同。

找到重复的图像

Score: 100.000%
.\cat1 copy.jpg
.\cat1.jpg

猫1和它的拷贝是相同的。

寻找近似重复的图片

Score: 91.116%
.\cat1 copy.jpg
.\cat2.jpg

Score: 91.116%
.\cat1.jpg
.\cat2.jpg

Score: 91.097%
.\bear1.jpg
.\bear2.jpg

Score: 59.086%
.\bear2.jpg
.\cat2.jpg

Score: 56.025%
.\bear1.jpg
.\cat2.jpg

Score: 53.659%
.\bear1.jpg
.\cat1 copy.jpg

Score: 53.659%
.\bear1.jpg
.\cat1.jpg

Score: 53.225%
.\bear2.jpg
.\cat1.jpg

我们可以在不同图片之间获得更有趣的分数比较结果。得分越高,相似度越高;得分越低,相似度越低。使用阈值 0.9 或 90%,我们可以过滤掉近似重复的图片。 仅两张图片之间的比较
Score: 91.097%
.\bear1.jpg
.\bear2.jpg
Score: 91.116%
.\cat1.jpg
.\cat2.jpg
Score: 93.715%
.\tower1.jpg
.\tower2.jpg

代码

from sentence_transformers import SentenceTransformer, util
from PIL import Image
import glob
import os

# Load the OpenAI CLIP Model
print('Loading CLIP Model...')
model = SentenceTransformer('clip-ViT-B-32')

# Next we compute the embeddings
# To encode an image, you can use the following code:
# from PIL import Image
# encoded_image = model.encode(Image.open(filepath))
image_names = list(glob.glob('./*.jpg'))
print("Images:", len(image_names))
encoded_image = model.encode([Image.open(filepath) for filepath in image_names], batch_size=128, convert_to_tensor=True, show_progress_bar=True)

# Now we run the clustering algorithm. This function compares images aganist 
# all other images and returns a list with the pairs that have the highest 
# cosine similarity score
processed_images = util.paraphrase_mining_embeddings(encoded_image)
NUM_SIMILAR_IMAGES = 10 

# =================
# DUPLICATES
# =================
print('Finding duplicate images...')
# Filter list for duplicates. Results are triplets (score, image_id1, image_id2) and is scorted in decreasing order
# A duplicate image will have a score of 1.00
duplicates = [image for image in processed_images if image[0] >= 1]

# Output the top X duplicate images
for score, image_id1, image_id2 in duplicates[0:NUM_SIMILAR_IMAGES]:
    print("\nScore: {:.3f}%".format(score * 100))
    print(image_names[image_id1])
    print(image_names[image_id2])

# =================
# NEAR DUPLICATES
# =================
print('Finding near duplicate images...')
# Use a threshold parameter to identify two images as similar. By setting the threshold lower, 
# you will get larger clusters which have less similar images in it. Threshold 0 - 1.00
# A threshold of 1.00 means the two images are exactly the same. Since we are finding near 
# duplicate images, we can set it at 0.99 or any number 0 < X < 1.00.
threshold = 0.99
near_duplicates = [image for image in processed_images if image[0] < threshold]

for score, image_id1, image_id2 in near_duplicates[0:NUM_SIMILAR_IMAGES]:
    print("\nScore: {:.3f}%".format(score * 100))
    print(image_names[image_id1])
    print(image_names[image_id2])

很棒的回答。以下问题与此相关,但不幸的是没有详细的答案,甚至已经被踩了:https://stackoverflow.com/questions/64520940/image-search-using-image-similarity-measures - Similar pictures

14

在查找图像之间的差异/相似性之前,不要使用像素化来处理图像,只需使用cv2.GaussianBlur()方法对它们进行一些模糊处理,然后使用cv2.matchTemplate()方法来查找它们之间的相似性:

import cv2
import numpy as np

def process(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return cv2.GaussianBlur(img_gray, (43, 43), 21)

def confidence(img1, img2):
    res = cv2.matchTemplate(process(img1), process(img2), cv2.TM_CCOEFF_NORMED)
    return res.max()

img1s = list(map(cv2.imread, ["img1_1.jpg", "img1_2.jpg", "img1_3.jpg"]))
img2s = list(map(cv2.imread, ["img2_1.jpg", "img2_2.jpg", "img2_3.jpg"]))

for img1, img2 in zip(img1s, img2s):
    conf = confidence(img1, img2)
    print(f"Confidence: {round(conf * 100, 2)}%")

输出:

Confidence: 83.6%
Confidence: 84.62%
Confidence: 87.24%

以下是上述程序中使用的图像:

img1_1.jpgimg2_1.jpg

enter image description here enter image description here

img1_2.jpgimg2_2.jpg

enter image description here enter image description here

img1_3.jpgimg2_3.jpg

enter image description here enter image description here

为证明模糊操作不会产生过多的假阳性,我运行了这个程序:

import cv2
import numpy as np

def process(img):
    h, w, _ = img.shape
    img = cv2.resize(img, (350, h * w // 350))
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return cv2.GaussianBlur(img_gray, (43, 43), 21)

def confidence(img1, img2):
    res = cv2.matchTemplate(process(img1), process(img2), cv2.TM_CCOEFF_NORMED)
    return res.max()

img1s = list(map(cv2.imread, ["img1_1.jpg", "img1_2.jpg", "img1_3.jpg"]))
img2s = list(map(cv2.imread, ["img2_1.jpg", "img2_2.jpg", "img2_3.jpg"]))

for i, img1 in enumerate(img1s, 1):
    for j, img2 in enumerate(img2s, 1):
        conf = confidence(img1, img2)
        print(f"img1_{i} img2_{j} Confidence: {round(conf * 100, 2)}%")

输出:

img1_1 img2_1 Confidence: 84.2% # Corresponding images
img1_1 img2_2 Confidence: -10.86%
img1_1 img2_3 Confidence: 16.11%
img1_2 img2_1 Confidence: -2.5%
img1_2 img2_2 Confidence: 84.61% # Corresponding images
img1_2 img2_3 Confidence: 43.91%
img1_3 img2_1 Confidence: 14.49%
img1_3 img2_2 Confidence: 59.15%
img1_3 img2_3 Confidence: 87.25% # Corresponding images

注意只有当将图像与其对应的图像匹配时,程序才会输出高置信度水平(84%以上)。

为了比较,这里是未经过模糊处理的结果:

import cv2
import numpy as np

def process(img):
    h, w, _ = img.shape
    img = cv2.resize(img, (350, h * w // 350))
    return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

def confidence(img1, img2):
    res = cv2.matchTemplate(process(img1), process(img2), cv2.TM_CCOEFF_NORMED)
    return res.max()

img1s = list(map(cv2.imread, ["img1_1.jpg", "img1_2.jpg", "img1_3.jpg"]))
img2s = list(map(cv2.imread, ["img2_1.jpg", "img2_2.jpg", "img2_3.jpg"]))

for i, img1 in enumerate(img1s, 1):
    for j, img2 in enumerate(img2s, 1):
        conf = confidence(img1, img2)
        print(f"img1_{i} img2_{j} Confidence: {round(conf * 100, 2)}%")

输出:

img1_1 img2_1 Confidence: 66.73%
img1_1 img2_2 Confidence: -6.97%
img1_1 img2_3 Confidence: 11.01%
img1_2 img2_1 Confidence: 0.31%
img1_2 img2_2 Confidence: 65.33%
img1_2 img2_3 Confidence: 31.8%
img1_3 img2_1 Confidence: 9.57%
img1_3 img2_2 Confidence: 39.74%
img1_3 img2_3 Confidence: 61.16%

3
在我看来,这可能是最简单的答案,也很可能是最好的起点。 - ldog

3
我正在使用(https://github.com/idealo/imagededup)来查找“近似图像相似性重复项”。该库包含以下算法:
  1. 卷积神经网络(CNN)
  2. 感知哈希(PHash)
  3. 差异哈希(DHash)
  4. 小波哈希(WHash)
  5. 平均哈希(AHash)

安装方法:

pip install imagededup

一个示例代码
from imagededup.methods import PHash
phasher = PHash()

# Generate encodings for all images in an image directory
encodings = phasher.encode_images(image_dir='path/to/image/directory')

# Find duplicates using the generated encodings
duplicates = phasher.find_duplicates(encoding_map=encodings)

# plot duplicates obtained for a given file using the duplicates dictionary
from imagededup.utils import plot_duplicates
plot_duplicates(image_dir='path/to/image/directory',
            duplicate_map=duplicates,
            filename='ukbench00120.jpg')

您可以通过更改您的中的max_distance_threshold来更改汉明距离参数。

希望对您有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接