Bor*_*ris 133 comparison opencv image similarity
OpenCV是否支持两个图像的比较,返回一些值(可能是百分比),表示这些图像有多相似?例如,如果相同的图像被传递两次将返回100%,如果图像完全不同则将返回0%.
我已经在StackOverflow上阅读了很多类似的主题.我也做了一些谷歌搜索.可悲的是,我无法想出一个令人满意的答案.
Sam*_*Sam 196
这是一个非常庞大的话题,从3行代码到整个研究杂志的答案.
我将概述最常见的此类技术及其结果.
最简单,最快捷的方法之一.建议几十年前作为寻找图像相似性的手段.这个想法是,森林将拥有大量的绿色,人脸会有很多粉红色,或者其他什么.因此,如果你将两张图片与森林进行比较,你会在直方图之间得到一些相似之处,因为两者都有很多绿色.
缺点:太简单了.香蕉和海滩看起来都一样,都是黄色的.
OpenCV方法:compareHist()
这里有一个很好的例子:matchTemplate找到了很好的匹配.它将搜索图像与正在搜索的图像进行卷积.它通常用于在较大的图像中找到较小的图像部分.
缺点:只有相同的图像,相同的大小和方向才能返回良好的结果.
OpenCV方法:matchTemplate()
被认为是进行图像搜索的最有效方法之一.从图像中提取许多特征,以确保即使旋转/缩放/倾斜也能再次识别相同的特征.以这种方式提取的特征可以与其他图像特征集匹配.另一个在第一个中具有高比例特征的图像很可能描绘了相同的对象/场景.它可用于查找照片之间拍摄角度的相对差异,或重叠量.
这里有许多OpenCV教程/示例,这里有一个很好的视频.整个OpenCV模块(features2d)专门用于它.
缺点:可能很慢.它并不完美.
nat*_*ncy 101
由于没有人发布完整的具体示例,因此这里有两种定量方法来确定两个图像之间的相似度。一种比较相同尺寸图像的方法;另一个用于不同图像的尺度不变和变换。0两种方法都会返回到 之间的相似度得分100,其中0表示完全不同的图像并100表示相同/重复的图像。对于介于两者之间的所有其他值:分数越低,相似度越低;分数越高,越相似。
方法#1:结构相似性指数(SSIM)
为了比较差异并确定两个图像之间的确切差异,我们可以利用图像质量评估:从错误可见性到结构相似性中介绍的结构相似性指数(SSIM) 。SSIM 是一种图像质量评估方法,它根据参考图像和失真图像之间的局部信息的统计特性来估计结构相似性的退化。SSIM 值的范围在 [-1, 1] 之间延伸,通常使用滑动窗口进行计算,其中整个图像的 SSIM 值计算为所有单独窗口结果的平均值。该方法已在用于图像处理的scikit-image库中实现,并且可以随.pip install scikit-image
该skimage.metrics.structural_similarity()函数返回比较图像score和差异图像diff。表示score两幅图像之间的平均 SSIM 分数,值越高表示相似度越高。该diff图像包含实际图像差异,较暗区域具有更大的视差。较大的差异区域以黑色突出显示,而较小的差异区域以灰色突出显示。这是一个例子:
输入图像
差异图像->突出显示掩模差异
比较两幅图像后的 SSIM 分数表明它们非常相似。
相似度得分:89.462%
为了可视化两个图像之间的确切差异,我们可以迭代每个轮廓,使用最小阈值区域进行过滤以消除微小的噪声,并使用边界框突出显示差异。
局限性:虽然这种方法效果很好,但也有一些重要的局限性。两个输入图像必须具有相同的大小/尺寸,并且还存在一些问题,包括缩放、平移、旋转和扭曲。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()
Run Code Online (Sandbox Code Playgroud)
方法#2:密集向量表示
通常,两个图像不会完全相同。它们可能会因背景、尺寸、功能添加/减少或变换(缩放、旋转、倾斜)而略有不同。换句话说,我们不能使用直接的像素到像素的方法,因为随着变化,问题从识别像素相似性转移到识别对象相似性。我们必须转向深度学习特征模型,而不是比较单个像素值。
为了确定相同和接近相似的图像,我们可以使用该sentence-transformers库提供一种简单的方法来计算图像的密集向量表示,以及OpenAI 对比语言图像预训练 (CLIP) 模型,该模型是一个已经在各种(图像、文本)对。这个想法是将所有图像编码到向量空间中,然后找到与图像非常相似的区域相对应的高密度区域。
当比较两个图像时,它们会得到 到 之间的0分数1.00。我们可以使用阈值参数来识别两个图像相似或不同。较低的阈值将导致聚类中相似图像较少。相反,较高的阈值将导致聚类具有更多相似的图像。重复图像的分数表示1.00两个图像完全相同。为了找到接近相似的图像,我们可以将阈值设置为任意值,例如0.9。例如,如果两个图像之间确定的分数大于,0.9那么我们可以断定它们是接近相似的图像。
一个例子:
该数据集有五个图像,请注意花朵 #1 的重复项,而其他图像则不同。
识别重复图像
Score: 100.000%
.\flower_1 copy.jpg
.\flower_1.jpg
Run Code Online (Sandbox Code Playgroud)
#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
Run Code Online (Sandbox Code Playgroud)
我们在不同图像之间得到了更有趣的结果。得分越高,相似度越高;分数越低,相似度越低。使用 90% 或 90% 的阈值0.9,我们可以过滤掉近似相似的图像。
仅两个图像之间的比较
Score: 97.141%
.\cat_1.jpg
.\cat_2.jpg
Run Code Online (Sandbox Code Playgroud)
Score: 95.693%
.\flower_1.jpg
.\flower_2.jpg
Run Code Online (Sandbox Code Playgroud)
Score: 88.914%
.\ladybug_1.jpg
.\ladybug_2.jpg
Run Code Online (Sandbox Code Playgroud)
Score: 94.503%
.\cherry_1.jpg
.\cherry_2.jpg
Run Code Online (Sandbox Code Playgroud)
代码:
Score: 100.000%
.\flower_1 copy.jpg
.\flower_1.jpg
Run Code Online (Sandbox Code Playgroud)
Kir*_*ran 31
如果匹配相同的图像(相同的大小/方向)
// 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
}
Run Code Online (Sandbox Code Playgroud)
Pri*_*han 11
山姆的解决方案应该足够了。我使用了直方图差异和模板匹配的组合,因为没有一种方法在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
Run Code Online (Sandbox Code Playgroud)
有点偏离主题但很有用的是 pythonicnumpy方法。它强大且快速,但只比较像素而不是图片包含的对象或数据(并且它需要相同大小和形状的图像):
无需 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))
Run Code Online (Sandbox Code Playgroud)
定义两个标准化图片(或矩阵)后,您可以对要比较的图片的乘积求和:
如果比较相似的图片,总和将返回 1:
In[1]: np.sum(picture1_norm**2)
Out[1]: 1.0
Run Code Online (Sandbox Code Playgroud)
如果它们不相似,您将得到一个介于 0 和 1 之间的值(乘以 100 后得到的百分比):
In[2]: np.sum(picture2_norm*picture1_norm)
Out[2]: 0.75389941124629822
Run Code Online (Sandbox Code Playgroud)
请注意,如果您有彩色图片,则必须在所有 3 个维度上执行此操作,或者仅比较灰度版本。我经常需要将大量图片与任意内容进行比较,这是一种非常快速的方法。