在python中测量两个rgb图像之间的相似度
Posted
技术标签:
【中文标题】在python中测量两个rgb图像之间的相似度【英文标题】:measuring similarity between two rgb images in python 【发布时间】:2020-01-14 19:14:00 【问题描述】:我有两个相同大小的 rgb 图像,我想计算一个相似度指标。我想从欧几里得距离开始:
import scipy.spatial.distance as dist
import cv2
im1 = cv2.imread("im1.jpg")
im2 = cv2.imread("im2.jpg")
>> im1.shape
(820, 740, 3)
>> dist.euclidean(im1,im2)
ValueError: Input vector should be 1-D.
我知道dist.euclidean
需要一个一维数组,而im1
和im2
是 3-D,但是是否有可以处理 3-D 数组的函数,或者是否可以转换 @987654329 @ 和 im2
转换成一维数组来保存图像中的信息?
【问题讨论】:
numpy.reshape()
是你的朋友。不过,您的相似性度量对图像没有多大用处。
我认为您正在寻找SSIM 和image difference with opencv。看看detect and visualize differences between two images
@ypnos,谢谢!是的,你是对的,与欧几里德距离的图像比较给了我荒谬的结果。我想这不是为了图像比较......你建议我改用什么?
@nathancy 谢谢,我会仔细阅读这些链接
这个方法已经在scikit-image
中实现为compare_ssim
【参考方案1】:
灰度解决方案 (?)
(下面有关于您对“保留图像中的信息”功能的评论的讨论)在我看来,您可以使用灰度图像而不是 RGB 图像来解决问题。我知道我在这里做出假设,但这是一个想法。
我将尝试一个与您的代码相关的简单示例,然后给出一个使用 2D 离散傅里叶变换的图像相似性测量示例,该变换使用灰度转换。该 DFT 分析将有自己的部分
(如果您在进行中看到此内容,我深表歉意。我只是想确保我的工作得到保存。)
由于我的假设,我打算用一些RGB图像尝试你的方法,然后看是否可以通过转换为灰度来解决问题。如果问题是用灰度解决的,我们可以分析灰度解决方案带来的信息损失量,方法是结合所有三个通道,分别进行比较,找到图像相似度。
方法
确保我拥有所有库/包/您想调用的任何内容。
> python -m pip install opencv-python
> python -m pip install scipy
> python -m pip install numpy
请注意,在此试验中,我使用了一些在尝试(如下所述)使用 2D DFT 时创建的 PNG 图像。
确保我遇到同样的问题
>>> import scipy.spatial.distance as dist
>>> import cv2
>>>
>>> im1 = cv2.imread("rhino1_clean.png")
>>> im2 = cv2.imread("rhino1_streak.png")
>>>
>>> im1.shape
(178, 284, 3)
>>>
>>> dist.euclidean(im1, im2)
## Some traceback stuff ##
ValueError: Input vector should be 1-D.
现在,让我们尝试使用灰度。如果可行,我们可以简单地找到每个 RGB 通道的距离。我希望它有效,因为我想做信息丢失分析。
让我们转换为灰度:
>>> im1_gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY)
>>> im2_gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY)
>>> im1_gray.shape
(178, 284)
一个简单的dist.euclidean(im1_gray, im2,gray)
会导致同样的ValueError: Input vector should be 1-D.
异常,但是我知道灰度图像数组(像素行数组)的结构,所以我做了以下操作。
>>> dists = []
>>> for i in range(0, len(im1_gray)):
... dists.append(dist.euclidean(im1_gray[i], im2_gray[i]))
...
>>> sum_dists = sum(dists)
>>> ave_dist = sum_dists/len(dists)
>>> ave_dist
2185.9891304058297
顺便说一下,这是两张原图:
灰度工作(带按摩),让我们试试颜色
按照这个 SO 答案中的一些程序,让我们执行以下操作。
信息的保存
分析完here(archived),再来看看我们的信息丢失情况。 (请注意,这将是一个非常幼稚的分析,但我想试一试。
灰度与颜色信息
让我们看看颜色与灰度。稍后,我们可以看看我们是否保留了有关距离的信息。
使用灰度与所有三个通道的不同距离测量的比较 - 使用一组图像的距离总和比率进行比较。
我不知道如何对距离进行熵测量,但我的直觉告诉我,如果我使用灰度和颜色通道计算距离,如果我没有丢失,我应该得出相似的距离比率任何信息。
看到这个问题时,我的第一个想法是使用二维离散傅里叶变换,我确信它在 Python、NumPy 或 OpenCV 中可用。基本上,DFT 的第一个组成部分将与图像中的大形状相关。 (这里是我将发表相关研究论文的地方:link。我没有仔细看 - 欢迎任何人提出其他建议。)
所以,让我查找一个可从 Python 轻松获得的 2-D DFT,然后我将继续编写一些工作代码。
(如果您在进行中看到此内容,我深表歉意。我只是想确保我的工作得到保存。)
首先,您需要确保拥有 PIL
Pillow
和 NumPy
。您似乎有NumPy
,但这里有一些说明。 (请注意,我目前在 Windows 上)...
> python -m pip install opencv-python
> python -m pip install numpy
> python -m pip install pillow
现在,这里有 5 张图片 -
-
犀牛图像,
rhino1_clean.jpg
(source);
我在 MS Paint 中画了一些黑色条纹的同一张图像,rhino1_streak.jpg
;
另一个犀牛图像,rhino2_clean.jpg
(source);
第一张河马图片hippo1_clean.jpg
(source);
第二张河马图片,hippo2_clean.jpg
(source)。
所有图片均合理使用。
好的,现在,为了进一步说明,让我们进入 Python 交互式终端。
>python
>>> import PIL
>>> import numpy as np
首先,如果我们使用灰度 PNG 图像,生活会更轻松 - PNG 因为它是一个直位图(而不是压缩图像),灰度是因为我不必显示通道的所有细节。
>>> rh_img_1_cln = PIL.Image.open("rhino1_clean.jpg")
>>> rh_img_1_cln.save("rhino1_clean.png")
>>> rh_img_1_cln_gs = PIL.Image.open("rhino1_clean.png").convert('LA')
>>> rh_img_1_cln_gs.save("rhino1_clean_gs.png")
对其他四张图片执行类似的步骤。我使用了 PIL 变量名称,rh_img_1_stk
、rh_img_2_cln
、hp_img_1_cln
、hp_img_2_cln
。我最终得到了以下灰度图像的图像文件名,我将进一步使用它们:rhino1_streak_gs.png
、rhino2_clean_gs.png
、hippo1_clean_gs.png
、hippo2_clean_gs.png
。
现在,让我们获取 DFT 的系数。以下代码(参考this SO answer)将用于第一个干净的犀牛图像。
首先让我们“看看”图像数组。这将向我们展示左上角列的网格版本,值越高越白,越低越黑。
请注意,在开始输出此数组之前,我将其设置为 numpy
默认值,参见。 https://docs.scipy.org/doc/numpy/reference/generated/numpy.set_printoptions.html
>>> np.set_printoptions(edgeitems=3,infstr='inf',
... linewidth=75, nanstr='nan', precision=8,
... suppress=False, threshold=1000, formatter=None)
>>> rh1_cln_gs_array = np.array(rh_img_1_cln_gs)
>>> for i in 0,1,2,3,4:
... print(rh1_cln_gs_array[i][:13])
...
[93 89 78 87 68 74 58 51 73 96 90 75 86]
[85 93 64 64 76 49 19 52 65 76 86 81 76]
[107 87 71 62 54 31 32 49 51 55 81 87 69]
[112 93 94 72 57 45 58 48 39 49 76 86 76]
[ 87 103 90 65 88 61 44 57 34 55 70 80 92]
现在,让我们运行 DFT 并查看结果。在开始实际转换之前,我更改了 numpy
打印选项以使事情变得更好。
>>> np.set_printoptions(formatter='all':lambda x: '0:.2f'.format(x))
>>>
>>> rh1_cln_gs_fft = np.fft.fft2(rh_img_1_cln_gs)
>>> rh1_cln_gs_scaled_fft = 255.0 * rh1_cln_gs_fft / rh1_cln_gs_fft.max()
>>> rh1_cln_gs_real_fft = np.absolute(rh1_cln_gs_scaled_fft)
>>> for i in 0,1,2,3,4:
... print(rh1_cln_gs_real_fft[i][:13])
...
[255.00 1.46 7.55 4.23 4.53 0.67 2.14 2.30 1.68 0.77 1.14 0.28 0.19]
[38.85 5.33 3.07 1.20 0.71 5.85 2.44 3.04 1.18 1.68 1.69 0.88 1.30]
[29.63 3.95 1.89 1.41 3.65 2.97 1.46 2.92 1.91 3.03 0.88 0.23 0.86]
[21.28 2.17 2.27 3.43 2.49 2.21 1.90 2.33 0.65 2.15 0.72 0.62 1.13]
[18.36 2.91 1.98 1.19 1.20 0.54 0.68 0.71 1.25 1.48 1.04 1.58 1.01]
现在,使用rhino1_streak.jpg
执行相同过程的结果
[255.00 3.14 7.69 4.72 4.34 0.68 2.22 2.24 1.84 0.88 1.14 0.55 0.25]
[40.39 4.69 3.17 1.52 0.77 6.15 2.83 3.00 1.40 1.57 1.80 0.99 1.26]
[30.15 3.91 1.75 0.91 3.90 2.99 1.39 2.63 1.80 3.14 0.77 0.33 0.78]
[21.61 2.33 2.64 2.86 2.64 2.34 2.25 1.87 0.91 2.21 0.59 0.75 1.17]
[18.65 3.34 1.72 1.76 1.44 0.91 1.00 0.56 1.52 1.60 1.05 1.74 0.66]
我将打印 \Delta 值而不是更全面的距离。如果需要距离,可以将此处显示的值的平方相加。
>>> for i in 0,1,2,3,4:
... print(rh1_cln_gs_real_fft[i][:13] - rh1_stk_gs_real_fft[i][:13])
...
[0.00 -1.68 -0.15 -0.49 0.19 -0.01 -0.08 0.06 -0.16 -0.11 -0.01 -0.27
-0.06]
[-1.54 0.64 -0.11 -0.32 -0.06 -0.30 -0.39 0.05 -0.22 0.11 -0.11 -0.11 0.04]
[-0.53 0.04 0.14 0.50 -0.24 -0.02 0.07 0.30 0.12 -0.11 0.11 -0.10 0.08]
[-0.33 -0.16 -0.37 0.57 -0.15 -0.14 -0.36 0.46 -0.26 -0.07 0.13 -0.14
-0.04]
[-0.29 -0.43 0.26 -0.58 -0.24 -0.37 -0.32 0.15 -0.27 -0.12 -0.01 -0.17
0.35]
我将只放置三个长度为 5 的系数数组,以展示它如何用于显示图像相似性。老实说,这对我来说是一个实验,所以我们会看看结果如何。
您可以将这些系数与距离或其他指标进行比较。
关于信息保存的更多信息
让我们用上面提出的方法对信息丢失进行信息理论分析。 分析完here(archived),再来看看我们的信息丢失情况。
祝你好运!
【讨论】:
【参考方案2】:你可以试试
import scipy.spatial.distance as dist
import cv2
import numpy as np
im1 = cv2.imread("im1.jpg")
im2 = cv2.imread("im2.jpg")
dist.euclidean(im1.flatten(), im2.flatten())
【讨论】:
【参考方案3】:您可以对两个图像使用 reshape 功能将它们从 3D 转换为 1D。
import scipy.spatial.distance as dist
import cv2
im1 = cv2.imread("im1.jpg")
im2 = cv2.imread("im2.jpg")
im1.reshape(1820400)
im2.reshape(1820400)
dist.euclidean(im1,im2)
【讨论】:
以上是关于在python中测量两个rgb图像之间的相似度的主要内容,如果未能解决你的问题,请参考以下文章