使用OpenCV检查图像的相似性

csbfibhn  于 2023-01-05  发布在  其他
关注(0)|答案(6)|浏览(130)

OpenCV是否支持比较两个图像,返回一些值(可能是百分比)来指示这些图像的相似程度?例如,如果相同的图像被传递两次,则返回100%;如果图像完全不同,则返回0%。
我已经在StackOverflow上读到了很多类似的主题,我也搜索了一下,很遗憾我没有找到一个满意的答案。

wecizke3

wecizke31#

这是一个庞大的主题,答案从3行代码到整个研究杂志。
我将概述最常见的此类技术及其结果。

比较直方图

最简单、最快的方法之一。几十年前提出的一种寻找图片相似性的方法。其思想是森林会有很多绿色,而人脸会有很多粉红色,或者其他什么。所以,如果你比较两张森林图片,你会得到直方图之间的一些相似性,因为你在两张图片中都有很多绿色。
缺点:它太简单了。香蕉和海滩看起来是一样的,因为都是黄色的。
OpenCV方法:比较历史记录()

模板匹配

matchTemplate finding good match是一个很好的例子。它将搜索图像与要搜索的图像进行卷积。它通常用于在较大的图像中查找较小的图像部分。
缺点:它只返回相同的图像,相同的大小和方向的好结果。
OpenCV方法:匹配模板()

特征匹配

被认为是进行图像搜索的最有效方法之一。从图像中提取多个特征,以保证即使在旋转、缩放或倾斜时也能再次识别相同的特征。用这种方法提取的特征可以与其他图像特征集进行匹配。具有高比例特征与第一个特征匹配的另一个图像被认为描述了相同的场景。
找到两组点之间的单应性还可以让您找到原始图片之间拍摄Angular 的相对差异或重叠量。
有很多OpenCV教程/示例,还有一个不错的视频here,整个OpenCV模块(features 2d)都是专门针对它的。
缺点:它可能很慢。它不完美。
在OpenCV问答网站上,我谈到了特征描述符和纹理描述符之间的区别,特征描述符在比较整个图像时非常重要,纹理描述符用于识别图像中的人脸或汽车等对象。

bz4sfanl

bz4sfanl2#

由于没有人贴出完整的具体例子,这里有两种定量的方法来确定两幅图像之间的相似度:一种方法是比较维度相同的图像;另一种用于缩放不变和变换无关的图像。这两种方法都返回0100之间的相似性得分,其中0表示完全不同的图像,100表示相同/重复的图像。对于介于两者之间的所有其他值:分数越低,相似性越低;分数越高,越相似。

    • 方法1:结构相似指数(SSIM)**

为了比较差异并确定两个图像之间的确切差异,我们可以利用 * 结构相似性指数 * SSIM是一种图像质量评价方法,它基于参考图像和失真图像之间局部信息的统计特性来估计结构相似性的退化,SSIM值的范围在[-1,1],通常使用滑动窗口计算,其中整个图像的SSIM值计算为所有单个窗口结果的平均值。此方法已在scikit-image图像处理库中实现,可随pip install scikit-image安装。
skimage.metrics.structural_similarity()函数返回比较结果score和差值图像,diffscore表示两张图像之间的平均SSIM分数,值越高表示相似性越高。diff图像包含实际图像差异,较暗区域具有更多差异。较大的差异区域以黑色突出显示,而较小的差异以灰色突出显示。此处'这是一个例子:
输入图像

差异图像->突出显示的蒙片差异

比较两幅图像后的SSIM评分显示它们非常相似。
相似度评分:八十九点四六二
为了可视化两幅图像之间的确切差异,我们可以迭代每个轮廓,使用最小阈值区域过滤以去除微小的噪声,并使用边界框突出显示差异。

    • 局限性:**尽管此方法效果很好,但仍存在一些重要局限性。两个输入图像必须具有相同的大小/尺寸,并且还存在一些问题,包括缩放、平移、旋转和扭曲。SSIM在处理模糊或噪声图像时也表现不佳。这些问题在方法#2中得到解决。

代码:

from skimage.metrics import structural_similarity
import cv2
import numpy as np

first = cv2.imread('clownfish_1.jpeg')
second = cv2.imread('clownfish_2.jpeg')

# Convert images to grayscale
first_gray = cv2.cvtColor(first, cv2.COLOR_BGR2GRAY)
second_gray = cv2.cvtColor(second, cv2.COLOR_BGR2GRAY)

# Compute SSIM between two images
score, diff = structural_similarity(first_gray, second_gray, full=True)
print("Similarity Score: {:.3f}%".format(score * 100))

# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type so we must convert the array 
# to 8-bit unsigned integers in the range [0,255] before we can use it with OpenCV
diff = (diff * 255).astype("uint8")

# Threshold the difference image, followed by finding contours to
# obtain the regions that differ between the two images
thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

# Highlight differences
mask = np.zeros(first.shape, dtype='uint8')
filled = second.copy()

for c in contours:
    area = cv2.contourArea(c)
    if area > 100:
        x,y,w,h = cv2.boundingRect(c)
        cv2.rectangle(first, (x, y), (x + w, y + h), (36,255,12), 2)
        cv2.rectangle(second, (x, y), (x + w, y + h), (36,255,12), 2)
        cv2.drawContours(mask, [c], 0, (0,255,0), -1)
        cv2.drawContours(filled, [c], 0, (0,255,0), -1)

cv2.imshow('first', first)
cv2.imshow('second', second)
cv2.imshow('diff', diff)
cv2.imshow('mask', mask)
cv2.imshow('filled', filled)
cv2.waitKey()
    • 方法#2:密集矢量表示**

通常情况下,两个图像不会完全相同。它们可能会因背景、尺寸、要素添加/减少或变换而略有不同换句话说,我们不能使用直接的像素到像素的方法,因为随着变化,问题从识别像素相似性转移到物体相似性。我们必须转换到深度-学习特征模型而不是比较各个像素值。
为了确定相同和近似的图像,我们可以使用sentence-transformers库,它提供了一种简单的方法来计算图像的密集矢量表示,以及OpenAI Contrastive Language-Image Pre-Training (CLIP) Model,它是一个已经在各种(图像,文本)对上训练过的神经网络。其思想是将所有图像编码到矢量空间中,然后找到高密度区域,对应于图像非常相似的区域。
比较两幅图像时,会给它们一个01.00之间的分数。我们可以使用一个阈值参数来识别两幅图像是相似还是不同。较低的阈值会导致其中具有较少相似图像的聚类。相反,较高的阈值将产生具有更多相似图像的聚类。复制图像将具有1.00的分数,这意味着两个图像完全相同。为了找到接近相似的图像,我们可以将阈值设置为任意值,例如0.9,例如,如果两个图像之间确定的得分大于0.9,则我们可以断定它们是接近相似的图像。
举个例子:

这个数据集有五张图像,注意花#1是如何重复的,而其他的是不同的。

    • 识别重复图像**
Score: 100.000%
.\flower_1 copy.jpg
.\flower_1.jpg

花#1及其副本相同

    • 识别近似图像**
Score: 97.141%
.\cat_1.jpg
.\cat_2.jpg

Score: 95.693%
.\flower_1.jpg
.\flower_2.jpg

Score: 57.658%
.\cat_1.jpg
.\flower_1 copy.jpg

Score: 57.658%
.\cat_1.jpg
.\flower_1.jpg

Score: 57.378%
.\cat_1.jpg
.\flower_2.jpg

Score: 56.768%
.\cat_2.jpg
.\flower_1 copy.jpg

Score: 56.768%
.\cat_2.jpg
.\flower_1.jpg

Score: 56.284%
.\cat_2.jpg
.\flower_2.jpg

我们在不同的图像之间得到了更有趣的结果,得分越高,相似度越高;分数越低,相似性越低,使用阈值0.9或90%,我们可以过滤掉接近相似的图像。

    • 仅在两个图像之间进行比较**

第一个计算机7d1x第一个计算机8d1x第一个计算机3b1x第一个计算机9d1x第一个计算机10d1x第一个计算机4b1x第一个计算机11d1x第一个计算机12d1x第一个计算机5b1x第一个计算机13d1x第一个计算机14d1x第一个计算机6b1x
代码:

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
# It may be 0.9999 due to lossy image compression (.jpg)
duplicates = [image for image in processed_images if image[0] >= 0.999]

# 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])
cnh2zyt3

cnh2zyt33#

如果用于匹配相同图像(相同尺寸/方向)

// Compare two images by getting the L2 error (square-root of sum of squared error).
double getSimilarity( const Mat A, const Mat B ) {
if ( A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols ) {
    // Calculate the L2 relative error between images.
    double errorL2 = norm( A, B, CV_L2 );
    // Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
    double similarity = errorL2 / (double)( A.rows * A.cols );
    return similarity;
}
else {
    //Images have a different size
    return 100000000.0;  // Return a bad value
}

Source

hzbexzde

hzbexzde4#

Sam的解决方案应该足够了。我使用了直方图差异和模板匹配的组合,因为没有一种方法是100%适合我的。虽然我对直方图方法不太重视。下面是我如何在简单的python脚本中实现的。

import cv2

class CompareImage(object):

    def __init__(self, image_1_path, image_2_path):
        self.minimum_commutative_image_diff = 1
        self.image_1_path = image_1_path
        self.image_2_path = image_2_path

    def compare_image(self):
        image_1 = cv2.imread(self.image_1_path, 0)
        image_2 = cv2.imread(self.image_2_path, 0)
        commutative_image_diff = self.get_image_difference(image_1, image_2)

        if commutative_image_diff < self.minimum_commutative_image_diff:
            print "Matched"
            return commutative_image_diff
        return 10000 //random failure value

    @staticmethod
    def get_image_difference(image_1, image_2):
        first_image_hist = cv2.calcHist([image_1], [0], None, [256], [0, 256])
        second_image_hist = cv2.calcHist([image_2], [0], None, [256], [0, 256])

        img_hist_diff = cv2.compareHist(first_image_hist, second_image_hist, cv2.HISTCMP_BHATTACHARYYA)
        img_template_probability_match = cv2.matchTemplate(first_image_hist, second_image_hist, cv2.TM_CCOEFF_NORMED)[0][0]
        img_template_diff = 1 - img_template_probability_match

        # taking only 10% of histogram diff, since it's less accurate than template method
        commutative_image_diff = (img_hist_diff / 10) + img_template_diff
        return commutative_image_diff

    if __name__ == '__main__':
        compare_image = CompareImage('image1/path', 'image2/path')
        image_difference = compare_image.compare_image()
        print image_difference
s4n0splo

s4n0splo5#

有点离题但很有用的是Python numpy方法,它健壮而快速,但只是比较像素,而不是图片中包含的对象或数据(它需要大小和形状相同的图像):
一个非常简单快速的方法是在没有openCV和任何计算机视觉库的情况下,通过以下公式对图片阵列进行归一化:

import numpy as np
picture1 = np.random.rand(100,100)
picture2 = np.random.rand(100,100)
picture1_norm = picture1/np.sqrt(np.sum(picture1**2))
picture2_norm = picture2/np.sqrt(np.sum(picture2**2))

在定义了两个赋范图(或矩阵)之后,你可以对你想要比较的图的乘积求和:
1)如果你比较相似的图片,总和将返回1:

In[1]: np.sum(picture1_norm**2)
Out[1]: 1.0

2)如果它们不相似,则会得到一个介于0和1之间的值(乘以100时为一个百分比):

In[2]: np.sum(picture2_norm*picture1_norm)
Out[2]: 0.75389941124629822

请注意,如果你有彩色的图片,你必须在所有的三维或只是比较灰度版本。我经常要比较大量的图片与任意的内容,这是一个非常快的方式这样做。

7fyelxc5

7fyelxc56#

可以对预训练的ImageRes数据使用诸如VGG 16的架构来使用自动编码器用于这样的任务;然后计算查询和其他图像之间的距离,以找到最接近的匹配。

相关问题